简体   繁体   English

如何返回带有错误消息或异常的 NotFound() IHttpActionResult?

[英]How do I return NotFound() IHttpActionResult with an error message or exception?

I am returning a NotFound IHttpActionResult , when something is not found in my WebApi GET action.当在我的 WebApi GET 操作中找不到某些内容时,我将返回 NotFound IHttpActionResult Along with this response, I want to send a custom message and/or the exception message (if any).除了此响应,我还想发送自定义消息和/或异常消息(如果有)。 The current ApiController 's NotFound() method does not provide an overload to pass a message.当前ApiControllerNotFound()方法不提供传递消息的重载。

Is there any way of doing this?有什么办法吗? or I will have to write my own custom IHttpActionResult ?或者我将不得不编写自己的自定义IHttpActionResult

这是返回 IHttpActionResult NotFound 和一条简单消息的单行代码:

return Content(HttpStatusCode.NotFound, "Foo does not exist.");

You'd need to write your own action result if you want to customize the response message shape.如果要自定义响应消息形状,则需要编写自己的操作结果。

We wanted to provide the most common response message shapes out of the box for things like simple empty 404s, but we also wanted to keep these results as simple as possible;我们希望为简单的空 404 等事件提供最常见的现成响应消息形状,但我们也希望使这些结果尽可能简单; one of the main advantages of using action results is that it makes your action method much easier to unit test.使用操作结果的主要优点之一是它使您的操作方法更易于单元测试。 The more properties we put on action results, the more things your unit test needs to consider to make sure the action method is doing what you'd expect.我们在操作结果上放置的属性越多,您的单元测试需要考虑的事情就越多,以确保操作方法按您的预期执行。

I often want the ability to provide a custom message as well, so feel free to log a bug for us to consider supporting that action result in a future release: https://aspnetwebstack.codeplex.com/workitem/list/advanced我通常也希望能够提供自定义消息,因此请随时记录错误,以便我们考虑在未来版本中支持该操作结果: https : //aspnetwebstack.codeplex.com/workitem/list/advanced

One nice thing about action results, though, is that you can always write your own fairly easily if you want to do something slightly different.但是,关于动作结果的一个好处是,如果您想做一些稍微不同的事情,您总是可以相当轻松地编写自己的结果。 Here's how you might do it in your case (assuming you want the error message in text/plain; if you want JSON, you'd do something slightly different with the content):在您的情况下,您可能会这样做(假设您想要文本/纯文本格式的错误消息;如果您想要 JSON,您可以对内容做一些稍微不同的事情):

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

Then, in your action method, you can just do something like this:然后,在您的操作方法中,您可以执行以下操作:

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

If you used a custom controller base class (instead of directly inheriting from ApiController), you could also eliminate the "this."如果您使用自定义控制器基类(而不是直接从 ApiController 继承),您还可以消除“this”。 part (which is unfortunately required when calling an extension method):部分(不幸的是在调用扩展方法时需要):

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.");
    }
}

You could use ResponseMessageResult if you like:如果您愿意,可以使用ResponseMessageResult

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

yeah, if you need much shorter versions, then I guess you need to implement your custom action result.是的,如果您需要更短的版本,那么我想您需要实现您的自定义操作结果。

You may use ReasonPhrase property of HttpResponseMessage class您可以使用 HttpResponseMessage 类的 ReasonPhrase 属性

catch (Exception exception)
{
  throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
  {
    ReasonPhrase = exception.Message
  });
}

You can create a custom negotiated content result as d3m3t3er suggested.您可以按照 d3m3t3er 的建议创建自定义协商内容结果。 However I would inherit from.但是我会继承。 Also, if you need it only for returning NotFound, you don't need to initialize the http status from constructor.此外,如果您只需要它来返回 NotFound,则不需要从构造函数初始化 http 状态。

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

I solved it by simply deriving from OkNegotiatedContentResult and overriding the HTTP code in the resulting response message.我通过简单地从OkNegotiatedContentResult并覆盖结果响应消息中的 HTTP 代码来解决它。 This class allows you to return the content body with any HTTP response code.此类允许您使用任何 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);
    }
}

I was needing to create an IHttpActionResult instance in the body of an IExceptionHandler class, in order to set the ExceptionHandlerContext.Result property.我需要在IExceptionHandler类的主体中创建一个IHttpActionResult实例,以便设置ExceptionHandlerContext.Result属性。 However I also wanted to set a custom ReasonPhrase .但是我也想设置一个自定义ReasonPhrase

