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;回望这些年来全心投入研发的日子&#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;实力宠粉没有套路&…

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

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

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

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

从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…

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

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

WPF等待动画

WPF开发者QQ群&#xff1a; 340500857 | 微信群 -> 进入公众号主页 加入组织欢迎转发、分享、点赞、在看&#xff0c;谢谢~。 01—效果预览效果预览&#xff08;更多效果请下载源码体验&#xff09;&#xff1a;02—代码如下一、CycleLoading.cs 代码如下using System.Win…

假期别在家里要发霉了?可以靠他们度过无聊时光

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

cs6序列号 mac版photoshop_重磅!Parallels Desktop 16 M1版发布

Parallels 现已发布 Parallels Desktop 16 技术预览版&#xff0c;可以在搭载 M1 芯片的 Mac 电脑上运行。该公司表示&#xff0c;它创建了一个 “使用 Apple M1 Mac 芯片硬件辅助虚拟化的新虚拟化引擎”&#xff0c;允许用户在虚拟机中运行基于 Arm 的操作系统&#xff0c;例如…

华为二面!!!面试官直接问我Java中到底什么是NIO?这不是直接送分题???

华为二面&#xff01;&#xff01;&#xff01;面试官直接问我Java中到底什么是NIO&#xff1f;这不是直接送分题&#xff1f;&#xff1f;&#xff1f;什么是NIO缓冲区(Buffer)缓冲区类型获取缓冲区核心属性核心方法非直接缓冲区和直接缓冲区非直接缓冲区直接缓冲区通道(Chann…

Delphi中的容器类(3)

TBucketList和TObjectBucketList类 从Delphi6开始&#xff0c;VCL的Contnrs单元中又增加了两个新的容器类TBucketList和TObjectBucketList。TBucketList实际上也是一个简单基于哈希表的指针-指针对列表。接口定义如下&#xff1a; TBucketList class(TCustomBucketList)… pu…

一文读懂哈希和一致性哈希算法

哈希 Hash 算法介绍哈希算法也叫散列算法, 不过英文单词都是 Hash, 简单一句话概括, 就是可以把任意长度的输入信息通过算法变换成固定长度的输出信息, 输出信息也就是哈希值, 通常哈希值的格式是16进制或者是10进制, 比如下面的使用 md5 哈希算法的示例md5("123456"…

延迟开学?这些教育读书公众号可以帮助孩子学习! 你都关注了吗?

全世界只有3.14 % 的人关注了青少年数学之旅受新型冠状病毒疫情影响&#xff0c;日前&#xff0c;教育部印发《关于2020年春季学期延期开学的通知》。推迟开学时间&#xff0c;意味着寒假的延长。为此&#xff0c;小编精选这些优质的教育号和读书号帮助孩子学习&#xff01;理想…

go方法的深入探究(7.21增补)

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff09;哪些类型可以有方法&#xff1a; 1&#xff09;只能对命名类型和命名类型的指针编写方法&#xff1b; 2&#xff09;不能对接口类型和接口类型的指针编写方法&#xff1b; 3&#xff09;只能在定义命名类型…

element文件上传有文件但是后台接收为空_程序员提高篇:大规格文件(G)是如何做分片优化的?...

作者&#xff1a;凹凸实验室 链接&#xff1a;https://juejin.im/post/5ebb4346e51d451ef53793ad整体思路第一步是结合项目背景&#xff0c;调研比较优化的解决方案。 文件上传失败是老生常谈的问题&#xff0c;常用方案是将一个大文件切片成多个小文件&#xff0c;并行请求接口…