Comment renvoyer HTTP 500 à partir de l'API Web ASP.NET Core RC2?

189

De retour dans RC1, je ferais ceci:

[HttpPost]
public IActionResult Post([FromBody]string something)
{    
    try{
        // ...
    }
    catch(Exception e)
    {
         return new HttpStatusCodeResult((int)HttpStatusCode.InternalServerError);
    }
}

Dans RC2, il n'y a plus HttpStatusCodeResult, et il n'y a rien que je puisse trouver qui me permette de renvoyer un type 500 de IActionResult.

L'approche est-elle maintenant entièrement différente de ce que je demande? N'essayons-nous plus d'attraper dans le Controllercode? Laissons-nous simplement le framework renvoyer une exception générique 500 à l'appelant d'API? Pour le développement, comment puis-je voir la pile d'exceptions exacte?

Mickael Caruso
la source

Réponses:

242

D'après ce que je peux voir, il existe des méthodes d'assistance à l'intérieur de la ControllerBaseclasse. Utilisez simplement la StatusCodeméthode:

[HttpPost]
public IActionResult Post([FromBody] string something)
{    
    //...
    try
    {
        DoSomething();
    }
    catch(Exception e)
    {
         LogException(e);
         return StatusCode(500);
    }
}

Vous pouvez également utiliser la StatusCode(int statusCode, object value)surcharge qui négocie également le contenu.

Federico Dipuma
la source
7
ce faisant, nous perdons les en-têtes CORS, donc les erreurs sont masquées aux clients du navigateur. V frustrant.
bbsimonbb
2
@bbsimonbb Les erreurs internes doivent être cachées aux clients. Ils doivent être enregistrés pour les développeurs.
Himalaya Garg
10
Les développeurs devraient avoir, traditionnellement, la prérogative de choisir le niveau d'information d'erreur renvoyé.
bbsimonbb
179

Vous pouvez utiliser Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodeet Microsoft.AspNetCore.Http.StatusCodespour former votre réponse, si vous ne souhaitez pas coder en dur des numéros spécifiques.

return  StatusCode(StatusCodes.Status500InternalServerError);

MISE À JOUR: août 2019

Peut-être pas directement lié à la question d'origine, mais en essayant d'obtenir le même résultat avec, Microsoft Azure Functionsj'ai constaté que je devais construire un nouvel StatusCodeResultobjet trouvé dans l' Microsoft.AspNetCore.Mvc.Coreassemblage. Mon code ressemble maintenant à ceci;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);
Edward Comeau
la source
11
Excellent, évite toutes les parties codées en dur / "nombres magiques". J'ai déjà utilisé StatusCode ((int) HttpStatusCode.InternalServerError) mais j'aime mieux le vôtre.
aleor
1
Une chose que je n'ai pas considérée à l'époque est que cela rend le code plus lisible, en y revenant, vous savez à quoi se rapporte l'erreur numéro 500, c'est juste là dans le code. Self documenting :-)
Edward Comeau
11
Je ne peux pas imaginer que l'erreur de serveur interne (500) change de sitôt.
lance
2
impressionnant. cela nettoie aussi vraiment mes attributs swagger. ex: [ProducesResponseType (StatusCodes.Status500InternalServerError)]
redwards510
43

Si vous avez besoin d'un corps dans votre réponse, vous pouvez appeler

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Cela renverra un 500 avec l'objet de réponse ...

David McEleney
la source
3
Si vous ne souhaitez pas créer un type d'objet de réponse spécifique: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });Et bien sûr, vous pouvez ajouter un message aussi descriptif que vous le souhaitez, ainsi que d'autres éléments.
Mike Taverne
18

Une meilleure façon de gérer cela à partir de maintenant (1.1) est de le faire dans Startup.cs's Configure():

app.UseExceptionHandler("/Error");

Cela exécutera la route pour /Error . Cela vous évitera d'ajouter des blocs try-catch à chaque action que vous écrivez.

Bien sûr, vous devrez ajouter un ErrorController similaire à celui-ci:

