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?

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ść.


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

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 ...


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 .


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.


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

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 ().


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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.