在ServiceStack + MVC Web应用程序中设置特定于用户的文化

时间:2014-06-10 17:56:12

标签: asp.net-mvc-4 authentication servicestack servicestack-bsd

我需要为使用ServiceStack 3MVC 4发送到我的Web应用程序的每个Web请求设置特定于用户的文化。

每个用户的文化都存储在他们在数据库中的配置文件中,我使用从IAuthSession派生的自定义身份验证提供程序将其检索到我自己的CredentialsAuthProvider实现中。所以我不关心浏览器的AcceptLanguage标头,而是想在ServiceStack从缓存中解析后立即将当前线程的文化设置为auth会话的Culture属性。这必须发生在ServiceStack服务和MVC控制器(从ServiceStackController派生)上。

实现上述目标的最佳方法是什么?

更新1

我找到了一种方法,虽然我不相信这是最佳解决方案。

在我的所有服务派生的基本服务类中,我覆盖了SessionAs<>属性,如下所示:

protected override TUserSession SessionAs<TUserSession>()
{
    var genericUserSession = base.SessionAs<TUserSession>();

    var userAuthSession = genericUserSession as UserAuthSession;
    if (userAuthSession != null && !String.IsNullOrWhiteSpace(userAuthSession.LanguageCode))
        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(userAuthSession.LanguageCode);

    return genericUserSession;
}

其中UserAuthSession是我自定义的ServiceStack IAuthSession实现。其LanguageCode属性在登录时设置为用户选择的ISO文化代码,存储在数据库中用户的配置文件中。

同样,在我的所有控制器派生的基本控制器类中,我像这样覆盖了AuthSession属性:

public override IAuthSession AuthSession
{
    get
    {
        var userAuthSession = base.AuthSession as UserAuthSession;
        if (userAuthSession != null && !String.IsNullOrWhiteSpace(userAuthSession.LanguageCode))
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(userAuthSession.LanguageCode);
        return userAuthSession;
    }
}

这似乎工作正常,因为无论何时调用服务或执行控制器操作,都会始终使用这两个属性,因此在执行任何下游逻辑之前,将设置当前线程的文化。

如果有人能想出更好的方法,请告诉我。

更新2

根据Scott的建议,我创建了一个自定义AuthenticateAndSetCultureAttribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthenticateAndSetCultureAttribute : AuthenticateAttribute
{
    public AuthenticateAndSetCultureAttribute() : base() { }
    public AuthenticateAndSetCultureAttribute(ApplyTo applyTo) : base(applyTo) { }
    public AuthenticateAndSetCultureAttribute(string provider) : base(provider) { }
    public AuthenticateAndSetCultureAttribute(ApplyTo applyTo, string provider) : base(applyTo, provider) { }

    public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
    {
        base.Execute(req, res, requestDto);

        var session = req.GetSession() as UserAuthSession;
        if (session != null && session.IsAuthenticated && !String.IsNullOrWhiteSpace(session.LanguageCode))
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(session.LanguageCode);
    }
}

因为我只是在用户通过身份验证时更改了文化,所以在我们检查身份验证的同一个地方(无论如何)都有意义。

然后,我使用此属性而不是原始[Authenticate]来装饰我的所有SS服务和MVC控制器。

现在,当调用SS服务时,将执行属性的Execute方法,并正确设置文化。但是,在调用MVC控制器操作时,Execute永远不会被执行,这实在令人费解,因为MVC + SS知道如何将未经身份验证的请求重定向到登录页面。

任何想法,任何人?

2 个答案:

答案 0 :(得分:3)

我会使用RequestFilter执行此操作,而不是覆盖SessionAs<T>。在AppHost Configure方法中:

public override void Configure(Container container)
{
    RequestFilters.Add((httpReq, httpResp, requestDto) => {
        var session = httpReq.GetSession() as UserAuthSession;
        if(session == null || !session.IsAuthenticated || String.IsNullOrWhiteSpace(session.LanguageCode))
            return;

        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(session.LanguageCode);
    });
}

答案 1 :(得分:1)

我最终创建了一个自定义MVC操作过滤器,根据经过身份验证的用户设置设置请求线程的文化:

public class SetUserCultureAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        var baseController = filterContext.Controller as BaseController;
        if (baseController == null) return;

        var userAuthSession = baseController.UserAuthSession;
        if (userAuthSession != null && userAuthSession.IsAuthenticated && !String.IsNullOrWhiteSpace(userAuthSession.LanguageCode))
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(userAuthSession.LanguageCode);
    }
}

然后我使用此属性修饰了我的BaseController类,并使用ServiceStack的常规Authorize属性保护了我的控制器/操作。

我原本打算为控制器和服务工作的先前创建的AuthenticateAndSetCultureAttribute现在仅用于SS服务。

MVC和SS方面的文化设置正确,所以我很高兴!