Jak zwrócić NotFound () IHttpActionResult z Komunikatem o błędzie lub wyjątkiem?

Zwracam NotFound IHttpActionResult, gdy coś nie znajduje się w mojej akcji GET WebAPI. Wraz z tą odpowiedzią chcę wysłać wiadomość niestandardową i/lub wiadomość wyjątku (jeśli taka istnieje). Aktualna metoda ApiController ' s NotFound() nie zapewnia przeciążenia, aby przekazać wiadomość.

Czy można to jakoś zrobić? albo będę musiał napisać własny custom IHttpActionResult?
Author: abatishchev, 2013-11-22

10 answers

Musisz napisać własny wynik akcji, jeśli chcesz dostosować kształt wiadomości odpowiedzi.

Chcieliśmy po wyjęciu z pudełka dostarczyć najczęstsze kształty wiadomości odpowiedzi dla takich rzeczy, jak proste puste 404, ale chcieliśmy również, aby te wyniki były tak proste, jak to możliwe; jedną z głównych zalet korzystania z wyników akcji jest to, że znacznie ułatwia to test jednostkowy. Im więcej właściwości dodamy do wyników akcji, tym więcej rzeczy należy wziąć pod uwagę w teście jednostkowym aby upewnić się, że metoda działania robi to, czego można się spodziewać.

Często chcę mieć możliwość dostarczania niestandardowych wiadomości, więc nie krępuj się zarejestrować błąd, abyśmy rozważyli wsparcie tej akcji w przyszłym wydaniu: https://aspnetwebstack.codeplex.com/workitem/list/advanced

Jedną z fajnych rzeczy w wynikach akcji jest to, że zawsze możesz napisać swój własny dość łatwo, jeśli chcesz zrobić coś nieco innego. Oto jak możesz to zrobić w Twoim przypadku (zakładając, że chcesz komunikat o błędzie w text / plain; jeśli chcesz JSON, zrobisz coś nieco innego z treścią):

public class NotFoundTextPlainActionResult : IHttpActionResult
{
    public NotFoundTextPlainActionResult(string message, HttpRequestMessage request)
    {
        if (message == null)
        {
            throw new ArgumentNullException("message");
        }

        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        Message = message;
        Request = request;
    }

    public string Message { get; private set; }

    public HttpRequestMessage Request { get; private set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute());
    }

    public HttpResponseMessage Execute()
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound);
        response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
        response.RequestMessage = Request;
        return response;
    }
}

public static class ApiControllerExtensions
{
    public static NotFoundTextPlainActionResult NotFound(this ApiController controller, string message)
    {
        return new NotFoundTextPlainActionResult(message, controller.Request);
    }
}

Następnie, w swojej metodzie działania, możesz po prostu zrobić coś takiego:

public class TestController : ApiController
{
    public IHttpActionResult Get()
    {
        return this.NotFound("These are not the droids you're looking for.");
    }
}

Jeśli użyłeś niestandardowej klasy bazowej kontrolera (zamiast bezpośrednio dziedziczyć z ApiController), możesz również wyeliminować "this."część (która jest niestety wymagana przy wywołaniu metody rozszerzenia):

public class CustomApiController : ApiController
{
    protected NotFoundTextPlainActionResult NotFound(string message)
    {
        return new NotFoundTextPlainActionResult(message, Request);
    }
}

public class TestController : CustomApiController
{
    public IHttpActionResult Get()
    {
        return NotFound("These are not the droids you're looking for.");
    }
}
 84
Author: dmatson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-22 18:29:17

Oto jedna linijka do zwrotu IHttpActionResult Niefound z prostą wiadomością:

return Content(HttpStatusCode.NotFound, "Foo does not exist.");
 243
Author: Anthony F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-19 16:14:20

Możesz użyć ResponseMessageResult jeśli chcesz:

var myCustomMessage = "your custom message which would be sent as a content-negotiated response"; 
return ResponseMessage(
    Request.CreateResponse(
        HttpStatusCode.NotFound, 
        myCustomMessage
    )
);

Tak, jeśli potrzebujesz znacznie krótszych wersji, to chyba musisz zaimplementować swój niestandardowy wynik działania.

 28
Author: Kiran Challa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 16:57:08

Możesz użyć właściwości ReasonPhrase klasy HttpResponseMessage

catch (Exception exception)
{
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
  {
    ReasonPhrase = exception.Message
  });
}
 7
Author: Dmytro Rudenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-22 07:49:02

Możesz utworzyć niestandardowy wynik negocjowanej zawartości zgodnie z sugestią d3m3t3er. Jednak odziedziczyłbym po nim. Ponadto, jeśli potrzebujesz go tylko do zwracania NotFound, nie musisz inicjalizować statusu http z konstruktora.

public class NotFoundNegotiatedContentResult<T> : NegotiatedContentResult<T>
{
    public NotFoundNegotiatedContentResult(T content, ApiController controller)
        : base(HttpStatusCode.NotFound, content, controller)
    {
    }

    public override Task<HttpResponseMessage> ExecuteAsync(
        CancellationToken cancellationToken)
    {
        return base.ExecuteAsync(cancellationToken).ContinueWith(
            task => task.Result, cancellationToken);
    }
}
 4
Author: Andrei S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 12:44:13

