.NET中如何在API调用前执行解密逻辑且不修改方法签名?
当然可以实现!你完全不用折腾修改API方法的签名,.NET早就给我们准备了优雅的解决方案,能在JSON反序列化之前就完成解密操作。下面给你分享两种最实用的实现方式:
方案一:自定义输入格式化器(Custom Input Formatter)
这是最贴合你需求的方式,因为ASP.NET Core的输入格式化器本身就负责将请求体转换为模型对象。我们只需要扩展它的逻辑,先完成解密再执行反序列化。
实现自定义格式化器
public class EncryptedJsonInputFormatter : TextInputFormatter { private readonly JsonSerializerOptions _jsonOptions; public EncryptedJsonInputFormatter(JsonSerializerOptions jsonOptions) { _jsonOptions = jsonOptions; // 指定我们处理的Content-Type,比如自定义的加密JSON类型 SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/encrypted-json")); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); } public override bool CanRead(InputFormatterContext context) { // 可以根据请求头或其他条件判断是否需要触发解密逻辑 return base.CanRead(context); } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context) { var httpContext = context.HttpContext; using var reader = new StreamReader(httpContext.Request.Body); // 读取加密的请求体字符串 var encryptedContent = await reader.ReadToEndAsync(); // 替换成你实际的解密逻辑(比如AES、RSA等) var decryptedContent = Decrypt(encryptedContent); // 将解密后的字符串反序列化为目标模型(比如TodoItem) var model = JsonSerializer.Deserialize(decryptedContent, context.ModelType, _jsonOptions); return await InputFormatterResult.SuccessAsync(model); } // 示例解密方法,请替换为你的真实加密算法实现 private string Decrypt(string encryptedText) { // 这里只是示例,实际请使用你的解密逻辑 var bytes = Convert.FromBase64String(encryptedText); return Encoding.UTF8.GetString(bytes); } }
注册格式化器
在Program.cs中把自定义格式化器添加到框架的输入格式化器集合中:
builder.Services.AddControllers(options => { // 获取默认的JSON序列化配置 var jsonOptions = options.JsonSerializerOptions; // 将自定义格式化器放在默认JSON格式化器前面,确保优先触发 options.InputFormatters.Insert(0, new EncryptedJsonInputFormatter(jsonOptions)); });
使用方式
发送请求时,将Content-Type设置为application/encrypted-json,框架就会自动调用我们的格式化器完成解密+反序列化,你的原PutTodoItem方法完全不需要修改。
方案二:使用Action Filter(动作过滤器)
这种方式适合需要全局或批量处理多个接口的场景,在请求进入API方法前拦截请求体,解密后再让默认的序列化流程处理。
实现解密过滤器
public class DecryptRequestBodyFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { var request = context.HttpContext.Request; // 启用请求体缓冲(默认请求体只能读取一次) request.EnableBuffering(); using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true); var encryptedContent = reader.ReadToEnd(); // 执行解密逻辑 var decryptedContent = Decrypt(encryptedContent); // 将解密后的内容重新写入请求体,并重置位置到开头 request.Body.Position = 0; using var writer = new StreamWriter(request.Body, Encoding.UTF8, leaveOpen: true); writer.Write(decryptedContent); writer.Flush(); request.Body.Position = 0; } public void OnActionExecuted(ActionExecutedContext context) { // 可在此处做后续清理或日志记录 } // 示例解密方法,请替换为你的真实实现 private string Decrypt(string encryptedText) { var bytes = Convert.FromBase64String(encryptedText); return Encoding.UTF8.GetString(bytes); } }
注册过滤器
可以选择全局注册(对所有API生效)或局部注册(仅对特定控制器/方法生效):
// 全局注册(Program.cs) builder.Services.AddControllers(options => { options.Filters.Add<DecryptRequestBodyFilter>(); }); // 局部注册(控制器或方法上) // [ServiceFilter(typeof(DecryptRequestBodyFilter))] // public class TodoItemsController : ControllerBase // { // // ... // }
方案对比
- 自定义输入格式化器:更规范,适合处理特定Content-Type的加密请求,逻辑边界清晰。
- Action Filter:更灵活,适合全局批量处理解密需求,无需修改请求的Content-Type。
两种方案都能让你保持原有的PutTodoItem方法签名不变,所有解密逻辑都在框架层面处理,代码更整洁易维护。
内容的提问来源于stack exchange,提问作者Kevin Adams
相关产品推荐
相关产品推荐

