问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何让 Web API 统一回传格式以及例外处理

发布网友 发布时间:2022-05-12 05:52

我来回答

2个回答

热心网友 时间:2023-11-23 22:07

1.所以首先我们需要先自定义一个 Model 来当作我们的包装的容器,其类别的定义如下:
public class ApiResultModel
{
public HttpStatusCode Status { get; set; }
public object Data { get; set; }
public string ErrorMessage { get; set; }
}

2.相信写过 ASP.NET MVC 的朋友一定会知道,一般我们会将一些在 Action 中固定的逻辑,利用 Filter 来套用到每一个 Action 上面,例如:Authorize。如果你对 Filter 不是很熟悉可以参考一下网络上前辈所写的文章:[VS2010] ASP.NET MVC with Action Filters。 所以这边我们也需要使用同样的技巧来重新打包我们回传的数据格式,我们先新增一个 ApiResultAttribute.cs 的档案,且继承 System.Web.Http.Filters.ActionFilterAttribute,并且复写 OnActionExecuted 的方法,如下:
public class ApiResultAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
}
}

3.而 OnActionExecuted 会在 Action 执行之后呼叫,也表示我们将资料送进这个方法里面,接着处理我们主要打包的程序逻辑,程序代码如下:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);

ApiResultModel result = new ApiResultModel();

// 取得由 API 返回的状态代码
result.Status = actionExecutedContext.ActionContext.Response.StatusCode;
// 取得由 API 返回的资料
result.Data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
// 重新封装回传格式
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
}

4.而为了要让所有的 Web API 都能套用我们自定义的 Filter,所以我们需要到 App_Start → WebApiConfig.cs → Register 来注册全局的 Web API Filter。(注意:若要注册 Web API 的 Filter 需在 WebApiConfig.cs 中注册,而非 FilterConfig.cs 中)
config.Filters.Add(new ApiResultAttribute());

5.重新建置之后我们再重新执行一次原先的 Web API 程序,就会看到回传的格式已经变成我们自定义的格式了:
"Status": 200,
"Data": [
{
"Account": "taxi",
"Mark": "",
"Name": "王大明",
"Telephone": "0986540123",
"AccountStatus": true
},
{
"Account": "taxi2",
"Mark": "",
"Name": "方大同",
"Telephone": "0922335111",
"AccountStatus": true
},
{
"Account": "q121234567",
"Mark": null,
"Name": "0000",
"Telephone": "0972334334",
"AccountStatus": true
}
],
"ErrorMessage": null

例外处理
前面我们已经将讯息打包成我们要的格式了,不过我们还没确切地去处理有关例外的程序代码,一般当程序发生错误产生例外时,我们当然也希望接收端能知道程序发生错误,进而显示该显示的讯息,而不是活生生地看着程序 Crash 或是停顿,这样将带给你的客户不好的体验,而在 ASP.NET MVC 中也有提供专门处理例外的 ExceptionFilterAttribute,所以接着来看看该如何打包我们的例外讯息吧。
1.新增一个 ApiErrorHandleAttribute.cs 并且继承 System.Web.Http.Filters.ExceptionFilterAttribute,接着复写 OnException 当例外发生时执行的方法,程序代码如下:
public class ApiErrorHandleAttribute : System.Web.Http.Filters.ExceptionFilterAttribute
{
public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
}
}

2.透过 OnException 的方法能让我们捕捉当例外发生时要处理的事情,一般系统我们也会在这边将发生错误的时间、登入的用户以及错误的状况记录下来 (例如:系统事件、存入数据库、写入 .txt 档 … 等),不过这边不是我们讨论的重点,我们先来看看该如何打包我们的例外讯息,程序代码如下:
public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);

// 取得发生例外时的错误讯息
var errorMessage = actionExecutedContext.Exception.Message;

var result = new ApiResultEntity()
{
Status = HttpStatusCode.BadRequest,
ErrorMessage = errorMessage
};

// 重新打包回传的讯息
actionExecutedContext.Response = actionExecutedContext.Request
.CreateResponse(result.Status, result);
}

3.而因为程序丢出例外后会先回到 OnActionExcuted 在进到例外的处理,所以我们稍微修改一下原本的 OnActionExcuted 这个方法,让发生例外时就直接跳过不再这边打包我们的讯息,程序代码如下:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// 若发生例外则不在这边处理
if (actionExecutedContext.Exception != null)
return;

base.OnActionExecuted(actionExecutedContext);

ApiResultModel result = new ApiResultModel();

// 取得由 API 返回的状态代码
result.Status = actionExecutedContext.ActionContext.Response.StatusCode;
// 取得由 API 返回的资料
result.Data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
// 重新封装回传格式
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
}

3.接着我们一样要将此自定义的 Filter 注册到程序当中,所以一样到 App_Start → WebApiConfig.cs → Register 来注册我们的 Filter:
config.Filters.Add(new ApiErrorHandleAttribute());

