I am returning a NotFound IHttpActionResult
, when something is not found in my WebApi GET action. Along with this response, I want to send a custom message and/
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 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);
}
}
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);
}