all*_*tej 16 asp.net-core-mvc asp.net-core
我需要返回一个一致的响应,并为所有请求返回一个类似的结构.在之前的.NET web api中,我能够使用DelegatingHandler(MessageHandlers)实现此目的.我想要返回的对象将封装在Result元素中.所以基本上json响应将是这种结构:
例1:
{
"RequestId":"some-guid-abcd-1234",
"StatusCode":200,
"Result":
{
"Id":42,
"Todo":"Do Hello World"
}
}
Run Code Online (Sandbox Code Playgroud)
例2:
{
"RequestId":"some-guid-abcd-1235",
"StatusCode":200,
"Result":
{
[
{
"Id":42,
"Todo":"Print Hello World"
},
{
"Id":43,
"Todo":"Print Thank you"
}
]
}
}
Run Code Online (Sandbox Code Playgroud)
在.NET核心中,看起来我需要通过中间件来实现这一点.我尝试了但是我没有看到更好的方法来提取内容,就像在以前的Web API中调用HttpResponseMessage.TryGetContentValue获取内容并将其包装在全局/通用响应模型中一样.
如何在.NET核心中实现相同的目标?
all*_*tej 15
我创建了一个中间件来包装响应的一致性.为了方便注册这个中间件,我还为IApplicationBuilder创建了一个扩展方法.所以在Startup.cs中,注册中间件:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//code removed for brevity.
...
app.UseResponseWrapper();
//code removed for brevity.
...
}
Run Code Online (Sandbox Code Playgroud)
这是中间件代码:
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
namespace RegistrationWeb.Middleware
{
public class ResponseWrapper
{
private readonly RequestDelegate _next;
public ResponseWrapper(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var currentBody = context.Response.Body;
using (var memoryStream = new MemoryStream())
{
//set the current response to the memorystream.
context.Response.Body = memoryStream;
await _next(context);
//reset the body
context.Response.Body = currentBody;
memoryStream.Seek(0, SeekOrigin.Begin);
var readToEnd = new StreamReader(memoryStream).ReadToEnd();
var objResult = JsonConvert.DeserializeObject(readToEnd);
var result = CommonApiResponse.Create((HttpStatusCode)context.Response.StatusCode, objResult, null);
await context.Response.WriteAsync(JsonConvert.SerializeObject(result));
}
}
}
public static class ResponseWrapperExtensions
{
public static IApplicationBuilder UseResponseWrapper(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ResponseWrapper>();
}
}
public class CommonApiResponse
{
public static CommonApiResponse Create(HttpStatusCode statusCode, object result = null, string errorMessage = null)
{
return new CommonApiResponse(statusCode, result, errorMessage);
}
public string Version => "1.2.3";
public int StatusCode { get; set; }
public string RequestId { get; }
public string ErrorMessage { get; set; }
public object Result { get; set; }
protected CommonApiResponse(HttpStatusCode statusCode, object result = null, string errorMessage = null)
{
RequestId = Guid.NewGuid().ToString();
StatusCode = (int)statusCode;
Result = result;
ErrorMessage = errorMessage;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个老问题,但也许这会对其他人有所帮助。
在 AspNetCore 2(不确定它是否适用于以前的版本)中,您可以添加一个 Custom OutputFormatter. 下面是使用内置JsonOutputFormatter.
请注意,这并没有经过彻底的测试,我不是 100% 认为更改上下文是可以的。我查看了 aspnet 源代码,这似乎无关紧要,但我可能错了。
public class CustomJsonOutputFormatter : JsonOutputFormatter
{
public CustomJsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
: base(serializerSettings, charPool)
{ }
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.OK)
{
var @object = new ApiResponse { Data = context.Object };
var newContext = new OutputFormatterWriteContext(context.HttpContext, context.WriterFactory, typeof(ApiResponse), @object);
newContext.ContentType = context.ContentType;
newContext.ContentTypeIsServerDefined = context.ContentTypeIsServerDefined;
return base.WriteResponseBodyAsync(newContext, selectedEncoding);
}
return base.WriteResponseBodyAsync(context, selectedEncoding);
}
}
Run Code Online (Sandbox Code Playgroud)
然后在你的 Startup 类中注册它
public void ConfigureServices(IServiceCollection services)
{
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
options.OutputFormatters.RemoveType<JsonOutputFormatter>();
options.OutputFormatters.Add(new WrappedJsonOutputFormatter(jsonSettings, ArrayPool<char>.Shared));
}
Run Code Online (Sandbox Code Playgroud)
我可以看到至少有两种选择来实现这一目标。
首先,如果您想将此包装器添加到项目中的所有 api 中,您可以通过在项目的startup.cs 部分中实现中间件来实现。app.Use这是通过在“配置”函数之前添加 来完成的,app.UseMvc方法如下:
app.Use(async (http, next) =>
{
//remember previous body
var currentBody = http.Response.Body;
using (var memoryStream = new MemoryStream())
{
//set the current response to the memorystream.
http.Response.Body = memoryStream;
await next();
string requestId = Guid.NewGuid().ToString();
//reset the body as it gets replace due to https://github.com/aspnet/KestrelHttpServer/issues/940
http.Response.Body = currentBody;
memoryStream.Seek(0, SeekOrigin.Begin);
//build our content wrappter.
var content = new StringBuilder();
content.AppendLine("{");
content.AppendLine(" \"RequestId\":\"" + requestId + "\",");
content.AppendLine(" \"StatusCode\":" + http.Response.StatusCode + ",");
content.AppendLine(" \"Result\":");
//add the original content.
content.AppendLine(new StreamReader(memoryStream).ReadToEnd());
content.AppendLine("}");
await http.Response.WriteAsync(content.ToString());
}
});
Run Code Online (Sandbox Code Playgroud)
您的另一个选择是拦截控制器中的调用。OnActionExecuted这可以通过覆盖控制器中的函数来完成。类似于以下内容:
public override void OnActionExecuted(ActionExecutedContext context)
{
//
// add code to update the context.Result as needed.
//
base.OnActionExecuted(context);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5851 次 |
| 最近记录: |