1

I'm using ASP.NET Core (MVC)

If I call an endpoint, then this.HttpContext is not null.

Within the same class as my endpoint, if I put a break point in the controller, this.HttpContext is always null.

How do I get the value of HttpContext from the controller?

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class LoginController : ControllerBase
{
    public LoginController()
    {
        var isNull = this.HttpContext; //always null
    }

    [HttpGet]
    public async Task Get()
    {
        var isNull = this.HttpContext; //not null
    }
}

The purpose for this, is on each end point, I want to access some values (which are from a cookie). In NET Framework, I'd store the cookie values in a base class (from within the constructor).

Whilst I can access HTTPContext on each each end point, doing it in the constructor means code it once per class.

The goal is very much about coding this less. I'm hoping I'm not just being lazy

2
  • HttpContext is not available in the constructor. (By design) This might be an XY problem. Commented Aug 15, 2020 at 20:06
  • @Nkosi Added more content Commented Aug 15, 2020 at 20:11

2 Answers 2

1

No, it is not the correct way to do it. you need to use Filter or middleware to do it. HttpContext class is always null in the constructor of a controller

Sample middleware code (for logging)

you can do whatever in this like read cookies or whatnot

public class LoggingMiddleware
{
    private static readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
    private readonly TelemetryClient telemetryClient;
    private IConfiguration configuration;
    private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
    private readonly string appName;
    private readonly bool loggingEnabled;

    private readonly RequestDelegate _next;

    public LoggingMiddleware(RequestDelegate next, IConfiguration config)
    {
        _next = next;
        configuration = config;
        _recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
        telemetryConfiguration.InstrumentationKey =  configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
        telemetryClient = new TelemetryClient(telemetryConfiguration);
        appName = configuration.GetValue<string>("AppName");
        loggingEnabled = configuration.GetValue<bool>("Logging:LogRequestResponse"); 
    }

    public async Task Invoke(HttpContext httpContext)
    {
        if(loggingEnabled)
        {
            await LogRequest(httpContext);
            await LogResponse(httpContext);
        }
    }

    private async Task LogRequest(HttpContext context)
    {
        context.Request.EnableBuffering();

        await using var requestStream = _recyclableMemoryStreamManager.GetStream();
        await context.Request.Body.CopyToAsync(requestStream);

        string correlationId = context.Request.Headers.Keys.FirstOrDefault(h => h.ToLower() == "correlationid");
        if (correlationId == null) correlationId = string.Empty;
        if (context.Request.Path != "/")
        {
            telemetryClient.TrackEvent($"{appName}-RequestMiddleware", new Dictionary<string, string>
            {
                { "AppName", appName },
                { "CorrelationId" , correlationId },
                { "Method" , context.Request.Method },
                { "Scheme", context.Request.Scheme},
                { "Host", context.Request.Host.Value },
                { "Path", context.Request.Path },
                { "QueryString", context.Request.QueryString.Value },
                { "Request Body", ReadStreamInChunks(requestStream) }

            });
        }
        context.Request.Body.Position = 0;
    }

    private static string ReadStreamInChunks(Stream stream)
    {
        const int readChunkBufferLength = 4096;

        stream.Seek(0, SeekOrigin.Begin);

        using var textWriter = new StringWriter();
        using var reader = new StreamReader(stream);

        var readChunk = new char[readChunkBufferLength];
        int readChunkLength;

        do
        {
            readChunkLength = reader.ReadBlock(readChunk,
                                               0,
                                               readChunkBufferLength);
            textWriter.Write(readChunk, 0, readChunkLength);
        } while (readChunkLength > 0);

        return textWriter.ToString();
    }

    private async Task LogResponse(HttpContext context)
    {
        var originalBodyStream = context.Response.Body;

        await using var responseBody = _recyclableMemoryStreamManager.GetStream();
        context.Response.Body = responseBody;

        await _next(context);

        context.Response.Body.Seek(0, SeekOrigin.Begin);
        var text = await new StreamReader(context.Response.Body).ReadToEndAsync();
        context.Response.Body.Seek(0, SeekOrigin.Begin);
        if (context.Request.Path != "/")
        {
            telemetryClient.TrackEvent($"{appName}-ResponseMiddleware", new Dictionary<string, string> {
                {"Scheme", context.Request.Scheme},
                { "AppName", appName },
                {"Host", context.Request.Host.Value},
                {"Path" , context.Request.Path},
                {"QueryString", context.Request.QueryString.Value},
                {"Response Body" , text}
                });
        }
       await responseBody.CopyToAsync(originalBodyStream);
    }
}


// Extension method used to add the middleware to the HTTP request pipeline.
public static class LoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<LoggingMiddleware>();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

No, you can't do it that way, controller constructors is danger zone (unless you know what you're doing) and should be used for DI only.

Instead, you should look at custom middleware: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1

More info on asp.net Core life-cycles: https://www.c-sharpcorner.com/article/asp-net-core-mvc-request-life-cycle/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.