如何在Domain项目静态类中安全获取当前用户语言?
解决方案
方案1:依赖注入请求作用域的文化提供器
在Domain层定义抽象的文化获取接口,Api层在请求作用域内实现该接口(从HttpContext读取用户语言),再将实现注入到Domain的验证器中。这种方式遵循依赖倒置原则,Domain层完全不依赖Api层的具体类型。
步骤:
- Domain层定义接口:
// Domain/Interfaces/ICultureProvider.cs namespace Domain.Interfaces; public interface ICultureProvider { CultureInfo GetCurrentCulture(); }
- Api层实现接口:
// Api/Services/RequestCultureProvider.cs using Domain.Interfaces; using Microsoft.AspNetCore.Http; namespace Api.Services; public class RequestCultureProvider : ICultureProvider { private readonly IHttpContextAccessor _httpContextAccessor; public RequestCultureProvider(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public CultureInfo GetCurrentCulture() { // 从请求头获取用户文化,可替换为用户配置等自定义逻辑 var langHeader = _httpContextAccessor.HttpContext?.Request.Headers["Accept-Language"].FirstOrDefault(); return string.IsNullOrEmpty(langHeader) ? CultureInfo.CurrentUICulture : new CultureInfo(langHeader); } }
- Api层注册服务:
在Program.cs中注册请求作用域的实现:
builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped<ICultureProvider, RequestCultureProvider>();
- Domain层验证器注入使用:
// Domain/Validators/UserValidator.cs using Domain.Entities; using Domain.Interfaces; using FluentValidation; using System.Resources; namespace Domain.Validators; public class UserValidator : AbstractValidator<User> { private readonly ResourceManager _resourceManager; private readonly ICultureProvider _cultureProvider; public UserValidator(ICultureProvider cultureProvider) { _cultureProvider = cultureProvider; _resourceManager = new ResourceManager("Domain.Resources.ValidationMessages", typeof(UserValidator).Assembly); RuleFor(x => x.Email) .NotEmpty().WithMessage(GetLocalizedMessage("EmailRequired")) .EmailAddress().WithMessage(GetLocalizedMessage("EmailInvalid")); } private string GetLocalizedMessage(string key) { var culture = _cultureProvider.GetCurrentCulture(); return _resourceManager.GetString(key, culture) ?? key; } }
优点:完全解耦,符合DDD原则,测试友好(可Mock ICultureProvider);缺点:需要修改验证器构造函数注入接口,对现有代码有一定侵入性。
方案2:验证时显式传递文化参数
在Domain层的验证方法中添加文化参数,Api层调用验证时,将当前请求的文化信息传递进去。这种方式直接简单,Domain层无状态,无需依赖注入。
步骤:
- Domain层修改验证器:
// Domain/Validators/UserValidator.cs using Domain.Entities; using FluentValidation; using System.Resources; namespace Domain.Validators; public class UserValidator : AbstractValidator<User> { private readonly ResourceManager _resourceManager; public UserValidator() { _resourceManager = new ResourceManager("Domain.Resources.ValidationMessages", typeof(UserValidator).Assembly); } // 新增带文化参数的验证方法 public ValidationResult Validate(User user, CultureInfo culture) { RuleFor(x => x.Email) .NotEmpty().WithMessage(GetLocalizedMessage("EmailRequired", culture)) .EmailAddress().WithMessage(GetLocalizedMessage("EmailInvalid", culture)); return base.Validate(user); } private string GetLocalizedMessage(string key, CultureInfo culture) { return _resourceManager.GetString(key, culture) ?? key; } }
- Api层调用验证时传递文化:
// Api/Controllers/UserController.cs using Domain.Entities; using Domain.Validators; using Microsoft.AspNetCore.Mvc; namespace Api.Controllers; [ApiController] [Route("api/users")] public class UserController : ControllerBase { [HttpPost] public IActionResult CreateUser([FromBody] User user) { // 从HttpContext获取当前文化 var culture = HttpContext.Request.GetTypedHeaders().AcceptLanguage.FirstOrDefault()?.Value ?? CultureInfo.CurrentUICulture; var validator = new UserValidator(); var result = validator.Validate(user, culture); if (!result.IsValid) { return BadRequest(result.Errors.Select(e => e.ErrorMessage)); } // 后续业务逻辑... return Ok(); } }
优点:简单直接,Domain层无需依赖外部服务;缺点:每次验证都要手动传递文化参数,易遗漏,存在代码重复。
方案3:使用AsyncLocal存储请求级文化信息
AsyncLocal
步骤:
- Domain层定义静态文化上下文类:
// Domain/Helpers/CultureContext.cs namespace Domain.Helpers; public static class CultureContext { private static readonly AsyncLocal<CultureInfo?> _currentCulture = new(); public static CultureInfo CurrentCulture { get => _currentCulture.Value ?? CultureInfo.CurrentUICulture; set => _currentCulture.Value = value; } }
- Api层添加中间件设置文化:
// Api/Middleware/CultureMiddleware.cs using Domain.Helpers; namespace Api.Middleware; public class CultureMiddleware { private readonly RequestDelegate _next; public CultureMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { // 从HttpContext获取用户文化 var langHeader = context.Request.Headers["Accept-Language"].FirstOrDefault(); var culture = string.IsNullOrEmpty(langHeader) ? CultureInfo.CurrentUICulture : new CultureInfo(langHeader); // 设置AsyncLocal的值 CultureContext.CurrentCulture = culture; try { await _next(context); } finally { // 清理上下文,避免污染后续请求(可选,但更安全) CultureContext.CurrentCulture = CultureInfo.CurrentUICulture; } } }
- Api层注册中间件:
在Program.cs中添加中间件(顺序需在路由之前):
app.UseMiddleware<CultureMiddleware>();
- Domain层验证器使用:
// Domain/Validators/UserValidator.cs using Domain.Entities; using Domain.Helpers; using FluentValidation; using System.Resources; namespace Domain.Validators; public class UserValidator : AbstractValidator<User> { private readonly ResourceManager _resourceManager; public UserValidator() { _resourceManager = new ResourceManager("Domain.Resources.ValidationMessages", typeof(UserValidator).Assembly); RuleFor(x => x.Email) .NotEmpty().WithMessage(GetLocalizedMessage("EmailRequired")) .EmailAddress().WithMessage(GetLocalizedMessage("EmailInvalid")); } private string GetLocalizedMessage(string key) { var culture = CultureContext.CurrentCulture; return _resourceManager.GetString(key, culture) ?? key; } }
优点:对现有代码侵入性小,无需修改验证器构造函数;缺点:依赖静态类,测试时需手动设置CultureContext.CurrentCulture,灵活性不如依赖注入。
内容的提问来源于stack exchange,提问作者Daniel Geissbühler
相关产品推荐
相关产品推荐

