如何在控制器中注入 HttpHeader 值?
How to inject HttpHeader value in controller?
我使用 ASP.NET Core API 开发了 Web API。每个传入请求都插入了一个自定义 header 值。例如 x-correlationid
。控制器使用此值来记录和跟踪请求。
目前我正在读取每个控制器中的值,如下所示
[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;
}
}
我认为这违反了 DI 规则。
我不想在控制器的构造函数中读取值,而是想在控制器的构造函数中注入值。
或者
中间件能否读取 x-correlationid
和 *somehow*
使其对所有控制器可用,这样我们就不必将其注入任何控制器?
这里有什么更好的选择?
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
您不能将值本身注入到 api 控制器的构造函数中,因为在构造时 HttpContext
将成为 null
.
一个 "injection-style" 选项是在您的操作中使用 FromHeaderAttribute
:
[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?
我认为中间件解决方案可能无法满足您的需求。相反,您可以创建一个派生自 Controller
的自定义基础 class,并让您的所有 Api 控制器都派生自它。
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;
}
}
这是我想到的。我想我也可以对其进行单元测试。
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];
}
}
}
然后在启动的配置服务方法中注册适配器
services.AddSingleton<IRequestContext, RequestContextAdapter>();
并将其注入控制器
[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;
}
}
根据您的需要,以下之一是合适的:
- 如果您在操作级别需要 header 值,那么使用 FromHeaderAttribute 听起来更好(更轻便)。
- 如果你需要在底层如Repository或DAL中使用这个header值,它会在Controller初始化之前实例化,然后考虑使用中间件来初始化header值并可用于其他组件。
我使用 ASP.NET Core API 开发了 Web API。每个传入请求都插入了一个自定义 header 值。例如 x-correlationid
。控制器使用此值来记录和跟踪请求。
目前我正在读取每个控制器中的值,如下所示
[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;
}
}
我认为这违反了 DI 规则。
我不想在控制器的构造函数中读取值,而是想在控制器的构造函数中注入值。
或者
中间件能否读取 x-correlationid
和 *somehow*
使其对所有控制器可用,这样我们就不必将其注入任何控制器?
这里有什么更好的选择?
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
您不能将值本身注入到 api 控制器的构造函数中,因为在构造时 HttpContext
将成为 null
.
一个 "injection-style" 选项是在您的操作中使用 FromHeaderAttribute
:
[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?
我认为中间件解决方案可能无法满足您的需求。相反,您可以创建一个派生自 Controller
的自定义基础 class,并让您的所有 Api 控制器都派生自它。
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;
}
}
这是我想到的。我想我也可以对其进行单元测试。
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];
}
}
}
然后在启动的配置服务方法中注册适配器
services.AddSingleton<IRequestContext, RequestContextAdapter>();
并将其注入控制器
[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;
}
}
根据您的需要,以下之一是合适的:
- 如果您在操作级别需要 header 值,那么使用 FromHeaderAttribute 听起来更好(更轻便)。
- 如果你需要在底层如Repository或DAL中使用这个header值,它会在Controller初始化之前实例化,然后考虑使用中间件来初始化header值并可用于其他组件。