14 KiB
Caller Header 接收改造实施计划
概述
根据《后端Caller字段接收清单.md》的要求,需要在 11 个 API 接口中从 HTTP Header 读取 Caller 字段(请求人姓名)。
核心业务含义:Caller 记录了该次请求是由谁发起的,对于创建类接口需要写入系统的创建人字段,对于所有接口都需要通过 Serilog 记录审计日志。
扩展性设计:采用 Middleware 统一提取 + RequestTrackingContext 建模的方案。后续新增 Device-Id、Request-Id 等 Header 时,只需:
- 在
RequestTrackingContext类中加一个属性 - 在 Middleware 中加一行读取代码
无需修改任何 Controller。
架构设计
HTTP Request (Header: Caller, Device-Id, Request-Id, ...)
│
▼
┌─────────────────────────┐
│ RequestTrackingMiddleware │ ← 统一提取所有追踪 Header
│ 存入 HttpContext.Items │ 写入 RequestTrackingContext
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Controller Action │ ← HttpContext.GetRequestTrackingContext().Caller
│ - A 类:读取 + 记录日志 │ _logger.LogInformation(...)
│ - B 类:读取 + 写创建人 │ entity.Creator = ...GetCaller();
└─────────────────────────┘
涉及的文件
| # | 文件 | 操作 | 说明 |
|---|---|---|---|
| 1 | src/CONTROLLER/Models/RequestTrackingContext.cs |
新建 | 追踪上下文模型 |
| 2 | src/CONTROLLER/Middleware/RequestTrackingMiddleware.cs |
新建 | 统一提取 Header |
| 3 | src/CONTROLLER/Extensions/HttpContextExtensions.cs |
新建 | HttpContext 扩展方法 |
| 4 | src/CONTROLLER/Program.cs |
修改 | 注册中间件 |
| 5 | src/CONTROLLER/Controllers/LabelController.cs |
修改 2 个方法 | 已有 Logger |
| 6 | src/CONTROLLER/Controllers/BagTagController.cs |
修改 4 个方法 | 需注入 Logger |
| 7 | src/CONTROLLER/Controllers/ShippingHandoverFormController.cs |
修改 3 个方法 | 需注入 Logger |
| 8 | src/CONTROLLER/Controllers/ShippingHandoverFormBagTagController.cs |
修改 1 个方法 | 需注入 Logger |
接口改造分类
A 类:仅记录 Caller 日志(7 个读操作接口)
| # | 方法 | 端点 |
|---|---|---|
| 1 | GET | api/label/label-replace/waybill/{number}/download |
| 2 | GET | api/label/label-replace/waybill/{number}/downloadNoTri |
| 3 | GET | api/bagtag/{tagNumber}/print |
| 4 | GET | api/shipping-handover/{bolNumber}/print |
| 7 | GET | api/shipping-handover/generate-number |
| 9 | GET | api/bagtag/available |
| 10 | GET | api/shipping-handover/bag-tag/associate-by-number/{id} |
B 类:记录日志 + 写入系统创建人字段(3 个创建/操作接口)
| # | 方法 | 端点 | 当前代码 | 改造内容 |
|---|---|---|---|---|
| 5 | POST | api/bagtag/generate |
creator 硬编码 "system" |
→ 从 Caller Header 读取 |
| 6 | POST | api/bagtag/auto-pack/start |
request.Creator 从请求体取 |
→ 用 Caller Header 覆盖 |
| 8 | GET | api/shipping-handover/create |
Creator 从 URL 参数取 |
→ 用 Caller Header 覆盖 |
实施步骤
步骤 1:创建 RequestTrackingContext 模型
src/CONTROLLER/Models/RequestTrackingContext.cs
namespace CONTROLLER.Models
{
public class RequestTrackingContext
{
public string Caller { get; set; } = "system";
// 后续扩展预留:
// public string DeviceId { get; set; }
// public string RequestId { get; set; }
}
}
-
所有追踪 Header 的值集中在一个模型中
-
默认值
"system"作为回退 -
后续新增 Header:添加属性 → Middleware 中加一行读取 → 完成
步骤 2:创建 RequestTrackingMiddleware 中间件
src/CONTROLLER/Middleware/RequestTrackingMiddleware.cs
using CONTROLLER.Models;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.Threading.Tasks;
namespace CONTROLLER.Middleware
{
public class RequestTrackingMiddleware
{
private readonly RequestDelegate _next;
public RequestTrackingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var trackingContext = new RequestTrackingContext();
var caller = context.Request.Headers["Caller"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(caller))
{
trackingContext.Caller = caller;
}
// 后续扩展只需加一行:
// var deviceId = context.Request.Headers["Device-Id"].FirstOrDefault();
// if (!string.IsNullOrWhiteSpace(deviceId)) trackingContext.DeviceId = deviceId;
context.Items["RequestTrackingContext"] = trackingContext;
await _next(context);
}
}
}
-
在管道最前端统一提取所有追踪 Header
-
存入
HttpContext.Items["RequestTrackingContext"] -
仅在 Header 有值时才覆盖默认值(保持回退逻辑)
步骤 3:创建 HttpContextExtensions 扩展方法
src/CONTROLLER/Extensions/HttpContextExtensions.cs
using CONTROLLER.Models;
using Microsoft.AspNetCore.Http;
namespace CONTROLLER.Extensions
{
public static class HttpContextExtensions
{
public static RequestTrackingContext GetRequestTrackingContext(this HttpContext context)
{
return context.Items["RequestTrackingContext"] as RequestTrackingContext
?? new RequestTrackingContext();
}
public static string GetCaller(this HttpContext context)
{
return context.GetRequestTrackingContext().Caller;
}
}
}
-
GetRequestTrackingContext()— 获取完整上下文(支持未来扩展) -
GetCaller()— 便捷方法,直接获取调用人
步骤 4:在 Program.cs 中注册中间件
在 app.UseResponseCompression(); 之前(或其他合适位置)添加:
app.UseMiddleware<RequestTrackingMiddleware>();
需要添加 using:
using CONTROLLER.Middleware;
步骤 5:改造 LabelController.cs(A 类:2 个接口)
已有 ILogger<LabelController> _logger,添加 using + 在方法入口处记录 Caller 日志:
添加 using:
using CONTROLLER.Extensions;
接口 #1 — DownloadLabelByWaybillNumber:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] DownloadLabel, WaybillNumber: {WaybillNumber}", caller, waybillNumber);
接口 #2 — DownloadLabelByWaybillNumberNoTrigger:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] DownloadLabelNoTrigger, WaybillNumber: {WaybillNumber}", caller, waybillNumber);
步骤 6:改造 BagTagController.cs(A 类 2 个 + B 类 2 个)
前置改造:注入 ILogger<BagTagController>:
-
添加
using Microsoft.Extensions.Logging;和using CONTROLLER.Extensions; -
添加构造函数参数和私有字段
_logger
A 类 — 接口 #3 PrintBagTag:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] PrintBagTag, TagNumber: {TagNumber}", caller, tagNumber);
B 类 — 接口 #5 GenerateBagTags:
-
当前:
var creator = /*...*/ "system";(硬编码) -
改为:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] GenerateBagTags, Channel: {Channel}, Count: {Count}", caller, request.ChannelName, request.Count);
var generatedTags = await _bagTagService.GenerateBagTagsAsync(request.ChannelName, request.Count, caller);
B 类 — 接口 #6 StartAutoPack:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] StartAutoPack, TagNumber: {TagNumber}", caller, request.TagNumber);
request.Creator = caller;
var result = await _bagTagService.StartAutoPackAsync(request.TagNumber, request.Creator);
A 类 — 接口 #9 GetAvailableBagTags:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] GetAvailableBagTags, Channel: {Channel}", caller, channel);
步骤 7:改造 ShippingHandoverFormController.cs(A 类 2 个 + B 类 1 个)
前置改造:注入 ILogger<ShippingHandoverFormController>:
-
添加
using Microsoft.Extensions.Logging;和using CONTROLLER.Extensions; -
添加构造函数参数和私有字段
_logger
B 类 — 接口 #8 CreateShippingHandoverForm:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] CreateShippingHandoverForm, HandoverNumber: {Number}, Channel: {Channel}", caller, HandoverNumber, Channel);
// 用 Caller 覆盖 Creator,Caller 为空时回退为 "system"
form.Creator = caller;
A 类 — 接口 #7 GenerateShippingHandoverNumber:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] GenerateShippingHandoverNumber, Channel: {Channel}", caller, channel);
A 类 — 接口 #4/#11 PrintBillOfLading:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] PrintBillOfLading, BolNumber: {BolNumber}", caller, bolNumber);
步骤 8:改造 ShippingHandoverFormBagTagController.cs(A 类:1 个接口)
前置改造:注入 ILogger<ShippingHandoverFormBagTagController>:
-
添加
using Microsoft.Extensions.Logging;和using CONTROLLER.Extensions; -
添加构造函数参数和私有字段
_logger
A 类 — 接口 #10 AssociateBagTagsByNumber:
var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] AssociateBagTagsByNumber, ShippingHandoverFormId: {Id}, TagNumbers: {Tags}", caller, shippingHandoverFormId, bagTagNumbers);
步骤 9:验证
dotnet build src/CONTROLLER/CONTROLLER.csproj
未来扩展示例
假设后续要加 Device-Id 和 Request-Id 两个 Header:
只需修改 2 个文件:
RequestTrackingContext.cs— 加 2 个属性:
public string DeviceId { get; set; }
public string RequestId { get; set; }
RequestTrackingMiddleware.cs— 加 2 行:
var deviceId = context.Request.Headers["Device-Id"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(deviceId)) trackingContext.DeviceId = deviceId;
var requestId = context.Request.Headers["Request-Id"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(requestId)) trackingContext.RequestId = requestId;
Controller 中可直接使用:
var ctx = HttpContext.GetRequestTrackingContext();
_logger.LogInformation("Caller: {C}, Device: {D}, Request: {R}", ctx.Caller, ctx.DeviceId, ctx.RequestId);
无需修改任何 Controller 的业务逻辑。
B 类接口改造对照表(写入创建人字段)
| # | 方法 | 当前代码 | 改造后代码 |
|---|---|---|---|
| 5 | GenerateBagTags |
var creator = "system"; |
var caller = HttpContext.GetCaller(); 传入 service |
| 6 | StartAutoPack |
request.Creator |
request.Creator = HttpContext.GetCaller(); |
| 8 | CreateShippingHandoverForm |
form.Creator = Creator; |
form.Creator = HttpContext.GetCaller(); |
A 类接口日志格式
[Caller: zhangsan] DownloadLabel, WaybillNumber: YW202605270001
[Caller: system] PrintBagTag, TagNumber: BT202605270001
[Caller: zhangsan] PrintBillOfLading, BolNumber: BOL202605270001
改造汇总
| 步骤 | 文件 | 操作 | A 类 | B 类 |
|---|---|---|---|---|
| 1 | Models/RequestTrackingContext.cs |
新建 | — | — |
| 2 | Middleware/RequestTrackingMiddleware.cs |
新建 | — | — |
| 3 | Extensions/HttpContextExtensions.cs |
新建 | — | — |
| 4 | Program.cs |
注册中间件 | — | — |
| 5 | LabelController.cs |
修改 2 个方法 | 2 | 0 |
| 6 | BagTagController.cs |
添加 Logger + 修改 4 个方法 | 2 | 2 |
| 7 | ShippingHandoverFormController.cs |
添加 Logger + 修改 3 个方法 | 2 | 1 |
| 8 | ShippingHandoverFormBagTagController.cs |
添加 Logger + 修改 1 个方法 | 1 | 0 |
| 9 | 构建验证 | dotnet build |
— | — |
| 合计 | 8 个文件 | 7 | 3 |