带有消息的自定义错误代码页 [英] Custom error code pages with message

查看:106
本文介绍了带有消息的自定义错误代码页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个自定义错误代码页,以显示在.NET Core MVC 1.1应用程序中传递给它的消息.我在Startup.cs类文件中设置了自定义错误代码页支持,然后在执行public IActionResult Example1 => NotFound("Some custom error message")的控制器中创建了一个简单视图.我希望此消息被推送到控制器,但事实并非如此.不带任何参数调用NotFound()会遇到错误控制器,但是一旦我通过消息传递,就永远不会使用该控制器,并且会显示一条简单的文本消息.

I am trying to create a custom error code page that displays a message I pass to it in my .NET Core MVC 1.1 application. I setup custom error code pages support in the Startup.cs class file and then created a simple view in a controller that does public IActionResult Example1 => NotFound("Some custom error message"). I expected this message to be pushed to the controller however this is not the case. Calling NotFound() without any parameters hits the error controller but as soon as I pass a message through, the controller is never used and a simple text message is displayed.

我本可以发誓过去曾经使用经典的.NET MVC来做到这一点,但是已经有一段时间了.

I could have sworn I used to do this in the past with classic .NET MVC but it has been awhile.

我如何拥有显示正确错误的自定义错误代码页.对于我期望JSON响应(API动作等)的情况,我还需要控制器具有在错误期间返回标准文本或JSON响应的能力.我假设有一种方法可以使用属性来完成此操作,但是我还没有找到一种方法来执行上述两项任务.

How can I have custom error code pages that display the proper error. I also need the ability in a controller to return the standard text or JSON response during the error for cases when I expect a JSON response (API actions and such). I am assuming there is a way to do this with a attribute but I have yet to find a way to do either of these tasks.

推荐答案

您可以做的事情与

What you could do is something similar to how the StatusCodePages middleware works. That middleware allows a pipeline re-execution model, to allow handling status code errors through the normal MVC pipeline. So when you return a non-successful status code from MVC, the middleware detects that and then re-executes the whole pipeline for a status code error route. That way, you are able to fully design status code errors. But as Chris Pratt already mentioned, those status codes are typically limited to just their code. There is not really a way to add additional details to it.

但是我们可以做的是在该重新执行模型的基础上创建我们自己的错误处理实现.为此,我们创建一个CustomErrorResponseMiddleware,它基本上检查CustomErrorResponseException异常,然后为我们的错误处理程序重新执行中间件管道.

But what we could do is create our own error handling implementation on top of that re-execution model. For that, we create a CustomErrorResponseMiddleware which basically checks for CustomErrorResponseException exceptions and then re-executes the middleware pipeline for our error handler.

// Custom exceptions that can be thrown within the middleware
public class CustomErrorResponseException : Exception
{
    public int StatusCode { get; set; }
    public CustomErrorResponseException(string message, int statusCode)
        : base(message)
    {
        StatusCode = statusCode;
    }
}

public class NotFoundResponseException : CustomErrorResponseException
{
    public NotFoundResponseException(string message)
        : base(message, 404)
    { }
}

// Custom context feature, to store information from the exception
public interface ICustomErrorResponseFeature
{
    int StatusCode { get; set; }
    string StatusMessage { get; set; }
}
public class CustomErrorResponseFeature : ICustomErrorResponseFeature
{
    public int StatusCode { get; set; }
    public string StatusMessage { get; set; }
}

// Middleware implementation
public class CustomErrorResponseMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _requestPath;

    public CustomErrorResponseMiddleware(RequestDelegate next, string requestPath)
    {
        _next = next;
        _requestPath = requestPath;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            // run the pipeline normally
            await _next(context);
        }
        catch (CustomErrorResponseException ex)
        {
            // store error information to be retrieved in the custom handler
            context.Features.Set<ICustomErrorResponseFeature>(new CustomErrorResponseFeature
            {
                StatusCode = ex.StatusCode,
                StatusMessage = ex.Message,
            });

            // backup original request data
            var originalPath = context.Request.Path;
            var originalQueryString = context.Request.QueryString;

            // set new request data for re-execution
            context.Request.Path = _requestPath;
            context.Request.QueryString = QueryString.Empty;

            try
            {
                // re-execute middleware pipeline
                await _next(context);
            }
            finally
            {
                // restore original request data
                context.Request.Path = originalPath;
                context.Request.QueryString = originalQueryString;
            }
        }
    }
}

现在,我们需要做的就是将其连接起来.因此,我们将中间件添加到Startup.Configure的开头附近:

Now, all we need to do is hook that up. So we add the middleware within our Startup.Configure, somewhere near the beginning:

app.UseMiddleware<CustomErrorResponseMiddleware>("/custom-error-response");

/custom-error-response是当请求自定义响应时我们正在重新执行的路由.这可以是正常的MVC控制器操作:

The /custom-error-response is the route that we are re-executing when a custom response is being requested. This can be a normal MVC controller action:

[Route("/custom-error-response")]
public IActionResult CustomErrorResponse()
{
    var customErrorResponseFeature = HttpContext.Features.Get<ICustomErrorResponseFeature>();

    var view = View(customErrorResponseFeature);
    view.StatusCode = customErrorResponseFeature.StatusCode;
    return view;
}

