ASP.NET Core自定义响应内容

问题

在业务开发中,对Web API的返回格式有一定要求,需要是定制化的Json结构,用于前端统一处理:

{Status : 0,Message: "",Info : xxx
}
  • Status表示响应的状态码,0为成功;

  • Message表示错误消息,Status不为0时返回;

  • Info表示API返回的实际数据,Json格式;

简单实现

当然,你可以定义一个数据结构作为每个API的返回值:

public class ResponseData<T>
{public int Status { get; set; } = 0;public string Message { get; set; }public T Info { get; set; }public ResponseData(T obj){Info = obj;}
}[HttpGet]
public ResponseData<IEnumerable<WeatherForecast>> Get()
{var rng = new Random();var data = Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();return new ResponseData<IEnumerable<WeatherForecast>>(data);
}

但是如果这样实现,每一个API方法都必须修改,实例化一个ResponseData对象返回。如果以后业务修改,要移除这个自定义结构又是件麻烦事。

有没有一劳永逸、并且更加优雅的实现方式呢?

自定义响应内容

既然这个Json结构是在原有的返回数据外围再包了一层,那么我们直接获取Web API的原始Response.Body,然后格式化成新的JSon在赋值给Response.Body不就可以了! 

但是,实际验证时发现在.NET 5下已经无法改写,无任何数据返回。示例代码如下:

app.Use(async (context, next) =>
{var newContent = string.Empty;using (var newBody = new MemoryStream()){context.Response.Body = newBody;await next();context.Response.Body = new MemoryStream();newBody.Seek(0, SeekOrigin.Begin);newContent = new StreamReader(newBody).ReadToEnd();newContent += ", World!";await context.Response.WriteAsync(newContent);}
});

难道这条路走不通?

IHttpResponseBodyFeature

在aspnetcore的源代码中找到了ResponseCompressionMiddleware(https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs)。

它是用来处理响应压缩的中间件,也就是说对响应做了处理,看看它的实现方式:

public async Task Invoke(HttpContext context)
{if (!_provider.CheckRequestAcceptsCompression(context)){await _next(context);return;}var originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>();var originalCompressionFeature = context.Features.Get<IHttpsCompressionFeature>();Debug.Assert(originalBodyFeature != null);var compressionBody = new ResponseCompressionBody(context, _provider, originalBodyFeature);context.Features.Set<IHttpResponseBodyFeature>(compressionBody);context.Features.Set<IHttpsCompressionFeature>(compressionBody);try{await _next(context);await compressionBody.FinishCompressionAsync();}finally{context.Features.Set(originalBodyFeature);context.Features.Set(originalCompressionFeature);}
}

它将IHttpResponseBodyFeature进行了替换:

context.Features.Set<IHttpResponseBodyFeature>(compressionBody);

IHttpResponseBodyFeature到底是个什么玩意?

“ASP.NET Core 中的请求功能”(https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/request-features?view=aspnetcore-5.0)作出了相应的解释: 

ASP.NET Core 在 Microsoft.AspNetCore.Http.Features 中定义了许多常见的 HTTP 功能接口,各种服务器和中间件共享这些接口来标识其支持的功能。服务器和中间件还可以提供自己的具有附加功能的接口。 

ResponseCustomBody

那我们就依葫芦画瓢,实现我们的ResponseCustomBody:

public class ResponseCustomBody : Stream, IHttpResponseBodyFeature
{private readonly HttpContext _context;private readonly IHttpResponseBodyFeature _innerBodyFeature;private readonly Stream _innerStream;public ResponseCustomBody(HttpContext context,IHttpResponseBodyFeature innerBodyFeature){_context = context;_innerBodyFeature = innerBodyFeature;_innerStream = innerBodyFeature.Stream;} public Stream Stream => this;public PipeWriter Writer => throw new NotImplementedException();public override bool CanRead => false;public override bool CanSeek => false;public override bool CanWrite => _innerStream.CanWrite;public override long Length => throw new NotImplementedException();public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }public async Task CompleteAsync(){await _innerBodyFeature.CompleteAsync();}public void DisableBuffering(){_innerBodyFeature.DisableBuffering();}public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellationToken = default){return _innerBodyFeature.SendFileAsync(path, offset, count, cancellationToken);}public Task StartAsync(CancellationToken cancellationToken = default){return _innerBodyFeature.StartAsync(cancellationToken);}public override void Flush(){_innerStream.Flush();}public override int Read(byte[] buffer, int offset, int count){throw new NotImplementedException();}public override long Seek(long offset, SeekOrigin origin){throw new NotImplementedException();}public override void SetLength(long value){throw new NotImplementedException();}public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken){var json = System.Text.Encoding.UTF8.GetString(buffer).TrimEnd('\0');json = "{\"Status\":0, \"Info\":" + json + " }";buffer = System.Text.Encoding.UTF8.GetBytes(json);count = buffer.Length;await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);}public override void Write(byte[] buffer, int offset, int count){throw new NotImplementedException();}
}

