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

时间:2018-01-12 14:23:22

标签: .net-core asp.net-core-mvc middleware asp.net-core-2.0

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

虽然只需httpContext.Response.WriteAsync来编写字符串,例如使用JsonSerializer将对象序列化为字符串,我想使用标准的序列化设置和内容协商,这样如果我将默认输出格式更改为XML,或者当我发送text/xml接受标头时配置了多个输出格式化程序,然后返回XML,就像从控制器返回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;
    }
}

1 个答案:

答案 0 :(得分:8)

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; }
}