Rozwiązałem to po prostu poprzez wyprowadzenie z OkNegotiatedContentResult i nadpisanie kodu HTTP w wynikowej wiadomości odpowiedzi. Ta klasa pozwala na zwrócenie zawartości ciała za pomocą dowolnego kodu odpowiedzi HTTP.

public class CustomNegotiatedContentResult<T> : OkNegotiatedContentResult<T>
{
    public HttpStatusCode HttpStatusCode;

    public CustomNegotiatedContentResult(
        HttpStatusCode httpStatusCode, T content, ApiController controller)
        : base(content, controller)
    {
        HttpStatusCode = httpStatusCode;
    }

    public override Task<HttpResponseMessage> ExecuteAsync(
        CancellationToken cancellationToken)
    {
        return base.ExecuteAsync(cancellationToken).ContinueWith(
            task => { 
                // override OK HTTP status code with our own
                task.Result.StatusCode = HttpStatusCode;
                return task.Result;
            },
            cancellationToken);
    }
}
 2
Author: demeter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-06 13:55:33

Potrzebowałem stworzyć IHttpActionResult instancję w ciele IExceptionHandler klasy, w celu ustawienia właściwości ExceptionHandlerContext.Result. Jednak chciałem również ustawić niestandardowy ReasonPhrase.

Odkryłem, że a {[5] } może zawijać a HttpResponseMessage (co pozwala łatwo ustawić ReasonPhrase).

Na Przykład:

public class MyExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        var ex = context.Exception as IRecordNotFoundException;
        if (ex != null)
        {
            context.Result = new ResponseMessageResult(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = $"{ex.EntityName} not found" });
        }
    }
}
 2
Author: Jono Job,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-04 07:23:13

Jeśli dziedziczysz z Bazy NegotitatedContentResult<T>, Jak wspomniano, i nie musisz przekształcać swojego content (np. chcesz tylko zwrócić łańcuch znaków), to nie musisz nadpisywać metody ExecuteAsync.

Wystarczy podać odpowiednią definicję typu i konstruktor, który powie bazie, który kod statusu HTTP ma zwrócić. Wszystko inne po prostu działa.

Oto przykłady dla NotFound i InternalServerError:

public class NotFoundNegotiatedContentResult : NegotiatedContentResult<string>
{
    public NotFoundNegotiatedContentResult(string content, ApiController controller)
        : base(HttpStatusCode.NotFound, content, controller) { }
}

public class InternalServerErrorNegotiatedContentResult : NegotiatedContentResult<string>
{
    public InternalServerErrorNegotiatedContentResult(string content, ApiController controller)
        : base(HttpStatusCode.InternalServerError, content, controller) { }
}

I wtedy można utworzyć odpowiednie metody rozszerzenia dla ApiController (lub zrób to w klasie bazowej, jeśli taką posiadasz):

public static NotFoundNegotiatedContentResult NotFound(this ApiController controller, string message)
{
    return new NotFoundNegotiatedContentResult(message, controller);
}

public static InternalServerErrorNegotiatedContentResult InternalServerError(this ApiController controller, string message)
{
    return new InternalServerErrorNegotiatedContentResult(message, controller);
}

I wtedy działają tak samo jak wbudowane metody. Możesz wywołać istniejącą NotFound() lub nową niestandardową NotFound(myErrorMessage).

I oczywiście, możesz pozbyć się" twardych " typów łańcuchów w niestandardowych definicjach typu i pozostawić je ogólne, jeśli chcesz, ale wtedy Może będziesz musiał się martwić o ExecuteAsync rzeczy, w zależności od tego, co <T> faktycznie jest.

Możesz spojrzeć nad kodem źródłowym dla NegotiatedContentResult<T>, aby zobaczyć wszystko, co robi. Nie ma w tym zbyt wiele.

 1
Author: sliderhouserules,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 19:32:59

Wiem, że PO zapytało z tekstem wiadomości, ale inną opcją zwrócenia 404 jest zwrócenie metody ihttpactionresult i użycie funkcji StatusCode

    public async Task<IHttpActionResult> Get([FromUri]string id)
    {
       var item = await _service.GetItem(id);
       if(item == null)
       {
           StatusCode(HttpStatusCode.NotFound);
       }
       return Ok(item);
    }
 0
Author: Maykool Jimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-06 16:24:44

Tutaj brakuje odpowiedzi na mały problem z fabułą programisty. Klasa ApiController wciąż ujawnia metodę NotFound(), której mogą używać Programiści. Spowodowałoby to, że niektóre 404 odpowiedzi zawierają niekontrolowany wynik ciała.

Przedstawiam tutaj kilka części kodu " better apicontroller NotFound method ", które zapewnią mniej podatną na błędy metodę, która nie wymaga od programistów znajomości "lepszego sposobu wysyłania 404".

  • Utwórz klasę dziedzicząc z ApiController called ApiController
    • używam tej techniki, aby uniemożliwić programistom używanie oryginalnej klasy
  • override its NotFound method to let devs use the first available api
  • jeśli chcesz to zniechęcić, oznacz to jako [Obsolete("Use overload instead")]
  • dodaj dodatkowe protected NotFoundResult NotFound(string message) które chcesz zachęcić
  • problem: wynik nie wspiera reakcji z ciałem. rozwiązanie: dziedziczenie i używanie NegotiatedContentResult. zobacz w załączeniu better NotFoundResult Klasa .
 0
Author: SandRock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-06 11:02:39