关键代码就是下面这段,我们取出原始响应内容,格式化后再写入:

public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{var json = System.Text.Encoding.UTF8.GetString(buffer).TrimEnd('\0');json = "{\"Status\":0, \"Info\":" + json + " }";buffer = System.Text.Encoding.UTF8.GetBytes(json);count = buffer.Length;await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
}

最后,我们再定义一个中间件使用ResponseCustomBody替换IHttpResponseBodyFeature:

public class ResponseCustomMiddleware : IMiddleware
{public async Task InvokeAsync(HttpContext context, RequestDelegate next){var originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>();var customBody = new ResponseCustomBody(context, originalBodyFeature);context.Features.Set<IHttpResponseBodyFeature>(customBody);try{await next(context);}finally{context.Features.Set(originalBodyFeature);}} 
}

运行效果也能满足我们的要求: 

结论

在本文中,我们利用了ASP.NET Core的功能接口实现了自定义格式响应。

你也可以尝试使用功能接口实现一些有用的功能。

如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!

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

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

相关文章

这个被称为20世纪最伟大人物的最强理科生,到底有多强,你根本不了解

▲ 点击查看从素有“计算机界诺贝奖”之称的图灵奖&#xff0c;距离现在已经走过了半个多世纪。在这50多年间&#xff0c;诞生了几十位顶尖的计算机科学家以及几十项科技成就。从智能设备到5G&#xff0c;从无人车到AI&#xff0c;在计算机的进化版图中&#xff0c;计算机从一个…

蚁族之痛:过年如过关

看着腾讯网长大&#xff0c;从十年前到现在&#xff0c;现在QQ.com也是我认为门户里做得最好的了。看了一下今天的话题《蚁族之痛&#xff1a;春节恐归症》&#xff0c;感觉说得很对&#xff0c;说出了我们这代人的心声。以前听大人们讲年关的来头&#xff1a;说是农民最怕的就…

倾情研发十年记

写在亚洲研究院成立十周年、亚洲工程院成立五周年之际今天是微软亚洲研究院成立十周年的日子&#xff0c;微软亚洲工程院也刚刚度过它的五周岁生日。在这个特殊的时刻&#xff0c;回望这些年来全心投入研发的日子&#xff0c;我心中充满了自豪和对未来的期待。十年前&#xff0…

华为二面!!!被问常用API,这也太偏门了吧,我秀了一波hhhh~

华为二面!!&#xff01;被问常用API&#xff0c;这也太偏门了吧&#xff0c;我秀了一波hhhh~常用API一、API概述二、Scanner类代码三、Random类代码四、* ArrayList类**存储基本数据类型**代码五、匿名对象昨天我去了华为面试&#xff0c;问我常用API&#xff0c;我以为我被搞到…

空值为0非空为1_万达广场4周年,1降到底!0元送万张杂技团门票、人气餐饮6.8折,这波周年庆我先锁为敬...

作为向来宠你没商量的国民商场4周年店庆&#xff0c;福利当然少不了&#xff01;黄金克减zui高100元餐饮全单6.8折&#xff0c;更享折上zhe更有街舞争霸赛、王者荣耀争霸赛等精彩活动等你打卡&#xff0c;?就问你来不来&#xff1f;精品超市&#xff0c;实力宠粉没有套路&…

linux注意的一些地方

assert宏的原型定义在<assert.h>中&#xff0c;其作用是如果它的条件返回错误&#xff0c;则终止程序执行 #include <assert.h>void assert( int expression ); assert的作用是现计算表达式 expression &#xff0c;如果其值为假&#xff08;即为0&#xff09;&…

处理多个选择结果

