1

我有一个带有自定义数据库路由的应用程序:

routes.Add("RouteWeb", new RouteWeb());

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    if (httpContext.Request.IsAjaxRequest() || httpContext.Request.Url == null) return null;

    var page = FindPageFromDataBase(httpContext.Request.Url);
    if (page == null) return null;

    var pageResult = new RouteData(this, new MvcRouteHandler());
    pageResult.Values["culture"] = page.Culture;
    pageResult.Values["controller"] = page.controller;
    pageResult.Values["action"] = page.action;

    return pageResult;
}

如您所见,我从数据库中获取页面,因此该站点的管理员可以更改页面的路由(www.site.com/page -> www.site.com/other-name)并且该站点使用新的姓名。

在数据库中,我检索页面的文化,因为每个页面都可能具有不同的文化,例如,如果您访问 www.site.com/page 它会获取英文内容,而如果您访问 www.site.com /pagina 它以西班牙语显示内容。

当用户可以使用日期过滤页面时,除了一个细节之外,这非常有效。

@using (Ajax.BeginForm(null, null, new AjaxOptions { HttpMethod = FormMethod.Get.ToString(), InsertionMode = InsertionMode.Replace, UpdateTargetId = "content_list", Url = Url.Action("UsersItems", "Users"), OnComplete = "OnComplete" }, new { id = "formSearch" }))
{
    ...
     @Html.DatePickerRangeFor(model => model.DateFrom, new { @class = "form-control", Value = Model.DateFrom == null ? "" : Convert.ToDateTime(Model.DateFrom).ToShortDateString()})
}

我必须使用自定义 ModelBinder 根据所选页面的语言将日期更改为正确的格式。

public class NullableDateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var cultureInfo = new CultureInfo(controllerContext.RouteData.Values["culture"].ToString());
        System.Threading.Thread.CurrentThread.CurrentUICulture = cultureInfo;
        System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);

        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        return value?.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
    }
}

我的问题是

controllerContext.RouteData.Values["culture"]

始终为空。当我第一次加载页面时,RouteData 获取了文化,但是在执行部分 ajax 请求时,所有值都消失了,并且 ModelBinder 给了我一个错误。

我不想将当前文化存储在会话变量中,因为我从数据库页面中读取了语言,并且只有 ajax 请求加载部分视图时才有问题。

有没有办法将当前页面的文化传递给 ModelBinder?

提前致谢。

4

1 回答 1

1

与其在 a 中设置当前文化,ModelBinder不如IAuthorizationFilter为此目的使用 a 。请注意,IActionFilters 在之后运行,ModelBinderIAuthorizationFilters在.之前ModelBinder运行。

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

public class CultureFilter : IAuthorizationFilter
{
    private readonly string defaultCulture;

    public CultureFilter(string defaultCulture)
    {
        this.defaultCulture = defaultCulture;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var values = filterContext.RouteData.Values;

        string culture = (string)values["culture"] ?? this.defaultCulture;

        CultureInfo ci = new CultureInfo(culture);

        Thread.CurrentThread.CurrentCulture = ci;
        Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);
    }
}

您可以通过将过滤器注册为全局过滤器来全局设置过滤器。

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CultureFilter(defaultCulture: "en"));
        filters.Add(new HandleErrorAttribute());
    }
}

由于它在运行之前ModelBinder运行,因此无需更改内部文化,ModelBinder因为它将从请求中获取环境文化。所以你可以完全消除这种习惯ModelBinder

当我第一次加载页面时,RouteData 获取了文化,但是在执行部分 ajax 请求时,所有值都消失了

这是因为每当 AJAX 请求进来时,您都会跳过您的路线:

if (httpContext.Request.IsAjaxRequest() || httpContext.Request.Url == null) return null;

你应该消除那条线。

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var page = FindPageFromDataBase(httpContext.Request.Url);
    if (page == null) return null;

    var pageResult = new RouteData(this, new MvcRouteHandler());
    pageResult.Values["culture"] = page.Culture;
    pageResult.Values["controller"] = page.controller;
    pageResult.Values["action"] = page.action;

    return pageResult;
}

另请注意,您可能会获得文化的空值,因为它未在您的默认路由中设置。您应该为每个无法通过 URL 传递文化的路由设置默认文化,并为每个可以通过文化的路由省略默认文化(这使得路由需要 URL 段匹配)。

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.Add(new RouteWeb());

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { culture = "en", controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

有关的:

于 2018-01-20T18:14:45.017 回答