DEV Community

Cover image for How to access the HttpContext in .NET API
Davide Bellone
Davide Bellone

Posted on • Originally published at code4it.dev

How to access the HttpContext in .NET API

If you are building an application that is exposed on the Web, you will probably need to read some data from the current HTTP Request or set some values on the HTTP Response.

In a .NET API, all the info related to both HTTP Request and HTTP Response is stored in a global object called HttpContext. How can you access it?

In this article, we will learn how to get rid of the old HttpContext.Current and what we can do to write more testable code.

Why not HttpContext directly

Years ago, we used to access the HttpContext directly in our code.

For example, if we had to access the Cookies collection, we used to do

var cookies = HttpContext.Current.Request.Cookies;
Enter fullscreen mode Exit fullscreen mode

It worked, right. But this approach has a big problem: it makes our tests hard to set up.

In fact, we were using a static instance that added a direct dependency between the client class and the HttpContext.

That's why the .NET team has decided to abstract the retrieval of that class: we now need to use IHttpContextAccessor.

Add IHttpContextAccessor

Now, I have this .NET project that exposes an endpoint, /WeatherForecast, that returns the current weather for a particular city, whose name is stored in the HTTP Header "data-location".

The real calculation (well, real... everything's fake, here ๐Ÿ˜…) is done by the WeatherService. In particular, by the GetCurrentWeather method.

public WeatherForecast GetCurrentWeather()
{
    string currentLocation = GetLocationFromContext();

    var rng = new Random();

    return new WeatherForecast
    {
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)],
        Location = currentLocation
    };
}
Enter fullscreen mode Exit fullscreen mode

We have to retrieve the current location.

As we said, we cannot anymore rely on the old HttpContext.Current.Request.

Instead, we need to inject IHttpContextAccessor in the constructor, and use it to access the Request object:

public WeatherService(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}
Enter fullscreen mode Exit fullscreen mode

Once we have the instance of IHttpContextAccessor, we can use it to retrieve the info from the current HttpContext headers:

string currentLocation = "";

if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue("data-location", out StringValues locationHeaders) && locationHeaders.Any())
{
    currentLocation = locationHeaders.First();
}

return currentLocation;
Enter fullscreen mode Exit fullscreen mode

Easy, right? We're almost done.

Configure Startup class

If you run the application in this way, you will not be able to access the current HTTP request.

That's because we haven't specified that we want to add IHttpContextAccessor as a service in our application.

To do that, we have to update the ConfigureServices class by adding this instruction:

services.AddHttpContextAccessor();
Enter fullscreen mode Exit fullscreen mode

Which comes from the Microsoft.Extensions.DependencyInjection namespace.

Now we can run the project!

If we call the endpoint specifying a City in the data-location header, we will see its value in the returned WeatherForecast object, in the Location field:

Location is taken from the HTTP Headers

Further improvements

Is it enough?

Is it really enough?

If we use it this way, every class that needs to access the HTTP Context will have tests quite difficult to set up, because you will need to mock several objects.

In fact, for mocking HttpContext.Request.Headers, we need to create mocks for HttpContext, for Request, and for Headers.

This makes our tests harder to write and understand.

So, my suggestion is to wrap the HttpContext access in a separate class and expose only the methods you actually need.

For instance, you could wrap the access to HTTP Request Headers in the GetValueFromRequestHeader of an IHttpContextWrapper service:

public interface IHttpContextWrapper
{
    string GetValueFromRequestHeader(string key, string defaultValue);
}

Enter fullscreen mode Exit fullscreen mode

That will be the only service that accesses the IHttpContextAccessor instance.

public class HttpContextWrapper : IHttpContextWrapper
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpContextWrapper(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public string GetValueFromRequestHeader(string key, string defaultValue)
    {
        if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(key, out StringValues headerValues) && headerValues.Any())
        {
            return headerValues.First();
        }

        return defaultValue;
    }
}
Enter fullscreen mode Exit fullscreen mode

In this way, you will be able to write better tests both for the HttpContextWrapper class, by focusing on the building of the HttpRequest, and for the WeatherService class, so that you can write tests without worrying about setting up complex structures just for retrieving a value.

But pay attention to the dependency lifescope! HTTP Requests info live within - guess what? - their HTTP Request. So, when defining the dependencies in the Startup class, remember to inject the IHttpContextWrapper as Transient or, even better, as Scoped. If you don't remember the difference, I got you covered here!

Wrapping up

In this article, we've learned that you can access the current HTTP request by using IHttpContextAccessor. Of course, you can use it to update the Response too, for instance by adding an HTTP Header.

Happy coding!

๐Ÿง

Top comments (0)