dotNET Core WebAPI 统一处理(返回值、参数验证、异常)

现在 Web 开发比较流行前后端分离

640?wx_fmt=jpeg

现在 Web 开发比较流行前后端分离,我们的产品也是一样,前端使用Vue,后端使用 dotNet Core WebAPI ,在写 API 的过程中有很多地方需要统一处理

  • 文档

  • 参数验证

  • 返回值

  • 异常处理

本文就说说 API 的统一处理这些事。

环境

dotNet Core:2.1

文档

Swagger 是一个 API 文档生成框架,在非 Core 时代就一直在使用,现在前后端分离的模式下,API 文档更是非常重要,让前端开发人员和后端开发人员能更好的沟通和合作,前端开发人员在 Swagger 可以了解到接口的地址、入参、出参,还能模拟调用,非常方便。

安装

在 VS For Mac 中创建 API 项目 DotNetCoreApiSample ,在依赖项中的 NuGet 上点击右键,选择添加包,如下图:

640?wx_fmt=png

搜索 Swashbuckle.AspNetCore,选中搜索结果的第一条,点击「添加包」按钮进行添加。

配置

Startup 类的 ConfigureServices 方法中添加

services.AddSwaggerGen(options =>
{options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info{Version = "v1",Title = "DotNet Core WebAPI文档"});});

Startup 类的 Configure 方法中添加

app.UseSwagger();
app.UseSwaggerUI(c =>
{c.SwaggerEndpoint("/swagger/v1/swagger.json", "DotNet Core WebAPI文档");
});

运行效果

运行 WepAPI 项目,在浏览器中输入 http://localhost:5000/swagger ,效果如下

640?wx_fmt=png

参数验证

此处所说的参数验证指的是实体类型的参数验证,通过在实体的属性上添加特性的方式来实现。

简单实现

创建名为 ValidationDemoController 的 API 类,代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;namespace DotNetCoreApiSample.Controllers
{[Route("api/[controller]")]public class ValidationDemoController : Controller{[HttpPost]public IActionResult AddUser([FromBody]User user){string errorMessage = string.Empty;if (!ModelState.IsValid){foreach (var item in ModelState.Values){foreach (var error in item.Errors){errorMessage += error.ErrorMessage + "|";}}}if(!string.IsNullOrEmpty(errorMessage)){return BadRequest(errorMessage);}return Ok();}}public class User{[Required(ErrorMessage = "用户Code不能为空")]public string Code { get; set; }[Required(ErrorMessage = "用户名称不能为空")]public string Name { get; set; }[Required(ErrorMessage = "用户年龄不能为空")][Range(1, 100, ErrorMessage = "年龄必须介于1~100之间")]public int Age { get; set; }public string Address { get; set; }}
}

  • 实体类属性使用 Required 等特性需要引用命名空间System.ComponentModel.DataAnnotations

  • 除了上面的 Required 和 Range 标记,还有很多实用的标记,详细参考:https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations(v=vs.110).aspx

  • 上面的示例代码将错误信息的收集写在了接口方法中,这是一个很不好的做法,仅仅实现了功能,下面将通过过滤器的方式来进行重构,统一处理错误信息

重构

添加名为 ValidateModelAttribute 的过滤器类,继承 ActionFilterAttribute ,代码如下

namespace DotNetCoreApiSample.Filters
{public class ValidateModelAttribute : ActionFilterAttribute{public override void OnActionExecuting(ActionExecutingContext context){if (!context.ModelState.IsValid){var result = context.ModelState.Keys.SelectMany(key => context.ModelState[key].Errors.Select(x => new ValidationError(key, x.ErrorMessage))).ToList();context.Result = new ObjectResult(result);}}}public class ValidationError{[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]public string Field { get; }public string Message { get; }public ValidationError(string field, string message){Field = field != string.Empty ? field : null;Message = message;}}
}

Startup 类的 ConfigureServices 方法中添加下面代码:

services.AddMvc(options =>
{options.Filters.Add<ValidateModelAttribute>();
});

使用 Postman 调用结果如下

640?wx_fmt=png

返回值

返回值的统一处理需要下面几个步骤:

  • 创建统一返回结果的实体类,所有的接口方法都返回固定格式,方便前端统一处理

  • 创建过滤器,过滤器用来拦截请求,包装结果,统一输出

  • Startup 类中进行配置注册

