You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Domain项目静态类中安全获取当前用户语言?

解决方案

方案1:依赖注入请求作用域的文化提供器

在Domain层定义抽象的文化获取接口,Api层在请求作用域内实现该接口(从HttpContext读取用户语言),再将实现注入到Domain的验证器中。这种方式遵循依赖倒置原则,Domain层完全不依赖Api层的具体类型。

步骤:

  1. Domain层定义接口:
// Domain/Interfaces/ICultureProvider.cs
namespace Domain.Interfaces;

public interface ICultureProvider
{
    CultureInfo GetCurrentCulture();
}
  1. 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);
    }
}
  1. Api层注册服务:
    在Program.cs中注册请求作用域的实现:
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICultureProvider, RequestCultureProvider>();
  1. 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层无状态,无需依赖注入。

步骤:

  1. 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;
    }
}
  1. 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是.NET提供的基于异步上下文的存储机制,比ThreadStatic更安全——它会跟随异步流传递,不会因线程复用导致上下文混乱。Api中间件设置AsyncLocal的值,Domain层从AsyncLocal读取。

步骤:

  1. 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;
    }
}
  1. 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;
        }
    }
}
  1. Api层注册中间件:
    在Program.cs中添加中间件(顺序需在路由之前):
app.UseMiddleware<CultureMiddleware>();
  1. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:13:29