6

I am trying to return status codes in the following way

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "invalid username/password" });

now this is not supported in MVC6, which sucks because using IActionResult seems really silly to me, and way less intuitive. I have found the following posts one and two. the first leads to a broken link, and the second applies to an MVC application.

I did realize that I need to create a middleware to address this issue, but I am not sure where to start, and since this is pretty useful stuff, I would expect there to be some open source solution I could use or maybe a code snippet.

For the record, I am using ASP.net 5 rc1 update 1 MVC 6

Community
  • 1
  • 1
gilmishal
  • 1,884
  • 1
  • 22
  • 37
  • Whats wrong with StatusCodeResult? https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/StatusCodeResult.cs – Andreas Feb 21 '16 at 15:17
  • You have to set your action's return value as IActionResult as far as I know, and I don't want that – gilmishal Feb 21 '16 at 15:54
  • 1
    I think you should be returning the correct `IActionResult` (`HttpUnauthorizedResult`), because that keeps the concerns of the controller inside the controller, rather than throwing your exception from deep within your domain. Do you want the request to still be processed by other middleware after you throw your exception? Also, is this exception thrown from every controller? – Dr Rob Lang Feb 22 '16 at 08:50
  • I have to say I disagree, in my opinion, the controller responsibility is to manage a resource, and that resource should be seen in the header of each method in the controller. When there is an invalid workflow (for example invalid credentials) it makes more sense to throw an exception, and let the exception be resolved by a middleware. Any way, this works in MVC5, and that's what bugs me the most. – gilmishal Feb 22 '16 at 08:57

1 Answers1

6

I think this change is due to .NET Core - where ASP.NET tries to do everything out of the box, ASP.NET Core only does what you specifically tell it to (which is a big part of why it's so much quicker and portable).

If you want this behaviour in Core you need to add it, either as a package that someone has written for you or by rolling your own.

It's fairly simple. First you need a custom exception to check for:

public class StatusCodeException : Exception
{
    public StatusCodeException(HttpStatusCode statusCode)
    {
        StatusCode = statusCode;
    }

    public HttpStatusCode StatusCode { get; set; }
}

Then you need a RequestDelegate handler that checks for the new exception and converts it to the HTTP response status code:

public class StatusCodeExceptionHandler
{
    private readonly RequestDelegate request;

    public StatusCodeExceptionHandler(RequestDelegate next)
    {
        this.request = next;
    }

    public Task Invoke(HttpContext context) => this.InvokeAsync(context); // Stops VS from nagging about async method without ...Async suffix.

    async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await this.request(context);
        }
        catch (StatusCodeException exception)
        {
            context.Response.StatusCode = (int)exception.StatusCode;
            context.Response.Headers.Clear();
        }
    }
}

Then you register this middleware in your startup:

public class Startup
{
    ...

    public void Configure(IApplicationBuilder app)
    {
        ...
        app.UseMiddleware<StatusCodeExceptionHandler>();

Finally you can throw the HTTP status code exception in your actions, while still returning an explicit type that you can easily unit test:

public Thing Get(int id) {
    Thing thing = GetThingFromDB();

    if (thing == null)
        throw new StatusCodeException(HttpStatusCode.NotFound);

    return thing;
}

This is fairly simple and someone out there has probably written a more complete one, but I haven't been able to find one or get a clear answer as to why this was dropped.

Keith
  • 150,284
  • 78
  • 298
  • 434