结果实体类

接口的返回值需要统一的格式,下面的属性字段是我认为必须要有的

  • Result:返回的结果

  • Message:出现错误或需要提示时的提示文本内容

  • Code:调用成功、失败或出错时的编码

  • ReturnStatus:用来判断接口调用状态的

创建返回结果的实体类 BaseResultModel

public class BaseResultModel
{public BaseResultModel(int? code = null, string message = null,object result = null, ReturnStatus returnStatus = ReturnStatus.Success){this.Code = code;this.Result = result;this.Message = message;this.ReturnStatus = returnStatus;}public int? Code { get; set; }public string Message { get; set; }public object Result { get; set; }public ReturnStatus ReturnStatus { get; set; }
}
public enum ReturnStatus
{Success = 1,Fail = 0,ConfirmIsContinue = 2,Error = 3
}

过滤器类

创建名称为 ApiResultFilterAttribute 的过滤器类,该类继承 ActionFilterAttribute ,具体代码如下

public class ApiResultFilterAttribute : ActionFilterAttribute
{public override void OnActionExecuting(ActionExecutingContext context){base.OnActionExecuting(context);}public override void OnResultExecuting(ResultExecutingContext context){var objectResult = context.Result as ObjectResult;context.Result = new OkObjectResult(new BaseResultModel(code:200, result: objectResult.Value));}
}

在过滤器中将接口的返回值获取后重新包装到 BaseResultModel 模型类中进行返回。

Startup 配置

在 Startup 类的 ConfigureServices 方法中添加如下代码

services.AddMvc(options =>
{options.Filters.Add<ValidateModelAttribute>();options.Filters.Add<ApiResultFilterAttribute>();
});

添加示例接口方法

[HttpGet]
public IActionResult GetUserCode()
{return Ok("oec2003");
}

运行效果

使用 Postman 调用该接口方法,返回结果如下

640?wx_fmt=png

继续重构参数验证

添加了返回值的过滤器类后,调用之前的参数验证的接口,会发现返回结果如下

{"code": 200,"message": null,"result": [{"field": "Age","message": "年龄必须介于1~100之间"}],"returnStatus": 1
}

接口会调用两次过滤器,先调用参数验证的过滤器,再调用返回值的过滤器,导致验证失败的接口返回值状态也是成功的,所以需要做进一步重构。

1、添加 ValidationFailedResultModel 类

public class ValidationFailedResultModel : BaseResultModel
{public ValidationFailedResultModel(ModelStateDictionary modelState){Code = 422;Message = "参数不合法";Result = modelState.Keys.SelectMany(key => modelState[key].Errors.Select(x => new ValidationError(key, x.ErrorMessage))).ToList();ReturnStatus = ReturnStatus.Fail;}
}public class ValidationError
{[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]public string Field { get; }public string Message { get; }public ValidationError(string field, string message){Field = field != string.Empty ? field : null;Message = message;}
}

将错误信息的收集移到了 ValidationFailedResultModel 类中,所以

2、修改 ValidateModelAttribute 过滤器,在修改代码之前,先要添加名为 ValidationFailedResult 的类,该类继承 ObjectResult ,用做参数验证的结果收集。

public class ValidationFailedResult: ObjectResult
{public ValidationFailedResult(ModelStateDictionary modelState): base(new ValidationFailedResultModel(modelState)){StatusCode = StatusCodes.Status422UnprocessableEntity;}
}

修改 ValidateModelAttribute 类

public override void OnActionExecuting(ActionExecutingContext context)
{if (!context.ModelState.IsValid){context.Result = new ValidationFailedResult(context.ModelState);}
}

3、修改 ApiResultFilterAttribute 过滤器,添加对 ValidationFailedResult 类型的判断

public override void OnResultExecuting(ResultExecutingContext context)
{if (context.Result is ValidationFailedResult){var objectResult = context.Result as ObjectResult;context.Result = objectResult;}else{var objectResult = context.Result as ObjectResult;context.Result = new OkObjectResult(new BaseResultModel(code: 200, result: objectResult.Value));}
}

4、调用参数验证接口结果如下

640?wx_fmt=png

异常处理

异常处理和参数验证的方式基本相同,有以下几个步骤

1、创建名为 CustomExceptionResultModel 的模型类