I found that a ResponseMessageResult could wrap a HttpResponseMessage (which allows ReasonPhrase to be set easily).我发现ResponseMessageResult可以包装一个HttpResponseMessage (它允许轻松设置 ReasonPhrase)。

For Example:例如:

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

If you inherit from the base NegotitatedContentResult<T> , as mentioned, and you don't need to transform your content (eg you just want to return a string), then you don't need to override the ExecuteAsync method.如果您从基础NegotitatedContentResult<T>继承,如上所述,并且您不需要转换您的content (例如,您只想返回一个字符串),那么您不需要覆盖ExecuteAsync方法。

All you need to do is provide an appropriate type definition and a constructor that tells the base which HTTP Status Code to return.您需要做的就是提供一个适当的类型定义和一个构造函数,告诉 base 返回哪个 HTTP 状态代码。 Everything else just works.其他一切都正常。

Here are examples for both NotFound and InternalServerError :以下是NotFoundInternalServerError示例:

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) { }
}

And then you can create corresponding extension methods for ApiController (or do it in a base class if you have one):然后你可以为ApiController创建相应的扩展方法(如果你有的话,也可以在基类中创建):

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

And then they work just like the built-in methods.然后它们就像内置方法一样工作。 You can either call the existing NotFound() or you can call your new custom NotFound(myErrorMessage) .您可以调用现有的NotFound() ,也可以调用新的自定义NotFound(myErrorMessage)

And of course, you can get rid of the "hard-coded" string types in the custom type definitions and leave it generic if you want, but then you may have to worry about the ExecuteAsync stuff, depending on what your <T> actually is.当然,您可以摆脱自定义类型定义中的“硬编码”字符串类型,并根据需要将其保留为通用类型,但是您可能不得不担心ExecuteAsync内容,具体取决于您的<T>实际内容是。

You can look over the source code for NegotiatedContentResult<T> to see all it does.您可以查看NegotiatedContentResult<T>源代码以了解它所做的一切。 There isn't much to it.没什么可做的。

Another nice possibility is to use a different built-in result type: NotFoundObjectResult(message) .另一个不错的可能性是使用不同的内置结果类型: NotFoundObjectResult(message)

Iknow PO asked with a message text, but another option to just return a 404 is making the method return a IHttpActionResult and use the StatusCode function我知道 PO 询问了一条消息文本,但另一个返回 404 的选项是使该方法返回 IHttpActionResult 并使用 StatusCode 函数

    public async Task<IHttpActionResult> Get([FromUri]string id)
    {
       var item = await _service.GetItem(id);
       if(item == null)
       {
           StatusCode(HttpStatusCode.NotFound);
       }
       return Ok(item);
    }

Answers here are missing a little developer story problem.这里的答案缺少一个小的开发者故事问题。 The ApiController class is still exposing a NotFound() method that developers may use. ApiController类仍然公开了一个NotFound()方法,开发人员可能会使用它。 This would cause some 404 response to contain a uncontrolled result body.这将导致一些 404 响应包含不受控制的结果主体。

I present here a few parts of code " better ApiController NotFound method " that will provide a less error-prone method that does not require developers to know "the better way of sending a 404".我在这里展示了代码的几个部分“ 更好的 ApiController NotFound 方法”,它将提供一种不太容易出错的方法,不需要开发人员知道“发送 404 的更好方法”。

  • create a class inheriting from ApiController called ApiController创建一个继承自ApiController的类名为ApiController
    • I use this technique to prevent developers from using the original class我使用这种技术来防止开发人员使用原始类
  • override its NotFound method to let devs use the first available api覆盖它的NotFound方法让开发者使用第一个可用的 api
  • if you want to discourage this, mark this as [Obsolete("Use overload instead")]如果您想阻止这种情况,请将其标记为[Obsolete("Use overload instead")]
  • add an extra protected NotFoundResult NotFound(string message) that you want to encourage添加您想要鼓励的额外protected NotFoundResult NotFound(string message)
  • problem: the result does not support responding with a body.问题:结果不支持用body响应。 solution: inherit and use NegotiatedContentResult .解决方案:继承并使用NegotiatedContentResult see attached better NotFoundResult class .请参阅附加更好的 NotFoundResult 类

asp.net核心中的一行代码:

Return StatusCode(404, "Not a valid request.");

Needed to return the error message for 404 Not Found and I am using Dot Net 6.0.需要返回 404 Not Found 的错误消息,我使用的是 Dot Net 6.0。

This is the code这是代码

Problem(statusCode: 404, detail: "Put your detailed error message here");

Where Problem is a method present in ControllerBase class.其中 Problem 是 ControllerBase 类中存在的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM