Is the Azure Function response status code always HTTP 200? Did you know?

2023, May 27

Is the Azure Function response status code always HTTP 200? Can I change that? It depends on your implementation.

The issue

Most ASP.NET developers (including myself) would leverage IActionResult in API responses, and that's where the issue lies.

What happens is that no matter the object that you use, which implements IActionResult, you always get an HTTP 200 response in Azure Functions.

Look at the endpoints below, for the first request OkStatusCode you should get a HTTP 200 as it returns a OkObjectResult, what about AlwaysHttp200StatusCode? It returns a BadRequestObjectResult, which in theory should be reflected as an HTTP 400 response. Is that the case? What about AlwaysHttp200StatusCodeAgain? That's an InternalServerErrorResult!

[Function(nameof(OkStatusCode))]
public IActionResult OkStatusCode([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    return new OkObjectResult("Welcome to Azure Functions!");
}

[Function(nameof(AlwaysHttp200StatusCode))]
public IActionResult AlwaysHttp200StatusCode([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    return new BadRequestObjectResult("Welcome to Azure Functions!");
}

[Function(nameof(AlwaysHttp200StatusCodeAgain))]
public IActionResult AlwaysHttp200StatusCodeAgain([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    return new InternalServerErrorResult();
}

Funny enough we get HTTP 200 responses with a body that lists the different statuses.

Expected-HTTP200

Expected-HTTP400

Expected-HTTP500

The HTTP response

The problem above happens because we are manipulating the response body payload and not the HTTP response.

To be able to modify the HTTP response, we need to use an object that changes Http responses, using HttpResponseData for example.

Look at the endpoints below:

[Function(nameof(OkHttpStatusCode))]
public HttpResponseData OkHttpStatusCode([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    var response = req.CreateResponse(HttpStatusCode.OK);
    response.WriteString("Welcome to Azure Functions!");

    return response;
}

[Function(nameof(BadRequestHttpStatusCode))]
public HttpResponseData BadRequestHttpStatusCode([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    var response = req.CreateResponse();
    response.WriteString("Welcome to Azure Functions!");
    response.StatusCode = HttpStatusCode.BadRequest;

    return response;
}

[Function(nameof(InternalErrorHttpStatusCode))]
public HttpResponseData InternalErrorHttpStatusCode([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
    _logger.LogInformation("C# HTTP trigger function processed a request.");

    var response = req.CreateResponse(HttpStatusCode.InternalServerError);
    response.WriteString("Welcome to Azure Functions!");

    return response;
}

When running those endpoints this is what we get. What we expect!

HTTP200

HTTP400

HTTP500

Check the repository example on PlayGoKids repository.