public class CustomExceptionResultModel:BaseResultModel
{public CustomExceptionResultModel(int? code, Exception exception){Code = code;Message = exception.InnerException != null ?exception.InnerException.Message :exception.Message;Result = exception.Message;ReturnStatus = ReturnStatus.Error;}
}

2、创建名为 CustomExceptionResult 的异常结果类

public class CustomExceptionResult:ObjectResult
{public CustomExceptionResult(int? code, Exception exception): base(new CustomExceptionResultModel(code, exception)){StatusCode = code;}
}

3、创建名为 CustomExceptionAttribute 的异常过滤器类,继承自 IExceptionFilter

public class CustomExceptionAttribute : IExceptionFilter
{public void OnException(ExceptionContext context){HttpStatusCode status = HttpStatusCode.InternalServerError;//处理各种异常context.ExceptionHandled = true;context.Result = new CustomExceptionResult((int)status, context.Exception);}
}

4、Startup 配置

在 Startup 类的 ConfigureServices 方法中添加如下代码

services.AddMvc(options =>
{options.Filters.Add<ValidateModelAttribute>();options.Filters.Add<ApiResultFilterAttribute>();options.Filters.Add<CustomExceptionAttribute>();
});

感兴趣的朋友可以在 Github 上下载示例代码进行调试。

总结

如果是从零开始搭建一个 WebAPI 项目,这些基础处理是必不可少的,有了这些做保障才能专注于业务代码的编写。

本文只是抛砖引玉,同样的思路我们还可以实现更多的功能,例如

  • 如果某些特殊接口需要直接返回值怎么办?

  • 怎样记录耗时较长的接口?

