Intrepid
Intrepid

Reputation: 2831

Creating new IHttpActionResult action result methods

Is there a way I can use the new IHttpActionResult interface to return a HttpStatusCode.NoContent response message?

I am currently using return new HttpResponseMessage( HttpStatusCode.NoContent ); and would like to convert this into return NoContent();.

IHttpActionResult has already got Ok(), Conflict() and NotFound() but I cannot find any for Forbidden() and NoContent() which I need to use in my project.

How easy is it to add other result types?

Upvotes: 43

Views: 32954

Answers (6)

joehoper
joehoper

Reputation: 423

This worked well for me:

public class CodeAndReason : IHttpActionResult
{
    private readonly HttpStatusCode code;
    private readonly string reason;

    public CodeAndReason(HttpStatusCode code, string reason)
    {
        this.code = code;
        this.reason = reason;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(code)
        {
            ReasonPhrase = reason,
            Content = new StringContent(reason),
        };

        return Task.FromResult(response);
    }

    public static IHttpActionResult NotFound(string reason)
    {
        return new CodeAndReason(HttpStatusCode.NotFound, reason);
    }

    public static IHttpActionResult Conflict(string reason)
    {
        return new CodeAndReason(HttpStatusCode.Conflict, reason);
    }

    public static IHttpActionResult Unauthorized(string reason)
    {
        return new CodeAndReason(HttpStatusCode.Unauthorized, reason);
    }
}

Used as:

return CodeAndReason.NotFound("Record {blah} not found");

Upvotes: 3

Scott Wendt
Scott Wendt

Reputation: 81

You can now use the following (.Net Standard):

return StatusCode(HttpStatusCode.NoContent);

or (.Net Core 2.1+)

return NoContent();

Upvotes: 8

Asaf Epelbaum
Asaf Epelbaum

Reputation: 586

I tried the @Intrepid implementation and I ran into some problems. I see two solutions here:

Solution 1: The part: return Forbidden(); should not work.

The compiler would not recognize this.

Instead it should be: return new ForbiddenResult(Request, "my reason");

UPDATE 1

Solution 2:

I think this is what @Interpid intended in his implementation, but he was missing a few things.

In order to use return Forbidden(); the CommonApiController should be updated with the functions that return the custom IHttpActionResult for Forbidden and NoContent

The class should look like this:

public abstract class CommonApiController: ApiController {

    protected ForbiddenResult Forbidden() {
     return new ForbiddenResult(this.Request);
    }

    protected ForbiddenResult Forbidden(string reason) {
     return new ForbiddenResult(this.Request, reason);
    }

    protected NoContentResult NoContent() {
     return new NoContentResult(this.Request);
    }

    public class ForbiddenResult: IHttpActionResult {
     private readonly HttpRequestMessage _request;
     private readonly string _reason;

     public ForbiddenResult(HttpRequestMessage request, string reason) {
      _request = request;
      _reason = reason;
     }

     public ForbiddenResult(HttpRequestMessage request) {
      _request = request;
      _reason = "Forbidden";
     }

     public Task < HttpResponseMessage > ExecuteAsync(CancellationToken cancellationToken) {
      var response = _request.CreateResponse(HttpStatusCode.Forbidden, _reason);
      return Task.FromResult(response);
     }
    }

    public class NoContentResult: IHttpActionResult {
     private readonly HttpRequestMessage _request;
     private readonly string _reason;

     public NoContentResult(HttpRequestMessage request, string reason) {
      _request = request;
      _reason = reason;
     }

     public NoContentResult(HttpRequestMessage request) {
      _request = request;
      _reason = "No Content";
     }

     public Task < HttpResponseMessage > ExecuteAsync(CancellationToken cancellationToken) {
      var response = _request.CreateResponse(HttpStatusCode.NoContent, _reason);
      return Task.FromResult(response);
     }
    }
   }

Anyway, if I am wrong and @Interpid's answer is correct. What am I missing here to make his implementation work?

Upvotes: 9

Rafael Companhoni
Rafael Companhoni

Reputation: 1790

There's no convenience method for no-content result because, by default, when a action returns void, the response will have the HTTP status 204.

If you wish to explicitly indicate that on the action, you could also return a StatusCode(HttpStatusCode.NoContent) from your action or a

ResponseMessage(new HttpResponseMessage(HttpStatusCode.NoContent)).

The Unauthorized() convenience method gives you a 401 status so, for Forbidden (403), you would also have to use StatusCode(HttpStatusCode.Forbidden) or

ResponseMessage(new HttpResponseMessage(HttpStatusCode.Forbidden))

Upvotes: 57

kmac.mcfarlane
kmac.mcfarlane

Reputation: 307

If you want to include a reason phrase with your response without adding a sub-class to ApiController, build a ResponseMessage object and return it from the action by the ResponseMessage() method. Try this:

public class InvoiceController : ApiController
{
    public async Task<IHttpActionResult> Post([FromBody]Invoice invoice)
    {
        if(User.IsInRole("Readonly"))
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);

            response.ReasonPhrase = "User has the Readonly role";
            return ResponseMessage(response);
        }

        // Rest of code

    }
}

Upvotes: 3

Intrepid
Intrepid

Reputation: 2831

I found this example site that shows how to add a custom IHttpActionResult method and I've used this to create the Forbidden() and NoContent() methods with great success.

public abstract class CommonApiController : ApiController
{
    public class ForbiddenResult : IHttpActionResult
    {
        private readonly HttpRequestMessage _request;
        private readonly string _reason;

        public ForbiddenResult(HttpRequestMessage request,string reason)
        {
            _request = request;
            _reason = reason;
        }

        public ForbiddenResult(HttpRequestMessage request)
        {
            _request = request;
            _reason = "Forbidden";
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = _request.CreateResponse(HttpStatusCode.Forbidden,_reason);
            return Task.FromResult(response);
        }
    }

    public class NoContentResult : IHttpActionResult
    {
        private readonly HttpRequestMessage _request;
        private readonly string _reason;

        public NoContentResult(HttpRequestMessage request,string reason)
        {
            _request = request;
            _reason = reason;
        }

        public NoContentResult(HttpRequestMessage request)
        {
            _request = request;
            _reason = "No Content";
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = _request.CreateResponse(HttpStatusCode.NoContent,_reason);
            return Task.FromResult(response);
        }
    }
}

And then I can use it like this:

public class InvoiceController : CommonApiController
{
    public async Task<IHttpActionResult> Post([FromBody]Invoice invoice)
    {
        if(User.IsInRole("Readonly"))
        {
            return Forbidden();
        }

        // Rest of code

    }
}

Upvotes: 25

Related Questions