오류 메시지 또는 예외와 함께 NotFound () IHttpActionResult를 반환하려면 어떻게해야합니까?
IHttpActionResult
WebApi GET 작업에서 무언가를 찾을 수없는 경우 NotFound를 반환하고 있습니다. 이 응답과 함께 사용자 지정 메시지 및 / 또는 예외 메시지 (있는 경우)를 보내려고합니다. 현재 ApiController
의 NotFound()
메서드는 메시지를 전달하기위한 오버로드를 제공하지 않습니다.
이 작업을 수행 할 방법이 있습니까? 아니면 내 사용자 정의를 작성해야 IHttpActionResult
합니까?
응답 메시지 모양을 사용자 지정하려면 고유 한 작업 결과를 작성해야합니다.
우리는 단순한 빈 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.");
}
}
다음은 간단한 메시지와 함께 IHttpActionResult NotFound를 반환하는 한 줄입니다.
return Content(HttpStatusCode.NotFound, "Foo does not exist.");
원하는 ResponseMessageResult
경우 사용할 수 있습니다 .
var myCustomMessage = "your custom message which would be sent as a content-negotiated response";
return ResponseMessage(
Request.CreateResponse(
HttpStatusCode.NotFound,
myCustomMessage
)
);
예, 훨씬 더 짧은 버전이 필요한 경우 사용자 지정 작업 결과를 구현해야합니다.
You may use ReasonPhrase property of HttpResponseMessage class
catch (Exception exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
{
ReasonPhrase = exception.Message
});
}
I solved it by simply deriving from OkNegotiatedContentResult
and overriding the HTTP code in the resulting response message. This class allows you to return the content body with any HTTP response code.
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);
}
}
You can create a custom negotiated content result as d3m3t3er suggested. 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.
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);
}
}
If you inherit from the base NegotitatedContentResult<T>
, as mentioned, and you don't need to transform your content
(e.g. you just want to return a string), then you don't need to override the ExecuteAsync
method.
All you need to do is provide an appropriate type definition and a constructor that tells the base which HTTP Status Code to return. Everything else just works.
Here are examples for both NotFound
and 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) { }
}
And then you can create corresponding extension methods for ApiController
(or do it in a base class if you have one):
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)
.
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.
You can look over the source code for NegotiatedContentResult<T>
to see all it does. There isn't much to it.
I was needing to create an IHttpActionResult
instance in the body of an IExceptionHandler
class, in order to set the ExceptionHandlerContext.Result
property. However I also wanted to set a custom ReasonPhrase
.
I found that a ResponseMessageResult
could wrap a HttpResponseMessage
(which allows ReasonPhrase to be set easily).
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" });
}
}
}
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
public async Task<IHttpActionResult> Get([FromUri]string id)
{
var item = await _service.GetItem(id);
if(item == null)
{
StatusCode(HttpStatusCode.NotFound);
}
return Ok(item);
}
'program tip' 카테고리의 다른 글
간단한 레이아웃에서 파급 효과를 만드는 방법 (0) | 2020.09.02 |
---|---|
Yarn 설치 명령 오류 해당 파일 또는 디렉토리 없음 : 'install' (0) | 2020.09.02 |
사용자 지정 DialogFragment를 조각 위에 투명하게 만들 수 없습니다. (0) | 2020.09.02 |
Fabric 플러그인에서 로그 아웃 (0) | 2020.09.02 |
여러 섹션을 열어 두는 jQuery UI 아코디언? (0) | 2020.09.02 |