4.重新建置后,当我们程序发生例外时也会依照我们的格式回传给使用者:
{
"Status": 400,
"Data": null,
"ErrorMessage": "尝试以零除。"
}

总结
就这样我们又成功解决了一个简单的案例,不过这边也需要提醒一下读者,一般在处理例外这边是不会直接将例外讯息回传给用户的,因为如果假设你今天丢出的例外有包含了一些比较敏感的信息,例如:数据库名称或数据表名称…等等,这样一来你的程序就间接的有了漏洞了,所以如果真的要用此程序代码记得后面例外捕捉那边还要在包装一下。

热心网友 时间:2023-11-23 22:07

在使用Web Api的时候,有时候只想返回JSON;实现这一功能有多种方法,这里提供两种方式,一种传统的,一种作者认为是正确的方法。

JSON in Web API – the formatter based approach

只支持JSON最普遍的做法是:首先清除其他所有的formatters,然后只保留JsonMediaTypeFormatter。

有了HttpConfiguration的实例,将会很简单的清除所有formatters,然后重新添加JsonMediaTypeFormatter。

实现代码如下:
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());这种方式虽然可以实现功能,但是所有的conent negotiation还是会发生,这就会产生以下额外的开销了。因为,已经知道要返回的结果了,也只想返回Json,其他的content negotiation都不需要了。

下面的方法可以很好的解决这个问题。

JSON in Web API – the conneg based approach

最好的方法是使用自定义的只返回Json Result的content negotiation代替Web Api中默认的content negotiation。

Conneg通过实现IContentNegotiator的Negotiator方法实现扩展。Negotiator方法返回ContentNegotiationResult(它包装了选择的headers和formatter)。

下面的方法通过传递一个JsonMediaTypeFormatter给自定义的conneg negotiator,让它一直返回applicaton/json 的content-type以及JsonMediaTypeFormatter。这种方法避免了每次请求都要重新创建一次formatter。

代码如下:

public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;

public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}

public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}接下来,需要在HttpConfiguration实例上注册新的实现机制:

var jsonFormatter = new JsonMediaTypeFormatter();

//optional: set serializer settings here
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));

通过替换默认的DefaultContentNegotiator,使用自定义的JsonContentNegotiator,它只支持Json,而且可以马上返回。

通过使用自定义的JsonContentNegotiator替换系统默认的DefaultContentNegotiator,很好的实现Web Api只返回Json的功能,而且没有额外的开销。
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
韩国EMS19.3公斤的箱子去中国要多少钱 俄罗斯快递查询 青春期孩子想买贵东西怎么办 发现青春期孩子买成人用品怎么办?? ...运送石油到中国上海湾,途中经过的海峡有( )A.白令海峡B.马六甲海峡C... 中俄货物运输企业 ...湾往我国运送石油的船只需要经过的海峡是: ( ) A.直布罗陀海峡 B... 童心唱响祖国完整版歌词【26句精选】 苹果有面容和没面容有什么区别 苹果无面容影响 响应头 x-aspnet-version是什么东西 为什么建筑上将墙称为女儿墙? 春节经济的文化消费 “女儿墙”这个名字的来由 女儿墙的来历 为什么宝宝睡着了不能拍照? 用影楼照相机给出生不到十几天的婴儿照相影响有多大? 公路两侧的墓地不允许立碑吗? 什么样的墓地能立碑 坟墓上能不能立碑?依据何来? 阴宅立碑在二十四山中什么山向不能立碑? 你对团队工作方法是否有更进一步的认识 为什么有的坟墓 大人说不可以立碑呢? 有什么原因? 关于如何管理团队的思考 对管理的认识有哪些? 问个关于风水的事,他们说有的坟墓可以立碑,有的不能立。有的不能立的依据是什么? 坟墓上能不能立碑?依据何来呢? 什么样的坟墓不能立碑 win10注册表无法搜索怎么办 Win10注册表编辑器中搜索功能不能用了怎么办 有什么比较好的免费网站后台管理系统 Apache TomcatHTTP响应消息头泄露信息的处理 口金包的第二圈要如何做? 华为曾经出过几款平板 如何给足疗店起名 纸杯可以变什么魔术? 旅途休闲的魔术有哪些? 谁有不要任何道具就可以变的魔术 用磁戒可以变些什么魔术? win10 屏幕 间歇性 黑一下 智能手机开关机键排线坏了,怎样能修好?最好能有教程。 cad总平面图中怎么画水路、电路(带v/带s)的线条/以及配电箱等等 如何快速学会电气原理?快速学会CAD?主要是绘电气配电箱图,有知道的可以QQ详谈,442319657,谢谢 请问多少岁可以磨骨啊? 磨颧骨最佳年龄? 1米7跑步,慢跑一步多少厘米? 磨骨手术有年龄限至吗 154身高跑步步幅多少? 韩国原辰整形,磨骨手术的年龄*医生好,请问磨骨手术有年龄*吗?多少岁可以做磨骨手术? 削骨改脸型多少岁数比较合适