  • 怎样做接口的验证?

点击「阅读原文」可访问示例代码。

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.net测试篇之单元测试/集成测试神器Autofixture

autofixture简介有了单元测试框架加上Moq(后面我们会用单独章节来介绍moq),可以说测试问题基上都能搞定了.然而有了AutoFixture对单元测试来说可以说是如虎添翼,AutoFixture并且它能与moq,rhinomock等框架结合,对单元测试带来的便捷性,可维护性和扩展性更是难以言表,只有用用了…

DotNetCore 3.0 助力 WPF本地化

概览随着我们的应用程序越来越受欢迎,我们的下一步将要开发多语言功能。方便越来越多的国家使用我们中国的应用程序,基于 WPF 本地化,我们很多时候使用的是系统资源文件,可是动态切换本地化,就比较麻烦了。实现思路现在…

开源题材征集 + MVCEF Core 完整教程小结

到目前为止,我们的MVCEF Core 完整教程的理论部分就全部结束了,共20篇,覆盖了核心的主要知识点。下一阶段是实战部分,我们将会把这些知识点串联起来,用10篇(天)来完成一个开源项目。现向园友征集题材,你提需…

对微软的敌视何时休? 从一篇语言评论文章对C#的评价说起

看到一篇公众号文章《2020年什么编程语言最受欢迎,待遇最高?》,其中对C#的描述如下:点击阅读原文,看到这是一篇翻译文章:https://codinginfinite.com/top-programming-languages-2020-stats-surveys/这篇文…

ASP.NET Core on K8S深入学习(6)Health Check

本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。预计阅读时间为10分钟。01—关于K8S中的健康监测所谓Health Check,就是健康检查,即防微杜渐。K8S是一个编排引擎可以帮助我们快捷地部署容器集…

关于 .Net Core runtimeconfig 文件说明

项目的bin\debug\netcoreapp${Version}下面能够找到这个${AppName}.runtimeconfig.json文件,简单来说,它就是用来定义应用程序所用的共享框架(.Net Core App)以及运行时选项 的一个文件。一个简单的例子{ "runtimeOptions&q…

net core 3.0 之Grpc新特性小试牛刀

作者: 相信微服务大家伙都有听说和知道,好处弊端咱也不多说了,Grpc算是一个比较全面的微服务框架,也得到微软的支持总结下来就是,跨平台,可靠,通信快,扩展性强,网络消耗小…

CF1168D Anagram Paths(由必要到充分/虚树)

CF1168D Anagram Paths 对于这道题首先有一个关键的性质,那就是对于一个树,它是可重排的,当且仅当在树上任意一个节点,所有字母在相关联的字符串中出现次数最大值之和小于当前点到叶子的距离。这个性质可以通过归纳证明&#xff0…

基于Coravel定时任务之计算总页数

在物联网系统中,需要计算底端所有设备的总数,除以分页每页显示数量,进行一个总页数的显示。包括状态,告警,日志等等数据都需要对应的总页数的显示。2.1 TaskSchedulerTaskScheduler库只支持.net,且需要结合…

Docker系列之AspNetCore Runtime VS .NetCore Runtime VS SDK(四)

接下来我们就要慢慢步入在.NET Core中使用Docker的殿堂了,如题在开始之前,我们需要搞清楚一些概念,要不然看到官方提供如下一系列镜像,我们会一脸懵逼,不知道到底要使用哪一个。AspNetCore Runtime VS .NetCore Runti…

.NET Core跨平台部署于Docker(Centos)- 视频教程

.NET Core跨平台部署于Docker,Docker部署于Centos中,演示跨平台特性!以下视频教程,请带上耳机开始聆听往期教程:.NET开发框架(一)-框架介绍与视频演示.NET开发框架(二)-框架功能简述.NET开发框架(三)-高可用服务器端设计.NET开发框…

【清华集训2014】Sum)(类欧几里得算法)

【清华集训2014】Sum 然后本质上我们需要求解的就是那个带根号式子的奇偶性,然后我们发现这个式子很像是类欧几里得算法,求解一个斜率为无理数直线下的整点个数,然后我们直接对于一般形式求解,那么就是每次利用整数部分将斜率减小…

一本让我多花2倍时间读的书

这里是Z哥的个人公众号每周五11:45 按时送达当然了,也会时不时加个餐~我的第「87」篇原创敬上Hi,大家好,我是Z哥。熟悉我的小伙伴应该知道,我平时看书大多都很快,之前还把自己的速读技巧分享给了…

[NOI2005]月下柠檬树 (自适应辛普森)

P4207 [NOI2005]月下柠檬树 如图,我们要求的面积就是这些圆形跟梯形的组合,由于投射到地面上,显然有h′htanθh \frac{h}{tan \theta}h′tanθh​,由此我们就可以开始推导这个f(x)f(x)f(x)函数了。 所以转换为我们要推导出直线a…

Kong 1.3发布,原生gRPC代理、上游TLS交叉认证

Kong 1.3 发布了,此版本亮点包括支持原生 gRPC 代理、上游 TLS 交叉认证,以及一系列新功能和性能改进。原生 gRPC 代理越来越多的用户转向微服务架构,并且希望有对原生 gRPC 代理的支持,Kong 1.3 解决了这个问题,为支持…

对Windows桌面应用程序进行UI自动化测试

所谓UI自动化测试,就是模拟一个用户,对应用程序的UI进行操作,以完成特定场景的功能性集成测试。要对Windows桌面应用程序进行UI自动化测试,目前可选的技术主要是两种:VS自带的CodedUI Test和AppiumWinAppDriver。但是&…

项目实战中如何使用抽象类和接口

引子:时常会有这么一个疑惑,抽象类和接口功能好像,真正用起来该如何抉择呢??好问题。。来看看书上怎么说的(C#7.0本质论)虽然方法可在基类中声明为抽象成员,但是!&#x…

番茄日志发布1.0.3版本-增加Kafka支持

番茄日志(TomatoLog)能做什么可能你是第一次听说TomatoLog,没关系,我可以从头告诉你,通过了解番茄日志,希望能帮助有需要的朋友,番茄日志处理将大大降低你采集、分析、处理日志的过程。介绍Toma…

ArangoDB 3.5发布:流事务API、蒙面数据、搜索性能大幅提升、最短路径功能

ArangoDB 3.5 发布了。ArangoDB 是一个分布式原生的多模型数据库,具有灵活的文档、图形和键值数据模型。使用方便的 SQL 查询语言或 JavaScript 扩展构建高性能应用程序。此版本亮点包括:期待已久的 Streaming Transactions API,可以直接使用…

ASP.NET Core on K8S深入学习(7)Dashboard知多少

本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。在第二篇《部署过程解析与Dashboard》中介绍了如何部署Dashboard,但是没有更多地介绍如何使用Dashboard,本文就来对Dashboard的使用进行补充。…