如何使用 MVC 的内容协商在 ASP.NET Core MVC 中间件中返回响应?

Posted

技术标签:

【中文标题】如何使用 MVC 的内容协商在 ASP.NET Core MVC 中间件中返回响应?【英文标题】:How can I return a response in ASP.NET Core MVC middleware using MVC's content negotiation? 【发布时间】:2018-01-12 14:23:22 【问题描述】:

我有一些 ASP.NET Core MVC 中间件来捕获我想从中返回响应的未处理异常。

虽然httpContext.Response.WriteAsync 很容易写一个字符串,例如使用JsonSerializer 将对象序列化为字符串,我想使用标准的序列化设置和内容协商,以便如果我将默认输出格式更改为XML 或text/xml 在我有多个输出格式化程序时发送接受标头如果我从控制器返回 ObjectResult,则会返回 XML。

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

这是我目前只写 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;
    

【问题讨论】:

【参考方案1】:

这在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; 
    

【讨论】:

知道为什么var executor = context.RequestServices.GetService&lt;IActionResultExecutor&lt;TResult&gt;&gt;(); 这段代码会失败吗?我尝试了所有变体,包括仅解析 ObjectResultExecutor。没有任何效果。但是我可以看到它在services.AddMvc()之后注册。

以上是关于如何使用 MVC 的内容协商在 ASP.NET Core MVC 中间件中返回响应?的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net MVC 与 Asp.net Web API 区别

如何使用 jquery 或 ajax 在 c#/asp.net 中为 MVC 项目更新 razor 部分视图

大型查询如何在 C#/ASP.NET MVC 4 部分中返回结果

ASP.NET MVC:如何在返回状态码 404 时提供内容? [复制]

asp.net mvc 如何实现自动提交或保留用户未登录前得信息

如何设置一个简单的ASP.NET MVC C#项目? [关闭]