web-dev-qa-db-fra.com

Equivalent ASP.NET Core de l'exception HttpException d'ASP.NET MVC 5

Dans ASP.NET MVC 5, vous pouvez générer une HttpException avec un code HTTP, ce qui définirait la réponse de la manière suivante:

throw new HttpException((int)HttpStatusCode.BadRequest, "Bad Request.");

HttpException n'existe pas dans ASP.NET Core. Quel est le code équivalent?

52

J'ai implémenté mon propre middleware HttpException et support qui capture tous les HttpException et les transforme en réponse d'erreur correspondante. Un extrait court peut être vu ci-dessous:

Vous pouvez utiliser le package Boilerplate.AspNetCore Nuget ou afficher le code source complet dans le projet ASP.NET MVC Boilerplate GitHub ou créer un nouveau projet à l'aide du - ASP.NET MVC Boilerplate modèle de projet qui vous permet d'activer éventuellement cette fonctionnalité lors de la création d'un nouveau projet (cochez simplement la case pour l'activer).

// Usage example in Startup.cs
public void Configure(IApplicationBuilder application)
{
    application.UseIISPlatformHandler();

    application.UseStatusCodePagesWithReExecute("/error/{0}");
    application.UseHttpException();

    application.UseMvc();
}

public static class ApplicationBuilderExtensions
{
    public static IApplicationBuilder UseHttpException(this IApplicationBuilder application)
    {
        return application.UseMiddleware<HttpExceptionMiddleware>();
    }
}

internal class HttpExceptionMiddleware
{
    private readonly RequestDelegate next;

    public HttpExceptionMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await this.next.Invoke(context);
        }
        catch (HttpException httpException)
        {
            context.Response.StatusCode = httpException.StatusCode;
            var responseFeature = context.Features.Get<IHttpResponseFeature>();
            responseFeature.ReasonPhrase = httpException.Message;
        }
    }
}

public class HttpException : Exception
{
    private readonly int httpStatusCode;

    public HttpException(int httpStatusCode)
    {
        this.httpStatusCode = httpStatusCode;
    }

    public HttpException(HttpStatusCode httpStatusCode)
    {
        this.httpStatusCode = (int)httpStatusCode;
    }

    public HttpException(int httpStatusCode, string message) : base(message)
    {
        this.httpStatusCode = httpStatusCode;
    }

    public HttpException(HttpStatusCode httpStatusCode, string message) : base(message)
    {
        this.httpStatusCode = (int)httpStatusCode;
    }

    public HttpException(int httpStatusCode, string message, Exception inner) : base(message, inner)
    {
        this.httpStatusCode = httpStatusCode;
    }

    public HttpException(HttpStatusCode httpStatusCode, string message, Exception inner) : base(message, inner)
    {
        this.httpStatusCode = (int)httpStatusCode;
    }

    public int StatusCode { get { return this.httpStatusCode; } }
}
38

Après un brève discussion avec @davidfowl , il semble qu'ASP.NET 5 n'ait pas cette notion de HttpException ou HttpResponseException qui passe "magiquement" aux messages de réponse.

Ce que vous pouvez faire est raccordez-vous au pipeline ASP.NET 5 via MiddleWare et créez-en un qui gère les exceptions pour vous.

Voici un exemple tiré du code source de leur middleware de gestionnaire d'erreurs qui définira le code d'état de la réponse sur 500 en cas d'exception plus loin dans le pipeline:

public class ErrorHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ErrorHandlerOptions _options;
    private readonly ILogger _logger;

    public ErrorHandlerMiddleware(RequestDelegate next, 
                                  ILoggerFactory loggerFactory,
                                  ErrorHandlerOptions options)
    {
        _next = next;
        _options = options;
        _logger = loggerFactory.CreateLogger<ErrorHandlerMiddleware>();
        if (_options.ErrorHandler == null)
        {
            _options.ErrorHandler = _next;
        }
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError("An unhandled exception has occurred: " + ex.Message, ex);

            if (context.Response.HasStarted)
            {
                _logger.LogWarning("The response has already started, 
                                    the error handler will not be executed.");
                throw;
            }

            PathString originalPath = context.Request.Path;
            if (_options.ErrorHandlingPath.HasValue)
            {
                context.Request.Path = _options.ErrorHandlingPath;
            }
            try
            {
                var errorHandlerFeature = new ErrorHandlerFeature()
                {
                    Error = ex,
                };
                context.SetFeature<IErrorHandlerFeature>(errorHandlerFeature);
                context.Response.StatusCode = 500;
                context.Response.Headers.Clear();

                await _options.ErrorHandler(context);
                return;
            }
            catch (Exception ex2)
            {
                _logger.LogError("An exception was thrown attempting
                                  to execute the error handler.", ex2);
            }
            finally
            {
                context.Request.Path = originalPath;
            }

            throw; // Re-throw the original if we couldn't handle it
        }
    }
}

Et vous devez l'enregistrer avec StartUp.cs:

public class Startup
{
    public void Configure(IApplicationBuilder app, 
                          IHostingEnvironment env, 
                          ILoggerFactory loggerfactory)
    {
       app.UseMiddleWare<ExceptionHandlerMiddleware>();
    }
}
23
Yuval Itzchakov

Sinon, si vous souhaitez simplement renvoyer un code d’état arbitraire et que l’approche basée sur les exceptions ne vous concerne pas, vous pouvez utiliser

return new HttpStatusCodeResult(400);

Mise à jour: à partir de .NET Core RC 2, le préfixe Http est supprimé. C'est maintenant:

return new StatusCodeResult(400);
12
Jeremy

La classe de base Microsoft.AspNet.Mvc.Controller Expose une surcharge HttpBadRequest(string) qui prend un message d'erreur à renvoyer au client. Donc, depuis une action de contrôleur, vous pouvez appeler:

return HttpBadRequest("Bad Request.");

En fin de compte, mon nez dit que toutes les méthodes privées appelées depuis une action de contrôleur doivent soit être totalement conscientes du contexte http et retourner un IActionResult, soit effectuer une autre petite tâche complètement isolée du fait qu'elle se trouve dans un pipeline http. . Ceci est mon opinion personnelle, mais une classe qui exécute une certaine logique métier ne doit pas renvoyer de codes d’état HTTP, mais doit générer ses propres exceptions qui peuvent être interceptées et traduites au niveau du contrôleur/action.

8
lc.