[Route("[controller]")]
public class ErrorController : Controller
{
    [Route("")]
    [AllowAnonymous]
    public IActionResult Get()
    {
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}

Plus d'informations ici .


Si vous souhaitez obtenir les données d'exception réelles, vous pouvez l'ajouter à ce qui précède Get()juste avant l' returninstruction.

// Get the details of the exception that occurred
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

if (exceptionFeature != null)
{
    // Get which route the exception occurred at
    string routeWhereExceptionOccurred = exceptionFeature.Path;

    // Get the exception that occurred
    Exception exceptionThatOccurred = exceptionFeature.Error;

    // TODO: Do something with the exception
    // Log it with Serilog?
    // Send an e-mail, text, fax, or carrier pidgeon?  Maybe all of the above?
    // Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500
}

Ci-dessus, extrait du blog de Scott Sauber .

gldraphael
la source
c'est génial, mais comment puis-je enregistrer l'exception qui a été levée?
redwards510
@ redwards510 Voici comment procéder: scottsauber.com/2017/04/03/ ... Je vais mettre à jour ma réponse pour la refléter, car c'est un cas d'utilisation très courant 😊
gldraphael
@gldraphael Nous utilisons actuellement Core 2.1. Le blog de Scott est génial, mais je suis curieux de savoir si l'utilisation de IExceptionHandlerPathFeature est actuellement les meilleures pratiques recommandées. Peut-être que créer un middleware personnalisé est-il préférable?
Pavel
@Pavel, nous utilisons le ExceptionHandlermiddleware ici. Vous pouvez, bien entendu, rouler le vôtre ou l'étendre comme bon vous semble. Voici le lien vers les sources . EDIT: Voir cette ligne pour IExceptionHandlerPathFeature .
gldraphael
15
return StatusCode((int)HttpStatusCode.InternalServerError, e);

Doit être utilisé dans des contextes non-ASP.NET (voir les autres réponses pour ASP.NET Core).

HttpStatusCodeest une énumération dans System.Net.

Shimmy Weitzhandler
la source
12

Que diriez-vous de créer une classe ObjectResult personnalisée qui représente une erreur de serveur interne comme celle pour OkObjectResult? Vous pouvez mettre une méthode simple dans votre propre classe de base afin de pouvoir facilement générer InternalServerError et la renvoyer comme vous le faites Ok()ou BadRequest().

[Route("api/[controller]")]
[ApiController]
public class MyController : MyControllerBase
{
    [HttpGet]
    [Route("{key}")]
    public IActionResult Get(int key)
    {
        try
        {
            //do something that fails
        }
        catch (Exception e)
        {
            LogException(e);
            return InternalServerError();
        }
    }
}

public class MyControllerBase : ControllerBase
{
    public InternalServerErrorObjectResult InternalServerError()
    {
        return new InternalServerErrorObjectResult();
    }

    public InternalServerErrorObjectResult InternalServerError(object value)
    {
        return new InternalServerErrorObjectResult(value);
    }
}

public class InternalServerErrorObjectResult : ObjectResult
{
    public InternalServerErrorObjectResult(object value) : base(value)
    {
        StatusCode = StatusCodes.Status500InternalServerError;
    }

    public InternalServerErrorObjectResult() : this(null)
    {
        StatusCode = StatusCodes.Status500InternalServerError;
    }
}
Airn5475
la source
6

Lorsque vous souhaitez renvoyer une réponse JSON dans MVC .Net Core, vous pouvez également utiliser:

Response.StatusCode = (int)HttpStatusCode.InternalServerError;//Equals to HTTPResponse 500
return Json(new { responseText = "my error" });

Cela renverra à la fois le résultat JSON et HTTPStatus. Je l'utilise pour renvoyer les résultats à jQuery.ajax ().

Tekin
la source
1
J'ai dû utiliser return new JsonResult ...mais autrement fonctionnait très bien.
Mike Taverne
5

Pour aspnetcore-3.1, vous pouvez également utiliser Problem()comme ci-dessous;

https://docs.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-3.1

 [Route("/error-local-development")]
public IActionResult ErrorLocalDevelopment(
    [FromServices] IWebHostEnvironment webHostEnvironment)
{
    if (webHostEnvironment.EnvironmentName != "Development")
    {
        throw new InvalidOperationException(
            "This shouldn't be invoked in non-development environments.");
    }

    var context = HttpContext.Features.Get<IExceptionHandlerFeature>();

    return Problem(
        detail: context.Error.StackTrace,
        title: context.Error.Message);
}
Shipahi Teoman
la source