您的当前位置:首页正文

dotnetCore源码扩展:自定义ActionResult

2024-11-10 来源:个人技术集锦

前言: 有时候需要在全局对结果进行包装类似如下的返回结果

{
    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类并修改

显示全文