Подтвердить что ты не робот

MVC 5 Как определить Owin LoginPath с локализованными маршрутами

У меня есть веб-сайт MVC 5 с локализованными маршрутами, определенными как

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

Если культура по умолчанию имеет значение "en-US".


Проблема возникает, когда при запуске я должен определить URL-адрес входа, используя свойство LoginPath, которое устанавливается один раз, и оно всегда будет использовать предоставленное значение, например. культура по умолчанию, если "/en-Us/Account/Login" является указанным значением. Затем я попытался использовать класс UrlHelper в надежде испытать некоторую магию, но результат, очевидно, тот же:

var httpContext = HttpContext.Current;
        if (httpContext == null) {
          var request = new HttpRequest("/", "http://example.com", "");
          var response = new HttpResponse(new StringWriter());
          httpContext = new HttpContext(request, response);
        }

        var httpContextBase = new HttpContextWrapper(httpContext);
        var routeData = new RouteData();
        var requestContext = new RequestContext(httpContextBase, routeData);
        UrlHelper helper = new UrlHelper(requestContext);

        var loginPath = helper.Action("Login", "Account");

        // Enable the application to use a cookie to store information for the signed in user
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,                
            LoginPath = new PathString(loginPath)
        });

Мой вопрос: есть ли способ взломать этот механизм для динамического извлечения текущей культуры или я вынужден установить текущую культуру в файл cookie, и, когда я перенаправлен на страницу входа в систему, используйте значение cookie для установить текущую культуру перед отображением страницы?

Спасибо

4b9b3361

Ответ 1

У меня точно была одна и та же проблема, и я понял способ преодолеть это ограничение.

В параметрах CookieAuthenticationOptions существует свойство "Provider", которое инициализируется с помощью CookieAuthenticationProvider. Это реализует метод под названием ApplyRedirect и делегат OnApplyRedirect. Моя первая идея состояла в том, чтобы перезаписать этот ApplyRedirect и реализовать требуемую логику для обработки локализованных маршрутов. Но, к сожалению, его нельзя переоценить. Передача моей логики в OnApplyRedirect приводит к перезаписыванию поведения по умолчанию. Вы теоретически можете захватить источник этого поведения, скопировать его в свой проект и изменить его в соответствии с вашими потребностями, но это, очевидно, не очень хорошая практика. Во-первых, я решил создать обертку вокруг CookieAuthenticationProvider с двумя точками расширения с использованием делегатов и сохранить поведение по умолчанию, кроме используемого URL-адреса, или упростить работу вокруг оболочки (от спасибо до lafi).

Затем в конфигурации auth я добавил свою собственную логику поставщику:

public void ConfigureAuth(IAppBuilder app)
{
    UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);

    CookieAuthenticationProvider provider = new CookieAuthenticationProvider();

    var originalHandler = provider.OnApplyRedirect;

    //Our logic to dynamically modify the path (maybe needs some fine tuning)
    provider.OnApplyRedirect = context =>
    {
        var mvcContext = new HttpContextWrapper(HttpContext.Current);
        var routeData = RouteTable.Routes.GetRouteData(mvcContext);

        //Get the current language  
        RouteValueDictionary routeValues = new RouteValueDictionary();
        routeValues.Add("lang", routeData.Values["lang"]);

        //Reuse the RetrunUrl
        Uri uri = new Uri(context.RedirectUri);
        string returnUrl = HttpUtility.ParseQueryString(uri.Query)[context.Options.ReturnUrlParameter];
        routeValues.Add(context.Options.ReturnUrlParameter, returnUrl);

        //Overwrite the redirection uri
        context.RedirectUri = url.Action("login", "account", routeValues);
        originalHandler.Invoke(context);
    };

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString(url.Action("login", "account")),
        //Set the Provider
        Provider = provider
    });
}

Смотрите также этот код:

Надеюсь, что это соответствует вашим потребностям.

UPDATE: Для большей путаницы я обновил свой ответ, чтобы использовать усовершенствование @Lafis, а не использовать класс-оболочку для применения расширенного поведения. Пожалуйста, также отдайте кредит @Lafis при повышении.

Ответ 2

Чтобы усилить ответ @martinoss, вы можете достичь того же результата без реализации обертки. Просто скопируйте исходный обработчик, назначьте новый, который реализует вашу логику перенаправления, чтобы изменить context.RedirectionUri, и в конце вызова исходного обработчика.

