Jak zwrócić HTTP 500 z interfejsu API sieci Web ASP.NET Core RC2?

189

Wracając do RC1, zrobiłbym to:

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

W RC2 nie ma już HttpStatusCodeResult i nie mogę znaleźć niczego, co pozwalałoby mi zwrócić 500 typów IActionResult.

Czy podejście, które teraz pytam, jest zupełnie inne? Czy już nie próbujemy złapać Controllerkodu? Czy zezwalamy po prostu, aby środowisko generowało ogólny wyjątek 500 z powrotem do programu wywołującego API? W jaki sposób mogę zobaczyć dokładny stos wyjątków?

Mickael Caruso
źródło

Odpowiedzi:

241

Z tego, co widzę, istnieją metody pomocnicze w ControllerBaseklasie. Wystarczy użyć StatusCodemetody:

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

Możesz także użyć StatusCode(int statusCode, object value)przeciążenia, które również negocjuje zawartość.

Federico Dipuma
źródło
7
w ten sposób tracimy nagłówki CORS, więc błędy są ukryte przed klientami przeglądarki. Frustrujące.
bbsimonbb
2
@bbsimonbb Błędy wewnętrzne powinny być ukryte przed klientami. Powinny one zostać zarejestrowane dla programistów.
Himalaya Garg
10
Programiści powinni mieć, tradycyjnie korzystający, uprawnienie do wyboru, jaki poziom zwracanych informacji o błędach.
bbsimonbb
179

Możesz użyć Microsoft.AspNetCore.Mvc.ControllerBase.StatusCodei Microsoft.AspNetCore.Http.StatusCodessformułować swoją odpowiedź, jeśli nie chcesz zakodować określonych numerów.

return  StatusCode(StatusCodes.Status500InternalServerError);

AKTUALIZACJA: sierpień 2019

Być może nie związane bezpośrednio z pierwotnym pytaniem, ale próbując osiągnąć ten sam wynik Microsoft Azure Functions, stwierdziłem, że muszę zbudować nowy StatusCodeResultobiekt znaleziony w Microsoft.AspNetCore.Mvc.Corezestawie. Mój kod wygląda teraz tak;

return new StatusCodeResult(StatusCodes.Status500InternalServerError);
Edward Comeau
źródło
11
Świetny, unika jakichkolwiek zakodowanych części / „magicznych liczb”. Wcześniej użyłem StatusCode ((int) HttpStatusCode.InternalServerError), ale bardziej podoba mi się twój.
aleor
1
Jedną z rzeczy, o których wtedy nie myślałem, jest to, że kod jest bardziej czytelny. Wracając do niego, wiesz, z jakim błędem związany jest numer 500, jest dokładnie tam, gdzie jest kod. Self-documenting :-)
Edward Comeau
11
Nie mogę sobie wyobrazić, aby wewnętrzny błąd serwera (500) zmienił się w najbliższym czasie.
rzuca
2
niesamowite. to również naprawdę oczyszcza moje atrybuty swagger. np .: [
ProducesResponseType
43

Jeśli potrzebujesz ciała w odpowiedzi, możesz zadzwonić

return StatusCode(StatusCodes.Status500InternalServerError, responseObject);

Zwróci to 500 z obiektem odpowiedzi ...

David McEleney
źródło
3
Jeśli nie chcesz tworzyć określonego typu obiektu odpowiedzi: return StatusCode(StatusCodes.Status500InternalServerError, new { message = "error occurred" });I oczywiście możesz dodać tak opisową wiadomość, jak chcesz, a także inne elementy.
Mike Taverne
18

Lepszym sposobem radzenia sobie z tym już teraz (1,1) jest to zrobić w Startup.cs„s Configure():

app.UseExceptionHandler("/Error");

Spowoduje to wykonanie trasy dla /Error. Pozwoli ci to uniknąć dodawania bloków „try-catch” do każdej pisanej akcji.

Oczywiście musisz dodać ErrorController podobny do tego:

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

Więcej informacji tutaj .


Jeśli chcesz uzyskać rzeczywiste dane wyjątku, możesz dodać to do powyższego Get()tuż przed returninstrukcją.

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

Powyżej fragmentu wzięty z bloga Scotta Saubera .

Gldraphael
źródło
to jest niesamowite, ale jak mogę zarejestrować zgłoszony wyjątek?
redwards510,
@ redwards510 Oto jak to zrobić: scottsauber.com/2017/04/03/ ... Zaktualizuję moją odpowiedź, aby ją odzwierciedlić, ponieważ jest to bardzo częsty przypadek użycia 😊
gldraphael
@gldraphael Obecnie używamy Core 2.1. Blog Scotta jest świetny, ale jestem ciekawy, czy używanie IExceptionHandlerPathFeature jest obecnie zalecaną najlepszą praktyką. Być może tworzenie niestandardowego oprogramowania pośredniego jest lepsze?
Pavel
@Pavel używamy ExceptionHandleroprogramowania pośredniego tutaj. Możesz oczywiście rzucić własną lub przedłużyć ją według własnego uznania. Oto link do źródeł . EDYCJA: Zobacz ten wiersz dla IExceptionHandlerPathFeature .
gldraphael
15
return StatusCode((int)HttpStatusCode.InternalServerError, e);

Powinien być używany w kontekstach innych niż ASP.NET (zobacz inne odpowiedzi dla ASP.NET Core).

HttpStatusCodejest wyliczeniem w System.Net.

Shimmy Weitzhandler
źródło
11

Co powiesz na utworzenie niestandardowej klasy ObjectResult, która reprezentuje wewnętrzny błąd serwera, taki jak ten OkObjectResult? Możesz umieścić prostą metodę we własnej klasie bazowej, aby łatwo wygenerować InternalServerError i zwrócić ją tak jak ty Ok()lub 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
źródło
6

Jeśli chcesz zwrócić odpowiedź JSON w MVC .Net Core Możesz również użyć:

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

Zwróci zarówno wynik JSON, jak i HTTPStatus. Używam go do zwracania wyników do jQuery.ajax ().

Tekin
źródło
1
Musiałem użyć, return new JsonResult ...ale poza tym działało świetnie.
Mike Taverne
5

W przypadku aspnetcore-3.1 można również użyć Problem()jak poniżej;

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);
}
Teoman Shipahi
źródło