登录后如何将路由更改为用户名? [英] How to change route to username after logged in?

查看:24
本文介绍了登录后如何将路由更改为用户名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

用户登录前,路由为:

本地主机:54274/Home本地主机:54274/首页/关于本地主机:54274/首页/联系方式本地主机:54274/首页/登录本地主机:54274/首页/注册

用户登录后,路径为:

1.本地主机:54274/项目2.本地主机:54274/项目/创建3. 本地主机:54274/Project/Edit/14. 本地主机:54274/Project/Delete/25. 本地主机:54274/Project/1/Requirement6. 本地主机:54274/Project/1/Requirement/Create7. 本地主机:54274/Project/1/Requirement/Edit/38.本地主机:54274/Project/1/Requirement/Delete/4

我想在用户登录后把路由改成用户名一次,比如用户名是hendyharf.

1.本地主机:54274/hendyharf/项目2.本地主机:54274/hendyharf/项目/创建3. 本地主机:54274/hendyharf/Project/Edit/14. 本地主机:54274/hendyharf/Project/Delete/25. 本地主机:54274/hendyharf/Project/1/Requirement6. 本地主机:54274/hendyharf/Project/1/Requirement/Create7.本地主机:54274/hendyharf/Project/1/Requirement/Edit/38.本地主机:54274/hendyharf/Project/1/Requirement/Delete/4

我的项目的控制器只有 3 个控制器:HomeControllerProjectControllerRequirementController

我的RouteConfig.cs 仍然是默认的

