前言: 有时候需要在全局对结果进行包装类似如下的返回结果
{
code:200,
result:{}
}
而.Net自带的ActionResult
自带一些不相关的属性,比如下面这样的,
为了解决上面的问题,继承ActionResult
类并重载 ExecuteResultAsync
方法
public class MyContentResult : ActionResult
{
public object Content { get; set; }
public override Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<MyContentResult>>();
return executor.ExecuteAsync(context, this);
}
}
每个ActionResult
都需要一个IActionResultExecutor<T>
的实现类来把数据写入响应流中,泛型T
需要和自定义ActionResult
对应
public class MyContentResultExecutor : IActionResultExecutor<MyContentResult>
{
private const string DefaultContentType = "text/plain; charset=utf-8";
private readonly IHttpResponseStreamWriterFactory _httpResponseStreamWriterFactory;
public MyContentResultExecutor(IHttpResponseStreamWriterFactory httpResponseStreamWriterFactory)
{
_httpResponseStreamWriterFactory = httpResponseStreamWriterFactory;
}
public async Task ExecuteAsync(ActionContext context, MyContentResult result)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
var response = context.HttpContext.Response;
ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
null,
response.ContentType,
DefaultContentType,
out var resolvedContentType,
out var resolvedContentTypeEncoding);
response.ContentType = resolvedContentType;
var defaultContentTypeEncoding = MediaType.GetEncoding(response.ContentType);
if (result.Content != null)
{
string content = JsonConvert.SerializeObject(result.Content);
response.ContentLength = resolvedContentTypeEncoding.GetByteCount(content);
using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
await textWriter.WriteAsync(content);
await textWriter.FlushAsync();
}
}
}
}
public class MyJsonResult : ActionResult
{
public object Value { get; set; }
public override Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<MyJsonResult>>();
return executor.ExecuteAsync(context, this);
}
}
public class MyJsonResultExecutor : IActionResultExecutor<MyJsonResult>
{
private static readonly string DefaultContentType = new MediaTypeHeaderValue("application/json")
{
Encoding = Encoding.UTF8
}.ToString();
private readonly IHttpResponseStreamWriterFactory _writerFactory;
private readonly MvcJsonOptions _options;
private readonly IArrayPool<char> _charPool;
public MyJsonResultExecutor(IHttpResponseStreamWriterFactory writerFactory, IOptions<MvcJsonOptions> options, ArrayPool<char> charPool)
{
if (writerFactory == null)
{
throw new ArgumentNullException(nameof(writerFactory));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (charPool == null)
{
throw new ArgumentNullException(nameof(charPool));
}
_writerFactory = writerFactory;
_options = options.Value;
_charPool = new JsonArrayPool<char>(charPool);
}
public async Task ExecuteAsync(ActionContext context, MyJsonResult result)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
var response = context.HttpContext.Response;
ResponseContentTypeHelper.ResolveContentTypeAndEncoding(
null,
response.ContentType,
DefaultContentType,
out var resolvedContentType,
out var resolvedContentTypeEncoding);
response.ContentType = resolvedContentType;
var serializerSettings = _options.SerializerSettings;
using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
using (var jsonWriter = new JsonTextWriter(writer))
{
jsonWriter.ArrayPool = _charPool;
jsonWriter.CloseOutput = false;
jsonWriter.AutoCompleteOnClose = false;
var jsonSerializer = JsonSerializer.Create(serializerSettings);
jsonSerializer.Serialize(jsonWriter, result.Value);
}
await writer.FlushAsync();
}
}
}
需要在Starup类中注册类型,否则运行会抛出IActionResultExecutor<MyContentResult>
接口未注册的异常
services.TryAddSingleton<IActionResultExecutor<MyContentResult>, MyContentResultExecutor>();
services.TryAddSingleton<IActionResultExecutor<MyJsonResult>, MyJsonResultExecutor>();
OK,我们试用下吧:在MyResultFilterAttribute
的重载方法OnResultExecuting
中对所有Action
方法返回值包装一下
结果符合预期:
result
中有content
属性,可以根据需要修改自定义ActionResult
类并修改