统一NET Core WebApi返回结果


    private static readonly string[] Summaries = new[]
    {
       "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    [

这个时候确实变得很美好了,但是还是没有逃脱一点,那就是我还是得通过特定的方法来得到一个 ResponseResult<t></t>类型的返回结果,包括我们给 ResponseResult<t></t>类封装静态方法,或者甚至是定义 ApiControllerBase基类,都是为了进一步简化这个操作。现在呢我想告别这个限制,我能不能把返回的结果直接就默认的转化成 ResponseResult<t></t>类型的结果呢?当然可以,这也是通过ASP.NET Core的封装思路中得到的启发,借助 implicit自动完成隐式转换,这个在ASP.NET Core的 ActionResult<t></t>类中也有体现

public static implicit operator ActionResult<TValue>(TValue value)
{
    return new ActionResult(value);
}

通过这个思路我们可以进一步完善 ResponseResult<t></t>类的实现方式,给它添加一个隐式转换的操作,仅仅定义一个方法即可,在 ResponseResult<t></t>类中继续完善

这种对于绝大部分返回成功结果的时候提供了非常简化的操作,这个时候如果你再去使用action的时候就可以进一步来简化返回值的操作了

因为我们定义了 TResponseResult<t></t>的隐式转换,所以这个时候我们就可以直接返回结果了,而不需要手动对结果返回值进行包装。

在上面我们为了尽量简化action返回 ResponseResult<t></t>的统一返回结构的封装,已经对 ResponseResult<t></t>类进行了许多的封装,并且还通过封装 ApiControllerBase基类进一步简化这一操作,但是终究还是避免不了一点,那就是很多时候可能想不起来对action的返回值去加 ResponseResult<t></t>类型的返回值,但是我们之前的所有封装都得建立在必须要声明 ResponseResult<t></t>类型的返回值的基础上才行,否则就不存在统一返回格式这一说法了。所以针对这些漏网之鱼,我们必须要有统一的拦截机制,这样才能更完整的针对返回结果进行处理,针对这种对action返回值的操作,我们首先想到的就是定义 &#x8FC7;&#x6EE4;&#x5668;进行处理,因此笔者针对这一现象封装了一个统一包装结果的过滤器,实现如下

public class ResultWrapperFilter : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
        var actionWrapper = controllerActionDescriptor?.MethodInfo.GetCustomAttributes(typeof(NoWrapperAttribute), false).FirstOrDefault();
        var controllerWrapper = controllerActionDescriptor?.ControllerTypeInfo.GetCustomAttributes(typeof(NoWrapperAttribute), false).FirstOrDefault();

在使用WebAPI的过程中,我们的action绝大部分是直接返回 ViewModelDto而并没有返回 ActionResult类型相关,但是无妨,这个时候MVC的底层操作会为我们将这些自定义的类型包装成 ObjectResult类型的,因此我们的 ResultWrapperFilter过滤器也是通过这一机制进行操作的。这里有两点需要考虑的

  • 首先是,我们必须要允许并非所有的返回结果都要进行 ResponseResult<t></t>的包装,为了满足这一需求我们还定义了 NoWrapperAttribute来实现这一效果,只要Controller或Action有 NoWrapperAttribute的修饰则不对返回结果进行任何处理。
  • 其次是,如果我们的Action上的返回类型已经是 ResponseResult<t></t>类型的,则也不需要对返回结果进行再次的包装。

关于 ResultWrapperFilter的定义其实很简单,因为在这里它只是起到了一个标记的作用

到了这里,还有一种特殊的情况需要注意,那就是当程序发生异常的时候,我们上面的这些机制也是没有办法生效的,因此我们还需要定义一个针对全局异常处理的拦截机制,同样是可以使用统一异常处理过滤器进行操作,实现如下

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger _logger;
    public GlobalExceptionFilter(ILogger logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {

写完过滤器了,千万不能忘了全局注册一下,否则它也就只能看看了,不会起到任何效果

builder.Services.AddControllers(options =>
{
    options.Filters.Add();
    options.Filters.Add();
});

当然针对上面两种针对漏网之鱼的处理,在ASP.NET Core上还可以通过中间件的方式进行处理,至于过滤器和中间件有何不同,相信大家已经非常清楚了,核心不同总结起来就一句话 &#x4E8C;&#x8005;&#x7684;&#x5904;&#x7406;&#x9636;&#x6BB5;&#x4E0D;&#x540C;&#xFF0C;&#x5373;&#x9488;&#x5BF9;&#x7BA1;&#x9053;&#x7684;&#x751F;&#x547D;&#x5468;&#x671F;&#x5904;&#x7406;&#x662F;&#x4E0D;&#x4E00;&#x6837;&#x7684;&#xFF0C;&#x4E2D;&#x95F4;&#x4EF6;&#x53EF;&#x4EE5;&#x5904;&#x7406;&#x4EFB;&#x4F55;&#x751F;&#x547D;&#x5468;&#x671F;&#x5728;&#x5B83;&#x4E4B;&#x540E;&#x7684;&#x573A;&#x666F;&#xFF0C;&#x4F46;&#x662F;&#x8FC7;&#x6EE4;&#x5668;&#x53EA;&#x7BA1;&#x7406;Controller&#x8FD9;&#x4E00;&#x5757;&#x7684;&#x4E00;&#x4EA9;&#x4E09;&#x5206;&#x5730;但是针对结果包装这一场景,笔者觉得使用过滤器的方式更容易处理一点,因为毕竟我们是要操作Action的返回结果,通过过滤器中我们可以直接拿到返回结果的值。但是这个操作如果在中间件里进行操作的话,只能通过读取 Response.Body进行操作了,笔者这里也封装了一个操作,如下所示

public static IApplicationBuilder UseResultWrapper(this IApplicationBuilder app)
{
        var serializerOptions = app.ApplicationServices.GetRequiredService>().Value.JsonSerializerOptions;
        serializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
        return app.Use(async (context, next) =>
        {
            var originalResponseBody = context.Response.Body;
            try
            {

相信通过上面的处理,我们就可以更容易的看出来,谁更容易的对统一结果进行包装处理了,毕竟我们是针对Action的返回结果进行处理,而过滤器显然就是为针对Controller和Action的处理而生的。但是通过中间件的方式能更完整的针对结果进行处理,因为许多时候我们可能是在自定义的中间件里直接拦截请求并返回,但是根据二八原则这种情况相对于Action的返回值毕竟是少数,有这种情况我们可以通过直接 ResponseResult<t></t>封装的方法进行返回操作,也很方便。但是这个时候呢,关于异常处理我们通过全局异常处理中间件,则能更多的处理更多的场景,且没有副作用,看一下它的定义

public static IApplicationBuilder UseException(this IApplicationBuilder app)
{
    return app.UseExceptionHandler(configure =>
    {
        configure.Run(async context =>
        {
            var exceptionHandlerPathFeature = context.Features.Get();
            var ex = exceptionHandlerPathFeature?.Error;
            if (ex != null)
            {
                var _logger = context.RequestServices.GetService>();
                var rspResult = ResponseResult<object>.ErrorResult(ex.Message);
                _logger?.LogError(ex, message: ex.Message);
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                context.Response.ContentType = "application/json;charset=utf-8";
                await context.Response.WriteAsync(rspResult.SerializeObject());
            }
        });
    });
}

使用全局异常梳理中间件是没有副作用的,主要因为在异常处理的时候我们不需要读取 Response.Body进行读取操作的,所以至于是选择异常处理中间件还是过滤器,大家可以针对自己的实际场景进行选择,两种方式都是可以的。

本文主要是展示了针对ASP.NET Core WeApi结果统一返回格式的相关操作,通过示例我们一步一步的展示了完成这一目标的不断升级的实现,虽然整体看起来比较简单,但是却承载着笔者一次又一次的思考升级。每次实现完一个阶段,都会去想有没有更好的方式去完善它。这其中还有一些思路来自微软源码为我们提供的思路,所以很多时候还是建议大家去看一看源码的,可以在很多时候为我们提供一种解决问题的思路。正如我看到的一句话,读源码也是一种围城,外面的人不想进去,里面的人不想出来。如果大家有更好的实现方式,欢迎一起讨论。曾经的时候我会为自己学到了一个新的技能而感到高兴,到了后来我会对有一个好的思路,或者好的解决问题的方法而感到高兴。读万卷书很重要,行万里路同样重要,读书是沉淀,行路是实践,结合到一起才能更好的促进,而不是只选择一种。

Original: https://www.cnblogs.com/Leo_wl/p/16472550.html
Author: HackerVirus
Title: 统一NET Core WebApi返回结果

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/547627/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MyBatis学习大全(狂神秦疆版)

    一、 MyBatis 1.什么是Mybaits 概念:MyBatis 是一款优秀的 持久层框架 它支持自定义 SQL、存储过程以及高级映射。 MyBatis 免除了几乎所有的 JD…

    技术杂谈 2023年6月21日
    070
  • Xperf Basics: Recording a Trace (the easy way)(转)

    Some time ago I wrote a long and detailed post about how to record traces using xperf. The…

    技术杂谈 2023年5月31日
    093
  • 期末求加分

    信2005-3班 20203910 陈振辉 和王建民老师度过了一个愉快的学期,期末希望成绩能加分,申请理由如下: 1.在第一次课上动手又动脑中,第一阶段班级达到前15名提交要求。 …

    技术杂谈 2023年6月21日
    048
  • 再也不用担心重装VSCode了

    1. 关于Settings Sync插件 Setings Sync插件可以同步你的VSCode配置到 Github Gist,当你更换电脑重新搭建VSCode环境的时候,直接使用该…

    技术杂谈 2023年7月23日
    068
  • Self-Attention:初步理解

    Self-Attention 的基本结构与计算 Attention(注意力)实际上就是权重的另一种应用的称呼,其具体结构与初始输入的 content (\vec{x_{1}}, \…

    技术杂谈 2023年7月24日
    060
  • Jquery获取selelct选中值

    javascript;gutter:true; 误区:</p> <pre><code> 一直以为jquery获取select中option被选中…

    技术杂谈 2023年5月31日
    091
  • Typora-发布文章到博客园

    Typora-发布文章到博客园 发布文件到博客园,需要先对博客园进行设置: 进入 帐户中心, 点击 博客设置 滑动滚动条到最底部,点击 其它设置,开启”允许 MetaW…

    技术杂谈 2023年6月1日
    0101
  • 使用ThreadLocal请务必remove

    特别注意,web容器的线程是重复使用的,web容器使用了线程池,当一个请求使用完某个线程,该线程会放回线程池被其它请求使用,这就导致一个问题,不同的请求还是有可能会使用到同一个线程…

    技术杂谈 2023年5月31日
    076
  • YAML注释

    既然对YAML的语法和基础知识感到满意,那么进一步了解它的细节。在本章中,将了解如何在YAML中使用注释。 YAML支持单行注释。 下面借助一个例子来解释其结构 – Y…

    技术杂谈 2023年5月31日
    079
  • graylog server 模块说明一 入口简单说明

    protected void startCommand() { final AuditEventSender auditEventSender = injector.getInst…

    技术杂谈 2023年5月30日
    072
  • Spring5 学习笔记

    学习地址: B站-动力节点 个人代码: GitHub Spring 概述 1.1 Spring 简介 Spring Framework 是一个使用Java开发的、轻量级的、开源框架…

    技术杂谈 2023年7月11日
    085
  • 「免费开源」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之EXCEL数据导出(十三)

    基于Vue和Quasar的前端SPA项目实战之数据导出(十三) 回顾 通过之前一篇文章基于Vue和Quasar的前端SPA项目实战之数据导入(九)的介绍,通过配置的方式可以零代码实…

    技术杂谈 2023年7月24日
    073
  • 【赵渝强老师】使用Oracle的跟踪文件

    一、什么是跟踪文件? 跟踪文件中包含了大量而详细的诊断和调试信息。通过对跟踪文件的解读和分析,我们可以定位问题、分析问题和解决问题。从跟踪文件的产生的来源来看,跟踪文件又可以分为两…

    技术杂谈 2023年7月24日
    070
  • 前端富文本基础及实现

    在日常生活中我们会经常接触到各种各样的文档格式和形式,其中富文本在文档格式中扮演了重要角色。对于前端而言,富文本产品也层出不穷,其应用也越来越广。 这篇文章将会为大家介绍前端富文本…

    技术杂谈 2023年5月31日
    0104
  • C#使用Google实现在线翻译

    本文部分参考了GitHub中wadereye的代码,在源代码基础上修改了部分针对tkk的筛选及使用逻辑。 由于谷歌的tkk值规则一直在变,且api的url也不是固定的,所以做了配置…

    技术杂谈 2023年5月30日
    090
  • Keka for Mac(mac压缩解压软件)中文版

    Original: https://www.cnblogs.com/123ccy/p/16551434.htmlAuthor: -Mac123-Title: Keka for Ma…

    技术杂谈 2023年5月31日
    086
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球