比如说选择段落 $("p”)&#xff0c;这样就会把页面的所有段落都选中。jQuery提供.each()方法来对选中的结果进行循环处理&#xff0c;而且在每次执行函数时&#xff0c;都会给函数传递匹配元素在选中结果里所处位置的数字值作为参数&#xff08;从零开始的整形变量&#…

随手拈来尽是折劲额事体

昨天中午&#xff0c;justina同学请我去港丽吃饭&#xff0c;世界顿时美好了&#xff01; 猛地发现&#xff0c;港丽的酸菜鱼竟然非常好吃&#xff0c;除了价钱贵&#xff0c;基本没有缺点了。 吃饭的时候&#xff0c;看到两件有劲的事情&#xff0c;一件比一件更折劲&#xff…

清北学霸的书单居然那么有讲究?看看你比学霸少看了哪些书......

一直以来&#xff0c;少年物理学家为大家分享了许多物理知识、科学家的小故事&#xff0c;以及生活中的趣味科学&#xff0c;得到了许多粉丝们的支持&#xff01;为了表达对大家的感谢&#xff0c;在新春之际&#xff0c;我们决定发起“最美学习萌娃”评选活动&#xff0c;借助…

FastGithub让Github畅通无阻

前言我近半年来被github的抽风虐得没脾气了&#xff0c;虽然我有代理的方式来上网&#xff0c;但代理速度并不理想&#xff0c;而且有时代理服务一起跟着抽风。这时候&#xff0c;我会搜索“github访问不了”相关题材&#xff0c;其中有“Github镜像服务器加速版”的&#xff0…

.Net性能调优-垃圾回收!!!最全垃圾回收来了

目前项目开发基本都基于.NetCore 3.1以上了&#xff0c;有些老版本的规则和概念也没有列出来,低版本的垃圾回收类型和内存释放方式会有所不同 垃圾回收器为什么存在 开发人员不必手动释放内存。 有效分配托管堆上的对象。 回收不再使用的对象&#xff0c;清除它们的内存&…

平流式初沉池贮砂斗计算_城市污水处理厂产泥量的计算

污泥是污水处理过程的副产物&#xff0c;包括筛余物、沉泥、浮渣和剩余污泥等。污泥体积约占处理水量的0.3%~0.5%左右&#xff0c;如水进行深度处理&#xff0c;污泥量还可能增加0.5~1倍。一、污水处理污泥分类及特性1、按成分不同分污泥&#xff1a;以有机物为主要成分。其主要…

C# 图片加水印例程

using System;using System.IO;using System.Collections;using System.Drawing;using System.Drawing.Drawing2D;using System.Drawing.Imaging; namespace Imag_writer{/// <summary>/// 水印的类型/// </summary>public enum WaterMarkType{ /// <summary&…

从N个元素中选择第i小的元素

时常在笔试,面试题中看到这个问题,《算法导论》中给出了很好的解答。 Selection of the ith smallest element of the array A can be done in θ(n) times. The psuedocode is following: CodeRandomized_Select(A,p,r,i){ if pr then return A[p] qRandomized_…

Blazor 模板化组件开发指南

翻译自 Waqas Anwar 2021年4月15日的文章 《A Developer’s Guide To Blazor Templated Components》 [1]在我之前的一篇文章 Blazor 组件入门指南中&#xff0c;我介绍了组件参数&#xff0c;并向您展示了如何将数据作为参数传递给 Blazor 组件以定制化其功能。在这篇文章中&a…

别太贪婪,这些技能能让你一辈子满足

全世界只有3.14 % 的人关注了青少年数学之旅在这个资讯丰富且易获取的时代&#xff0c;越来越多的人不愿意花时间阅读书籍&#xff0c;碎片化阅读成了主流。人们获取的东西多而杂&#xff0c;很难系统、全面。海量信息对人是冲击&#xff0c;更是诱惑。谁不想了解天下奇闻&…

纳尼???我JVM优化过头了,直接把异常信息优化没了?怎么办

你好呀&#xff0c;我是why。 你猜这次我又要写个啥没有卵用的知识点呢&#xff1f; 不好意思&#xff0c;问的稍微有点早了&#xff0c;啥提示都没给&#xff0c;咋猜呢&#xff0c;对吧&#xff1f; 先给你上个代码&#xff1a; public class ExceptionTest {public stati…

angular $watch

在scope内置的所有函数中&#xff0c;用得最多的可能就是$watch 函数了&#xff0c;当你的数据模型中某一部分发生变化时&#xff0c;$watch函数可以向你发出通知。你可以监控单个对象的属性&#xff0c;也可以监控需要经过计算的结果&#xff08;函数&#xff09;&#xff0c;…

C# 读写ACCESS的OLE对象,演示图片与长文件的读写

网络上的读写OLE对象的代码是多&#xff0c;不过多是转载的&#xff0c;大部分人从来都没实际测试过&#xff0c;只是COPY来COPY去。我重来没看到一个真正可以运行的东东。没办法&#xff0c;只有自力更生&#xff0c;花了一点时间出了点研究成果&#xff0c;写到这里做个记录。…

RHCE课程-初级部分6、编辑工具VIM,网络配置,进程优先,日志文件简介。

我们通常用各种编辑工具来处理文本文件 常用的编辑工具:VIVIMEMACSVI和VIM的区别它们都是多模式编辑器&#xff0c;不同的是vim 是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。vim的这些优势主要体现在以下几个方面&#xff1a;易…