簡體   English   中英

如何返回帶有錯誤消息或異常的 NotFound() IHttpActionResult?

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

當在我的 WebApi GET 操作中找不到某些內容時,我將返回 NotFound IHttpActionResult 除了此響應,我還想發送自定義消息和/或異常消息(如果有)。 當前ApiControllerNotFound()方法不提供傳遞消息的重載。

有什么辦法嗎? 或者我將不得不編寫自己的自定義IHttpActionResult

這是返回 IHttpActionResult NotFound 和一條簡單消息的單行代碼:

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

如果要自定義響應消息形狀,則需要編寫自己的操作結果。

我們希望為簡單的空 404 等事件提供最常見的現成響應消息形狀,但我們也希望使這些結果盡可能簡單; 使用操作結果的主要優點之一是它使您的操作方法更易於單元測試。 我們在操作結果上放置的屬性越多,您的單元測試需要考慮的事情就越多,以確保操作方法按您的預期執行。

我通常也希望能夠提供自定義消息,因此請隨時記錄錯誤,以便我們考慮在未來版本中支持該操作結果: https : //aspnetwebstack.codeplex.com/workitem/list/advanced

但是,關於動作結果的一個好處是,如果您想做一些稍微不同的事情,您總是可以相當輕松地編寫自己的結果。 在您的情況下,您可能會這樣做(假設您想要文本/純文本格式的錯誤消息;如果您想要 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);
    }
}

然后,在您的操作方法中,您可以執行以下操作:

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

如果您使用自定義控制器基類(而不是直接從 ApiController 繼承),您還可以消除“this”。 部分(不幸的是在調用擴展方法時需要):

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

如果您願意,可以使用ResponseMessageResult

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

是的,如果您需要更短的版本,那么我想您需要實現您的自定義操作結果。

您可以使用 HttpResponseMessage 類的 ReasonPhrase 屬性

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

您可以按照 d3m3t3er 的建議創建自定義協商內容結果。 但是我會繼承。 此外,如果您只需要它來返回 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);
    }
}

我通過簡單地從OkNegotiatedContentResult並覆蓋結果響應消息中的 HTTP 代碼來解決它。 此類允許您使用任何 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);
    }
}

我需要在IExceptionHandler類的主體中創建一個IHttpActionResult實例,以便設置ExceptionHandlerContext.Result屬性。 但是我也想設置一個自定義ReasonPhrase

我發現ResponseMessageResult可以包裝一個HttpResponseMessage (它允許輕松設置 ReasonPhrase)。

例如:

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

如果您從基礎NegotitatedContentResult<T>繼承,如上所述,並且您不需要轉換您的content (例如,您只想返回一個字符串),那么您不需要覆蓋ExecuteAsync方法。

您需要做的就是提供一個適當的類型定義和一個構造函數,告訴 base 返回哪個 HTTP 狀態代碼。 其他一切都正常。

以下是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) { }
}

然后你可以為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);
}

然后它們就像內置方法一樣工作。 您可以調用現有的NotFound() ,也可以調用新的自定義NotFound(myErrorMessage)

當然,您可以擺脫自定義類型定義中的“硬編碼”字符串類型,並根據需要將其保留為通用類型,但是您可能不得不擔心ExecuteAsync內容,具體取決於您的<T>實際內容是。

您可以查看NegotiatedContentResult<T>源代碼以了解它所做的一切。 沒什么可做的。

另一個不錯的可能性是使用不同的內置結果類型: NotFoundObjectResult(message)

我知道 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);
    }

這里的答案缺少一個小的開發者故事問題。 ApiController類仍然公開了一個NotFound()方法,開發人員可能會使用它。 這將導致一些 404 響應包含不受控制的結果主體。

我在這里展示了代碼的幾個部分“ 更好的 ApiController NotFound 方法”,它將提供一種不太容易出錯的方法,不需要開發人員知道“發送 404 的更好方法”。

  • 創建一個繼承自ApiController的類名為ApiController
    • 我使用這種技術來防止開發人員使用原始類
  • 覆蓋它的NotFound方法讓開發者使用第一個可用的 api
  • 如果您想阻止這種情況,請將其標記為[Obsolete("Use overload instead")]
  • 添加您想要鼓勵的額外protected NotFoundResult NotFound(string message)
  • 問題:結果不支持用body響應。 解決方案:繼承並使用NegotiatedContentResult 請參閱附加更好的 NotFoundResult 類

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

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

需要返回 404 Not Found 的錯誤消息,我使用的是 Dot Net 6.0。

這是代碼

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

其中 Problem 是 ControllerBase 類中存在的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM