[英]How do I return NotFound() IHttpActionResult with an error message or exception?
當在我的 WebApi GET 操作中找不到某些內容時,我將返回 NotFound IHttpActionResult
。 除了此響應,我還想發送自定義消息和/或異常消息(如果有)。 當前ApiController
的NotFound()
方法不提供傳遞消息的重載。
有什么辦法嗎? 或者我將不得不編寫自己的自定義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 狀態代碼。 其他一切都正常。
以下是NotFound
和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) { }
}
然后你可以為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)
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.