Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject HttpHeader value in controller?

I have Web API developed using ASP.NET Core API. Every incoming request has a custom header value inserted. eg x-correlationid. The controller use this value for logging and tracing the request. Currently I'm reading the value in each controller as below

[Route("api/[controller]")]
public class DocumentController : Controller
{
    private ILogger<TransformController> _logger;
    private string _correlationid = null;

    public DocumentController(ILogger<DocumentController > logger)
    {
        _logger = logger;
        _correlationid = HttpContext.Request.Headers["x-correlationid"];
    }

    [HttpPost]
    public async Task<intTransform([FromBody]RequestWrapper request)
    {
        _logger.LogInformation("Start task. CorrelationId:{0}", _correlationid);

         // do something here

        _logger.LogInformation("End task. CorrelationId:{0}", _correlationid);

        return result;
    }
}

I think this is against DI rules.

Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
Or
Can middleware read the x-correlationid and *somehow* make it available to all the controllers so we don't have to inject it in any controller?

What would be a better option here?

like image 259
LP13 Avatar asked Sep 12 '16 22:09

LP13


People also ask

How can you obtain a RequestHeader in a controller method?

First, we used the @RequestHeader annotation to supply request headers to our controller methods. After checking out the basics, we took a detailed look at the attributes for the @RequestHeader annotation. The example code is available over on GitHub.

How do I read header values in spring boot?

To read individual HTTP header in Spring, we can use the @RequestHeader annotation and specify the header name as the parameter. Let's take an example where we want to read the "accept-language" header information in our controller.

How do I read header values in Web API?

As shown above, the header value can be easily read through the HttpContext object. Please add below generic logic to read through any of the custom headers. HttpContext will be accessible through the WebAPI pipeline and can be available through middleware (as shown in the above example) or .


Video Answer


3 Answers

Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.

You can't inject the value itself into the constructor of the api controller, because at the time of construction the HttpContext is going to be null.

One "injection-style" option would be to use the FromHeaderAttribute in your actions:

[HttpPost]
public async Task<int> Transform(
    [FromBody]RequestWrapper request,
    [FromHeader(Name="x-correlationid")] string correlationId)
{
    return result;
}

Can middleware read the x-correlationid and somehow make it available to all the controllers so we don't have to inject it in any controller?

I think a middleware solution would probably be overkill for what you need. Instead, you can create a custom base class that derives from Controller and have all your Api controllers derive from that.

public class MyControllerBase : Controller
{
    protected string CorrelationId =>
        HttpContext?.Request.Headers["x-correlationid"] ?? string.Empty;
}

[Route("api/[controller]")]
public class DocumentController : MyControllerBase 
{
    private ILogger<TransformController> _logger;

    public DocumentController(ILogger<DocumentController> logger)
    {
        _logger = logger;
    }

    [HttpPost]
    public async Task<intTransform([FromBody]RequestWrapper request)
    {
        _logger.LogInformation($"Start task. CorrelationId:{CorrelationId}");

        // do something here

        _logger.LogInformation($"End task. CorrelationId:{CorrelationId}");
        return result;
    }
}
like image 190
Will Ray Avatar answered Sep 22 '22 06:09

Will Ray


This is what I came up with. I think i can also unit test it.

public interface IRequestContext
{
    string CorrelationId { get; }
}

public sealed class RequestContextAdapter : IRequestContext
{
    private readonly IHttpContextAccessor _accessor;
    public RequestContextAdapter(IHttpContextAccessor accessor)
    {
        this._accessor = accessor;
    }

    public string CorrelationId
    {
        get
        {
            return this._accessor.HttpContext.Request.Headers[Constants.CORRELATIONID_KEY];
        }
    }
}

then in startup's configureservice method register the adapter

 services.AddSingleton<IRequestContext, RequestContextAdapter>();

and inject it in controller

[Route("api/[controller]")]
public class DocumentController : Controller
{
    private ILogger<TransformController> _logger;
    private IRequestContext _requestContext = null;

    public DocumentController(ILogger<DocumentController > logger,IRequestContext requestContext)
    {
        _logger = logger;
        _requestContext = requestContext;
    }

    [HttpPost]
    public async Task<intTransform([FromBody]RequestWrapper request)
    {
        _logger.LogInformation("Start task. CorrelationId:{0}", _requestContext.CorrelationId);

         // do something here

        _logger.LogInformation("End task. CorrelationId:{0}", _requestContext.CorrelationId);

        return result;
    }
}
like image 20
LP13 Avatar answered Sep 20 '22 06:09

LP13


Depending on your needs one of following is suitable:

  • If you need your header values at action level, then using FromHeaderAttribute sounds better (lighter and easier).
  • If you need to use this header value in lower layers like Repository or DAL, which will be instantiated before Controller has been initialized, then consider to use middleware to get header values initialized and available for other components.
like image 33
Imran Javed Avatar answered Sep 19 '22 06:09

Imran Javed