6

I'm working on a project using ASP.NET 5 and I'm writing a web api.

I've inherited some code and database stored procedures that use raiserror to indicate something is wrong (username/password incorrect, expired license etc).

The stored procedure returns nothing to uniquely identify that error, except the message text.

I want to be able to return a HTTP UNAUTHORIZED response, but also shuttle the error message along to the client too.

The built in IActionResult HttpUnauthorized() method doesn't allow for a reason to be given.

So I wrote my own ActionResult that looks like this:

public class UnauthorizedWithMessageResult : IActionResult
{
    private readonly string _message;

    public UnauthorizedWithMessageResult(string message)
    {
        _message = message;
    }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        using (var sw = new HttpResponseStreamWriter(context.HttpContext.Response.Body, Encoding.UTF8))
        {
            await sw.WriteLineAsync(_message);
        }

        await new HttpUnauthorizedResult().ExecuteResultAsync(context);
    }
}

The problem is that the client is receiving a 200-OK like everything is fine.

I've stepped through this and after the delegation to HttpUnauthorizedResult has been done, the status code is indeed set to 403.

It looks like Web API is (at some point) seeing that there's content in the body of the response and decides that means everything is OK and resets the status code.

Is there any way to get around this without having to resort to sending the message as a header or something? (or is that the correct way to do this?)

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
Clint
  • 6,133
  • 2
  • 27
  • 48
  • You can't set a status code after you write any portion of the response body. Have you tried calling HttpUnauthorizedResult.ExecuteResultAsync before writing your message? – yaakov Mar 24 '16 at 02:38
  • 1
    looks this link may solve your problem http://weblogs.asp.net/gunnarpeipman/asp-net-mvc-3-creating-httpstatuscoderesult-with-view-based-body – Gomes Mar 24 '16 at 03:02
  • @Gomes that looks very promising! I suppose conceptually it makes sense, shall have to take a look soon! – Clint Mar 24 '16 at 03:05
  • @Gomes unfortunately that doesn't solve my issue, the `StatusDescription` property is no longer a thing... – Clint Mar 24 '16 at 04:06

3 Answers3

6

You can do this:

return new ObjectResult("The message") { 
    StatusCode = (int?) HttpStatusCode.Unauthorized 
};
CSJ
  • 3,641
  • 2
  • 19
  • 29
5

It works this way:

public IActionResult IWillNotBeCalled()
{
    return new UnauthorizedWithMessageResult("MY SQL ERROR");            
}

public class UnauthorizedWithMessageResult : IActionResult
{
    private readonly string _message;

    public UnauthorizedWithMessageResult(string message)
    {
        _message = message;
    }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        // you need to do this before setting the body content
        context.HttpContext.Response.StatusCode = 403;

        var myByteArray = Encoding.UTF8.GetBytes(_message);
        await context.HttpContext.Response.Body.WriteAsync(myByteArray, 0, myByteArray.Length);
        await context.HttpContext.Response.Body.FlushAsync();
    }
}

You have to set the StatusCode before setting the body and you have to flush the body stream to be sure that the content will be set inside the response.

enter image description here

Hope it helps :)

Luca Ghersi
  • 3,261
  • 18
  • 32
-1

You can return any status code you want, like so:

return new HttpStatusCodeResult(403);
Danny van der Kraan
  • 5,344
  • 6
  • 31
  • 41
  • 2
    It's not the status code that's the problem, it's adding some text to the response body along with a status code that's proving to be challenging. – Clint Mar 24 '16 at 09:19