如何在 ServiceStack 中正确本地化 Razor 视图

Posted

技术标签:

【中文标题】如何在 ServiceStack 中正确本地化 Razor 视图【英文标题】:How can I properly localize Razor Views in ServiceStack 【发布时间】:2013-10-30 13:03:43 【问题描述】:

我目前正在从 Accept-Language-HTTP-Header 中获取首选文化并将其存储在 AuthUserSession 中。

在 AppHost.Configure 中:

PreRequestFilters.Add((httpReq, httpResp) =>

    var session = httpReq.GetSession();
    if (session is AuthUserSession)
    
        var auths = ((AuthUserSession)session);
        if (auths.Culture == null)
        
            //parse languages
            var languages = httpReq.Headers["Accept-Language"];
            //auths.Culture = Helpers.CultureHelper.GetBestAcceptLanguageMatch(languages);
            auths.Culture = "en-US";
            httpReq.SaveSession(session, new TimeSpan(0, 20, 0));
        
    
);

我目前在用户偏好的文化中呈现视图的解决方案是从 Razor 视图中更改当前的线程 UICulture:

@inherits ViewPage<LandingPage.ServiceModel.Operations.AskQuestions>

@
    var session = GetSession<ServiceStack.ServiceInterface.Auth.AuthUserSession>();
    var prevCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
    System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(session.Culture);
    //access a Resource
    ViewBag.Title = Resources.AskTitle;

Views Content
@
    System.Threading.Thread.CurrentThread.CurrentUICulture = prevCulture;

这看起来既不雅又笨拙。有什么更好的方法来做到这一点?

*编辑: 我正在寻找两个挂钩点:一个在视图被调用之前,一个在它被渲染之后。这些应该保持对其他请求的干扰为零。

【问题讨论】:

【参考方案1】:

我今天发现自定义 IServiceRunner 提供了正确的钩子(请参阅 https://github.com/ServiceStack/ServiceStack/wiki/Customize-HTTP-Responses)。

正如上面的那样,它只在页面由服务提供时才有效,因为在请求 ContentPage 时甚至不会触及 RequestFilters 和 ServiceRunner。

可能有趣的是,RequestFilter 是另一个挂钩点,它在视图的执行之前被调用。但对于 ResponseFilter 来说似乎也是如此。我尝试在 ResponseFilter 中重置 CurrentUICulture 的小测试导致我的页面未本地化。

AppHost

public override void Configure(Funq.Container container)

    //plugins
    Plugins.Add(new RazorFormat());
    Plugins.Add(new AuthFeature(() =>
        new AuthUserSession(),
        new IAuthProvider[] 
            new BasicAuthProvider()
        ));

    //request filters
    PreRequestFilters.Add((httpReq, httpResp) =>
    
        var session = httpReq.GetSession();
        if (session is AuthUserSession)
        
            var auths = ((AuthUserSession)session);
            if (auths.Culture == null)
            
                var languages = httpReq.Headers["Accept-Language"];
                auths.Culture = Helpers.CultureHelper.GetBestAcceptLanguageMatch(languages);

                httpReq.SaveSession(session, new TimeSpan(0, 20, 0));
            
            httpReq.SetItem("Culture", auths.Culture);
        
    );

    //funq
    var userRep = new InMemoryAuthRepository();
    container.Register<IUserAuthRepository>(userRep);
    container.Register<ICacheClient>(c => new MemoryCacheClient());


//use a custom IServiceRunner
public override ServiceStack.ServiceHost.IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)

    var runner = base.CreateServiceRunner<TRequest>(actionContext);
    return new CultureAwareServiceRunner<TRequest>(this, actionContext);

CultureAwareServiceRunner.cs

public class CultureAwareServiceRunner<T> : ServiceRunner<T>


    public CultureAwareServiceRunner(AppHost appHost, ServiceStack.WebHost.Endpoints.ActionContext actionContext) : 
        base(appHost, actionContext)
     

    public override void OnBeforeExecute(IRequestContext requestContext, T request)
    
        var httpReq = requestContext.Get<IHttpRequest>();
        httpReq.SetItem("PreviousCulture", Thread.CurrentThread.CurrentUICulture);
        string culture = httpReq.GetItem("Culture") as string;
        if (culture != null)
            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture);
        base.OnBeforeExecute(requestContext, request);
    

    public override object OnAfterExecute(IRequestContext requestContext, object response)
    
        var httpReq = requestContext.Get<IHttpRequest>();
        var prevCulture = httpReq.GetItem("PreviousCultureCulture") as CultureInfo;
        if (prevCulture != null)
            Thread.CurrentThread.CurrentUICulture = prevCulture;
        return base.OnAfterExecute(requestContext, response);
    

【讨论】:

检查 github.com/ServiceStack/ServiceStack/wiki/Order-of-Operations 会更早地获得该信息。

以上是关于如何在 ServiceStack 中正确本地化 Razor 视图的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 和 ServiceStack.OrmLite 中使用架构名称

为啥 UserAuthExtensions.PopulateFromMap(session, jwtPayload) 不能在 ServiceStack.Auth 中正确反序列化带有转义的 json 值

如何在 ServiceStack 5.0 项目中使用来自 ServiceStack 4.0 的服务模型?

如何在 ServiceStack 中添加 protobuf

如何在 ServiceStack 中使用 Dapper

ServiceStack.Redis常用操作 - 事务并发锁_转