60

I'm working on a service in WebAPI 2, and the endpoint currently returns an IHttpActionResult. I'd like to return a status code 422, but since it's not in the HttpStatusCode enumeration, I'm at a loss as to how it would be sent, since all of the constructors require a parameter of HttpStatusCode

As it stands now, I'm returning BadResult(message), but returning a 422 + message would be more descriptive and useful for my clients. Any ideas?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Garrison Neely
  • 3,238
  • 3
  • 27
  • 39
  • 1
    What happens if you return '(HttpStatusCode)422`. That's valid C# but I haven't used WebAPI 2, to be sure it won't be rejected. Otherwise one can certainly return `422 Unprocessable Entity` and other post-RFC 2616 statuses with ASP.NET or ASP.NET MVC, so maybe it's possible to drop down to that in WebAPI2, but again I don't know that so can't give a full answer. I do know that in MVC while setting just `HttpResponse.StatusCode` will automatically set the correct status description for the RFC 2616 codes, you'll need to set "Unprocessable Entity" etc. yourself beyond those. – Jon Hanna Apr 30 '14 at 23:01
  • Sorry, why do you want to use 422? It is not defined in the HTTP 1.1 specification! – Toan Nguyen May 02 '14 at 06:48
  • @ToanNguyen 422 is defined in HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV) (http://tools.ietf.org/html/rfc4918#section-11.2) See also: http://www.bennadel.com/blog/2434-http-status-codes-for-invalid-data-400-vs-422.htm – Ergwun Jun 10 '14 at 04:30

8 Answers8

96

According to C# specification:

The set of values that an enum type can take on is not limited by its enum members. In particular, any value of the underlying type of an enum can be cast to the enum type and is a distinct valid value of that enum type

Therefore you can cast status code 422 to HttpStatusCode.

Example controller:

using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace CompanyName.Controllers.Api
{
    [RoutePrefix("services/noop")]
    [AllowAnonymous]
    public class NoOpController : ApiController
    {
        [Route]
        [HttpGet]
        public IHttpActionResult GetNoop()
        {
            return new System.Web.Http.Results.ResponseMessageResult(
                Request.CreateErrorResponse(
                    (HttpStatusCode)422,
                    new HttpError("Something goes wrong")
                )
            );
        }
    }
}
lilo.jacob
  • 2,334
  • 1
  • 20
  • 17
  • Great solution. I refactored this and made a method on the controller that returns an IHttpActionResult. It is simplified to 'return Status(404,"Some error."). – RyanJMcGowan Oct 13 '16 at 02:31
28
 return Content((HttpStatusCode) 422, whatEver);

credit is for: Return content with IHttpActionResult for non-OK response

and your code must be <= 999

and please ignore codes between 100 to 200.

Community
  • 1
  • 1
peyman
  • 490
  • 7
  • 10
  • 1
    Can you explain more why we should ignore codes between 100 to 200 please? – Dhouha BOUSSALEM Oct 16 '20 at 13:40
  • Presumably, the advice to ignore codes between 100 and 200 is because codes 100-199 are informational and code 200 (OK) usually means success. However, I think this advice is too broad. There are less common status codes in the 201-299 range, such as 201 (Created), for which I wouldn't return an error message. A good list of status codes can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status – Amos Long Feb 15 '23 at 21:49
3

I use this way simple and elegant.

public ActionResult Validate(User user)
{
     return new HttpStatusCodeResult((HttpStatusCode)500, 
               "My custom internal server error.");
}

Then angular controller.

function errorCallBack(response) {            
$scope.response = {
   code: response.status,
   text: response.statusText
}});    

Hope it helps you.

2

Another simplified example:

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        HttpStatusCode codeNotDefined = (HttpStatusCode)422;
        return Content(codeNotDefined, "message to be sent in response body");
    }
}

Content is a virtual method defined in abstract class ApiController, the base of the controller. See the declaration as below:

protected internal virtual NegotiatedContentResult<T> Content<T>(HttpStatusCode statusCode, T value);
themefield
  • 3,847
  • 30
  • 32
2

I'm using a custom Filter in my controllers that catches any exceptions and returns custom error results so I needed a way to return a custom message response based on the exception message.

I'm using JsonResult (found in the Microsoft.AspNetCore.Mvc namespace) to return a custom status code and messagesas such:

public class DomainViolationFilter : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            switch (context.Exception)
            {
                case EntityNotFoundException e:
                    JsonResult toReturn = new JsonResult(e);
                    toReturn.StatusCode = 404;
                    context.Result = toReturn;
                    return;
            }
        }
    }

In my case, I'm serializing directly the exception object (ex) for simplicity but you should be returning only the relevant (and safe) data, or whatever you need in your use case.

Filipe Madureira
  • 420
  • 4
  • 17
1

For this you may need to use an Action Filter Attribute. Nothing fancy. Just create a class and inherit it from ActionFilterAttribute class in c#. Then override a method named OnActionExecuting to implement this. Then just use this filter on the head of any controller. Following is a demo.

On the condition when you need to produce custom status code based message in ActionFilterAttribute you can write in following way:

        if (necessity_to_send_custom_code)
        {
            actionContext.Response = actionContext.Request.CreateResponse((HttpStatusCode)855, "This is custom error message for you");
        }

Hope this helps.

0

This works for C# in 2023, whereas the older examples do not seem to work with Content as the first parameter needs to be String content.

var response = Content("We cannot process this");
response.StatusCode = 422;

return response; 
Bart McLeod
  • 151
  • 2
  • 6
0

Alot of answer here use casting or integer directly.

But you can also use Microsoft.AspNetCore.Http.StatusCodes and get back an int with the added benefit of reference type safety.

Example: return Content(StatusCodes.Status422UnprocessableEntity, ...);

Codingwiz
  • 192
  • 2
  • 14