由于它使用MVC,因此还需要一个视图:

Since this uses MVC, this also needs a view:

@model ICustomErrorResponseFeature
@{
    ViewData["Title"] = "Error";
}

<p>There was an error with your request:</p>
<p>@Model.StatusMessage</p>

基本上就是这些.现在,我们可以从MVC操作中抛出自定义错误响应异常来触发此操作:

And that’s basically all. Now, we can just throw our custom error response exceptions from our MVC actions to trigger this:

// generate a 404
throw new NotFoundResponseException("This item could not be found");

// or completely custom
throw new CustomErrorResponseException("This did not work", 400);

当然,我们也可以进一步扩展它,但这应该是基本思想.

Of course, we could also expand this further, but that should be the basic idea.

如果您已经在使用StatusCodePages中间件,当您已经在StatusCodePages中间件中完全使用了这种自定义重新执行功能时,您可能会认为它是否真的必要.嗯,事实并非如此.我们也可以直接对此进行扩展.

If you are already using the StatusCodePages middleware, you might think whether all this custom re-execution is really necessary, when you already have exactly that in the StatusCodePages middleware. And well, it is not. We can also just expand on that directly.

为此,我们将仅添加上下文功能,可以在正常执行过程中的任何时候设置上下文功能.然后,我们只返回一个状态码,并让StatusCodePages中间件运行.然后,在其处理程序内部,我们可以查找我们的功能并使用那里的信息来扩展状态代码错误页面:

For that, we will just add the context features, which we can set at any point during the normal execution. Then, we just return a status code, and let the StatusCodePages middleware run. Inside its handler, we can then look for our feature and use the information there to expand the status code error page:

// Custom context feature
public interface IStatusCodePagesInfoFeature
{
    string StatusMessage { get; set; }
}
public class StatusCodePagesInfoFeature : IStatusCodePagesInfoFeature
{
    public string StatusMessage { get; set; }
}

// registration of the StatusCodePages middleware inside Startup.Configure
app.UseStatusCodePagesWithReExecute("/error/{0}");

// and the MVC action for that URL
[Route("/error/{code}")]
public IActionResult StatusCode(int code)
{
    var statusCodePagesInfoFeature = HttpContext.Features.Get<IStatusCodePagesInfoFeature>();

    return View(model: statusCodePagesInfoFeature?.StatusMessage);
}

除了正常的控制器操作外,我们还可以在返回状态代码之前设置该功能:

Inside of the normal controller actions, we can set that feature before returning a status code:

HttpContext.Features.Set<IStatusCodePagesInfoFeature>(new StatusCodePagesInfoFeature
{
    StatusMessage = "This item could not be found"
});
return NotFound();


太糟糕了,您无法在中间件类中拦截NotFound,Unauthorized等响应.

It is too bad you cannot intercept NotFound, Unauthorized, etc. responses in a middleware class.

好的,选择三!您可以完全拦截这些响应,而不仅仅是在中间件内部,因为它们是MVC结果,不会离开MVC管道.因此,您必须在MVC筛选器管道中拦截它们.但是我们绝对可以运行一个筛选器,例如结果过滤器,用于修改结果.

Okay, option three! You can totally intercept those responses, just not inside of middleware, since these are MVC results and will not leave the MVC pipeline. So you have to intercept them within the MVC filter pipeline. But we could absolutely run a filter, for example a result filter, that modifies the result.

问题是我们仍然需要一种传递信息的方法.我们可以再次使用上下文功能,但是也可以使用MVC对象结果.因此,我们可以在MVC操作中执行以下操作:

The problem is that we still need a way to pass the information on. We could use a context feature again, but we can also use the MVC object results. So the idea is that we can just do the following in the MVC actions:

return NotFound("The item was not found");

因此通常,该字符串将是纯文本响应.但是,在执行结果和生成响应之前,我们可以运行结果过滤器进行修改,然后返回视图结果.

So usually, that string would be the plain text response. But before the result is being executed and the response is being generated, we can run a result filter to modify this and return a view result instead.

public class StatusCodeResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        // retrieve a typed controller, so we can reuse its data
        if (context.Controller is Controller controller)
        {
            // intercept the NotFoundObjectResult
            if (context.Result is NotFoundObjectResult notFoundResult)
            {
                // set the model, or other view data
                controller.ViewData.Model = notFoundResult.Value;

                // replace the result by a view result
                context.Result = new ViewResult()
                {
                    StatusCode = 404,
                    ViewName = "Views/Errors/NotFound.cshtml",
                    ViewData = controller.ViewData,
                    TempData = controller.TempData,
                };
            }
            // intercept other results here…
        }

        await next();
    }
}

您需要的只是现在在Views/Errors/NotFound.cshtml上的视图,一旦注册了过滤器,一切都会神奇地起作用.

All you need is a view at Views/Errors/NotFound.cshtml now and everything will magically work once you have the filter registered.

您可以通过将[TypeFilter(typeof(StatusCodeResultFilter))]属性添加到控制器或单个操作来注册过滤器,也可以

You can either register the filter by adding a [TypeFilter(typeof(StatusCodeResultFilter))] attribute to the controller or individual actions, or you can register it globally.

这篇关于带有消息的自定义错误代码页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