CookieAuthenticationProvider provider = new CookieAuthenticationProvider();

var originalHandler = provider.OnApplyRedirect;
provider.OnApplyRedirect = context =>
{
    //insert your logic here to generate the redirection URI
    string NewURI = "....";
    //Overwrite the redirection uri
    context.RedirectUri = NewURI;
    originalHandler.Invoke(context);
};

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
   AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
   LoginPath = new PathString(url.Action("Login", "Account")),
   Provider = provider
});

Ответ 3

Как насчет этого:

var cao = new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider { OnApplyRedirect = ApplyRedirect }
        };
app.UseCookieAuthentication(cao);

и

  private static void ApplyRedirect(CookieApplyRedirectContext context)
    {

        UrlHelper _url = new UrlHelper(HttpContext.Current.Request.RequestContext);
        String actionUri = _url.Action("Login", "Account", new { });
        context.Response.Redirect(actionUri);
    }

Ответ 4

Не принимая слишком большую ответственность за формат url и т.д., вы можете сделать что-то вроде следующего

public static void Configuration(IAppBuilder app)
{
    UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString(url.Action("LogOn", "Account", new { area = "Account" })),
        Provider = new CookieAuthenticationProvider
        {
            OnApplyRedirect = context => context.Response.Redirect(context.RedirectUri.Replace(CultureHelper.GetDefaultCulture(), Thread.CurrentUiCulture.Name))
        }
    });
}

Ответ 5

Я улучшил ответ Sentinel, чтобы сохранить обратный URL:

private static void ApplyRedirect(CookieApplyRedirectContext context)
        {
            //use this way to keep return url
            var loginUrl = context.RedirectUri.Insert(
                context.RedirectUri.IndexOf("/Account/Login"),
                "/" + CultureHelper.GetCurrentCulture());

            context.Response.Redirect(loginUrl);
        }

Ответ 6

Я думаю, что я на год опоздал в этом ответе, но главная цель здесь - поделиться знаниями...:)

Я просто нашел ту же проблему в приложении, которое я сейчас разрабатываю. Я изучил количество кода, в котором мы нуждаемся (в предыдущих сообщениях), чтобы исправить это, и я волнуюсь (большая часть кода была сложной и касалась внутренности зверя). Поэтому я попытался найти более легкое решение, и то, что я сделал, добавило следующий маршрут в коллекцию маршрутов:

routes.MapRoute(
            name: "loginRoute",
            url: "account/login",
            defaults:new { culture = "", controller = "account", action = "login", id = UrlParameter.Optional });

Это позволит мне активировать действие входа в контроллер учетной записи, и мой стандартный механизм (переопределение метода BeginExecuteCore от контроллера) может привязать текущую культуру пользовательского интерфейса к URL-адресу.

Я надеюсь, что это поможет кому-то.

Добавлено: Мой стандартный механизм:

protected override IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
    {
        var cultureName = RouteData.Values["culture"] as string;

        var cultureCookie = Request.Cookies["_culture"];
        if (cultureCookie != null && string.IsNullOrEmpty(cultureName))
        {
            cultureName = cultureCookie.Value;
        }

        if (cultureName == null)
            cultureName = Request.UserLanguages != null && Request.UserLanguages.Length > 0 ? Request.UserLanguages[0] : null; 

        cultureName = CultureHelper.GetImplementedCulture(cultureName); 

        if (RouteData.Values["culture"] as string != cultureName)
        {
            RouteData.Values["culture"] = cultureName.ToLowerInvariant(); // lower case too

            var cookie = Request.Cookies["_culture"];
            if (cookie != null)
                cookie.Value = cultureName;   // update cookie value
            else
            {
                cookie = new HttpCookie("_culture") { Value = cultureName, Expires = DateTime.Now.AddYears(1) };
            }
            Response.Cookies.Add(cookie);

            // Redirect user
            Response.RedirectToRoute(RouteData.Values);
        }

        Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

        return base.BeginExecuteCore(callback, state);
    }

Ответ 7

Я нашел намного более простой способ:

UrlHelper _url = new UrlHelper(HttpContext.Current.Request.RequestContext);

public void ConfigureAuth(IAppBuilder app)
{
   String actionUri = _url.Action("Login", "Account", new { });
   String unescapeActionUri = System.Uri.UnescapeDataString(actionUri);

   app.UseCookieAuthentication(new CookieAuthenticationOptions
   {
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
      LoginPath = new PathString(unescapeActionUri)
   });

[...]