如何使用 MVC 的内容协商 return 在 ASP.NET 核心 MVC 中间件中进行响应?

How can I return a response in ASP.NET Core MVC middleware using MVC's content negotiation?

我有一些 ASP.NET 核心 MVC 中间件来捕获未处理的异常,我想 return 从中得到响应。

虽然 httpContext.Response.WriteAsync 很容易写一个字符串,例如使用 JsonSerializer 将 object 序列化为字符串,我想使用标准序列化设置和内容协商,这样如果我将默认输出格式更改为 XML 或 text/xml 接受 header 当我配置了多个输出格式化程序时发送 XML 是 returned,就像我 return 来自控制器的 ObjectResult 一样.

有谁知道如何在中间件中实现这一点?

到目前为止,这是我的代码,它只写 JSON:

public class UnhandledExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IOutputFormatter _outputFormatter;
    private readonly IHttpResponseStreamWriterFactory _streamWriterFactory;

    public UnhandledExceptionMiddleware(RequestDelegate next, JsonOutputFormatter outputFormatter, IHttpResponseStreamWriterFactory streamWriterFactory)
    {
        _next = next;
        _outputFormatter = outputFormatter;
        _streamWriterFactory = streamWriterFactory;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var error = new ErrorResultModel("Internal Server Error", exception.Message, exception.StackTrace);
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        await _outputFormatter.WriteAsync(new OutputFormatterWriteContext(context, _streamWriterFactory.CreateWriter, typeof(ErrorResultModel), error));
    }
}

其中 ErrorResultModel 定义为:

public class ErrorResultModel
{
    public string ResultMessage { get; };
    public string ExceptionMessage { get; };
    public string ExceptionStackTrace { get; };

    public ErrorResultModel(string resultMessage, string exceptionMessage, string exceptionStackTrace)
    {
        ResultMessage = resultMessage;
        ExceptionMessage = exceptionMessage;
        ExceptionStackTrace = exceptionStackTrace;
    }
}

这在 ASP.NET Core 2.0 MVC 中是不可能的。

这将是可能的in 2.1:

    public static class HttpContextExtensions
    {
        private static readonly RouteData EmptyRouteData = new RouteData();
    
        private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();
    
        public static Task WriteResultAsync<TResult>(this HttpContext context, TResult result)
            where TResult : IActionResult
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
    
            var executor = context.RequestServices.GetService<IActionResultExecutor<TResult>>();
    
            if (executor == null)
            {
                throw new InvalidOperationException($"No result executor for '{typeof(TResult).FullName}' has been registered.");
            }
    
            var routeData = context.GetRouteData() ?? EmptyRouteData;
    
            var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
    
            return executor.ExecuteAsync(actionContext, result);
        }
    }

    public class Program : StartupBase
    {
        public static Task Main(string[] args)
        {
            return BuildWebHost(args).RunAsync();
        }
    
        public static IWebHost BuildWebHost(string[] args)
        {
            return new WebHostBuilder().UseStartup<Program>().UseKestrel().Build();
        }
    
        public override void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore().AddJsonFormatters();
        }
    
        public override void Configure(IApplicationBuilder app)
        {
            app.Use((ctx, next) =>
            {
                var model = new Person("Krisian", "Hellang");
    
                var result = new ObjectResult(model);
    
                return ctx.WriteResultAsync(result);
            });
        }
    }
    
    public class Person
    {
        public Person(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
    
        public string FirstName { get; }
    
        public string LastName { get; }
    }