public static void RegisterRoutes(RouteCollection routes){route.IgnoreRoute("{resource}.axd/{*pathInfo}");路线.MapRoute(名称:默认",url: "{controller}/{action}/{id}",默认值:新 { 控制器 =主页",动作 =索引",id = UrlParameter.Optional });}}

我应该如何将路由更改为用户名?

解决方案

需要添加路由来覆盖有用户名的情况.

public static void RegisterRoutes(RouteCollection routes){route.IgnoreRoute("{resource}.axd/{*pathInfo}");路线.MapRoute(名称:用户名_默认",url: "{username}/{controller}/{action}/{id}",默认值:新 { 控制器 = 家",动作 = 索引",id = UrlParameter.Optional },约束:新{用户名=新OwinUsernameConstraint()});路线.MapRoute(名称:默认",url: "{controller}/{action}/{id}",默认值:新 { 控制器 =主页",动作 =索引",id = UrlParameter.Optional });}

但要使其正常工作,您需要在 URL 中添加一个文字字符串以将该段标识为用户名(即 username-{username}),或者您需要使仅允许数据库中的用户名的约束.下面是后者的一个例子:

使用 MvcUsernameInUrl.Models;使用系统;使用 System.Collections.Generic;使用 System.Globalization;使用 System.Linq;使用 System.Web;使用 System.Web.Caching;使用 System.Web.Routing;命名空间 MvcUsernameInUrl{公共类 OwinUsernameConstraint : IRouteConstraint{私有对象同步锁 = 新对象();public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection){如果(参数名称 == 空)throw new ArgumentNullException("parameterName");如果(值 == 空)throw new ArgumentNullException("values");对象值;if (values.TryGetValue(parameterName, out value) && value != null){string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);返回 this.GetUsernameList(httpContext).Contains(valueString);}返回假;}私有 IEnumerableGetUsernameList(HttpContextBase httpContext){string key = "UsernameConstraint.GetUsernameList";var usernames = httpContext.Cache[key];如果(用户名 == 空){锁(同步锁){用户名 = httpContext.Cache[key];如果(用户名 == 空){//从数据库中检索用户名列表使用 (var db = ApplicationDbContext.Create()){用户名 =(来自 db.Users 中的用户选择 users.UserName).ToList();}httpContext.Cache.Insert(钥匙:钥匙,值:用户名,依赖项:空,absoluteExpiration: Cache.NoAbsoluteExpiration,滑动到期:TimeSpan.FromSeconds(15),优先级:CacheItemPriority.NotRemovable,onRemoveCallback: null);}}}返回(IEnumerable)用户名;}}}

<块引用>

注意:我强烈建议在示例中使用缓存,因为路由约束在每个请求上运行,并且每次请求都访问数据库是不好的.这样做的缺点是用户名在注册后最多需要 15 秒才能激活.除了将记录添加到数据库中之外,您还可以通过在注册新帐户时更新缓存(以线程安全的方式)来解决此问题,这将使其在路由约束中立即可用.

那么这只是在用户登录时执行 302 重定向的问题.您可能会在 全局过滤器.

使用 System.Web;使用 System.Web.Mvc;命名空间 MvcUsernameInUrl{公共类 RedirectLoggedOnUserFilter : IActionFilter{public void OnActionExecuting(ActionExecutingContext filterContext){var routeValues = filterContext.RequestContext.RouteData.Values;bool isLoggedIn = filterContext.HttpContext.User.Identity.IsAuthenticated;bool requestHasUserName = routeValues.ContainsKey("username");if (isLoggedIn && !requestHasUserName){var userName = filterContext.HttpContext.User.Identity.Name;//添加用户名作为路由值routeValues.Add("用户名", 用户名);filterContext.Result = new RedirectToRouteResult(routeValues);}else if (!isLoggedIn && requestHasUserName){//删除用户名作为路由值routeValues.Remove("用户名");filterContext.Result = new RedirectToRouteResult(routeValues);}}public void OnActionExecuted(ActionExecutedContext filterContext){//没做什么}}}

用法

公共类 FilterConfig{public static void RegisterGlobalFilters(GlobalFilterCollection 过滤器){过滤器.添加(新重定向登录用户过滤器());过滤器.添加(新的处理错误属性());}}

MVC 将在生成 URL 时自动重用请求中的路由值,因此无需更改您的任何 ActionLinks 以包含 username.

这是一个在 GitHub 上使用 MVC5、OWIN 和 ASP.NET Identity 的工作演示.>

Before user login, the route is:

localhost:54274/Home
localhost:54274/Home/About
localhost:54274/Home/Contact
localhost:54274/Home/Login
localhost:54274/Home/Register

Once after user logged in, the route is:

1. localhost:54274/Project
2. localhost:54274/Project/Create
3. localhost:54274/Project/Edit/1
4. localhost:54274/Project/Delete/2
5. localhost:54274/Project/1/Requirement
6. localhost:54274/Project/1/Requirement/Create
7. localhost:54274/Project/1/Requirement/Edit/3
8. localhost:54274/Project/1/Requirement/Delete/4

I want the route changed to the username once after user logged in. For example, the username is hendyharf.

1. localhost:54274/hendyharf/Project
2. localhost:54274/hendyharf/Project/Create
3. localhost:54274/hendyharf/Project/Edit/1
4. localhost:54274/hendyharf/Project/Delete/2
5. localhost:54274/hendyharf/Project/1/Requirement
6. localhost:54274/hendyharf/Project/1/Requirement/Create
7. localhost:54274/hendyharf/Project/1/Requirement/Edit/3
8. localhost:54274/hendyharf/Project/1/Requirement/Delete/4

The controller for my project are only 3 controllers: HomeController, ProjectController, and RequirementController

My RouteConfig.cs is still in a default

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

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

How should I supposed to do to make the route change to username?

解决方案

You need to add a route to cover the case that has a user name.

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

    routes.MapRoute(
        name: "Username_Default",
        url: "{username}/{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        constraints: new { username = new OwinUsernameConstraint() }
    );

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

But for that to work right, you will either need to add a literal string to your URL to identify the segment as username (i.e. username-{username}) or you will need to make a constraint that only allows the user names that are in the database. Here is an example of the latter:

using MvcUsernameInUrl.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Caching;
using System.Web.Routing;

namespace MvcUsernameInUrl
{
    public class OwinUsernameConstraint : IRouteConstraint
    {
        private object synclock = new object();

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (parameterName == null)
                throw new ArgumentNullException("parameterName");
            if (values == null)
                throw new ArgumentNullException("values");

            object value;
            if (values.TryGetValue(parameterName, out value) && value != null)
            {
                string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
                return this.GetUsernameList(httpContext).Contains(valueString);
            }
            return false;
        }

        private IEnumerable<string> GetUsernameList(HttpContextBase httpContext)
        {
            string key = "UsernameConstraint.GetUsernameList";
            var usernames = httpContext.Cache[key];
            if (usernames == null)
            {
                lock (synclock)
                {
                    usernames = httpContext.Cache[key];
                    if (usernames == null)
                    {
                        // Retrieve the list of usernames from the database
                        using (var db = ApplicationDbContext.Create())
                        {
                            usernames = (from users in db.Users
                                            select users.UserName).ToList();
                        }

                        httpContext.Cache.Insert(
                            key: key,
                            value: usernames,
                            dependencies: null,
                            absoluteExpiration: Cache.NoAbsoluteExpiration,
                            slidingExpiration: TimeSpan.FromSeconds(15),
                            priority: CacheItemPriority.NotRemovable,
                            onRemoveCallback: null);
                    }
                }
            }

            return (IEnumerable<string>)usernames;
        }
    }
}

NOTE: I strongly recommend using caching for this as in the example, since route constraints run on every request and it is not good to hit the database on every request. The downside of this is that it takes up to 15 seconds for the username to become active after it is registered. You could potentially get around this by updating the cache (in a thread-safe way) when a new account is registered in addition to adding the record to the database, which would make it available immediately in the route constraint.

Then it is simply a matter of doing a 302 redirect when the user logs in. You could potentially do that in a global filter.

using System.Web;
using System.Web.Mvc;

namespace MvcUsernameInUrl
{
    public class RedirectLoggedOnUserFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var routeValues = filterContext.RequestContext.RouteData.Values;
            bool isLoggedIn = filterContext.HttpContext.User.Identity.IsAuthenticated;
            bool requestHasUserName = routeValues.ContainsKey("username");

            if (isLoggedIn && !requestHasUserName)
            {
                var userName = filterContext.HttpContext.User.Identity.Name;
                // Add the user name as a route value
                routeValues.Add("username", userName);

                filterContext.Result = new RedirectToRouteResult(routeValues);
            }
            else if (!isLoggedIn && requestHasUserName)
            {
                // Remove the user name as a route value
                routeValues.Remove("username");

                filterContext.Result = new RedirectToRouteResult(routeValues);
            }
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // Do nothing
        }
    }
}

Usage

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new RedirectLoggedOnUserFilter());
        filters.Add(new HandleErrorAttribute());
    }
}

MVC will automatically reuse route values from the request when genrating URLs, so there is no need to change any of your ActionLinks to include username.

Here is a working demo on GitHub using MVC5, OWIN, and ASP.NET Identity.

这篇关于登录后如何将路由更改为用户名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