控制器名称前的参数路由问题

时间:2018-02-27 21:49:21

标签: c# asp.net-mvc asp.net-mvc-routing

我正在尝试将此路由逻辑连接到我的ASP MVC应用程序中。

为了简化安全性,我想让所有传递的URL在控制器名称之前都有 project id。

要求如下:

  • 检查网址是否包含项目ID
  • 如果是,请检查用户是否有权访问该项目。
  • 如果未满足上述任何要求,请将用户重定向至website.com/Account/Login

网址示例:

  

http://localhost:36923/112233/DataTableRows?id=P8dFd9o8DEitJpak6lGAbA

如果用户确实可以访问项目ID 112233,则会调用正确的控制器/操作。如果没有,下面的操作过滤器会正确地将用户重定向到错误页面。到目前为止,这是有效的。

问题是缺少项目ID或用户是否无权访问指定的项目。重定向到Account/Login会产生404错误。

这是我创建的自定义路线:

public sealed class ProjectAttributeRoute : Route
{
    public ProjectAttributeRoute(string url)
        : base(url, new MvcRouteHandler())
    {

    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var r = base.GetRouteData(httpContext);

        //No project id provided so redirect to login page.
        if (r == null)
        {
            r = new RouteData(this, this.RouteHandler);
            r.Values.Add("controller", "Account");
            r.Values.Add("action", "Login");
        }

        return r;
    }

}

我的路线配置:

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

    routes.MapCustomRoutes("ProjectRoute", "
        {project}/{controller}/{action}",
        defaults: new { action = "Index" }
    );

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


    routes.MapRoute(
        name: "LoginRoute",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
        );

}

public static class RouteConfigExtensions
{
    public static void MapCustomRoutes(this RouteCollection routes, string name, string url, object defaults = null, object constraints = null)
    {
        routes.Add(name, new ProjectAttributeRoute(url)
        {
            Defaults = new RouteValueDictionary(defaults),
            Constraints = new RouteValueDictionary(constraints),
            DataTokens = new RouteValueDictionary()
        });
    }
}

实际验证用户是否有权访问URL中传递的项目的操作过滤器:

public class ProjectAccessFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        var controller = filterContext.Controller as BaseApiController;

        //if no project provided in URL. TODO: Make an exception of /Account/Login
        if (!filterContext.RequestContext.RouteData.Values.ContainsKey("project"))
        {
            UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);
            filterContext.HttpContext.GetOwinContext().Authentication.SignOut();
            filterContext.Result = new RedirectResult(urlHelper.Action("Login", "Account"));
            return;
        }

        var urlProjectId = filterContext.RequestContext.RouteData.Values["project"].ToString();
        List<ProjectModel> userProjects = new List<ProjectModel>();

        //the data layer which is called in GetProjectsFromCurrentUser is async, so I have to do this...
        var t = Task.Run(async () =>
        {
            return (await controller.GetProjectsFromCurrentUser()).ToList();
        });

        userProjects = t.Result;

        if (!userProjects.Any(x => x.Id == urlProjectId))
        {
            UnhandledExceptionViewModel viewModel = new UnhandledExceptionViewModel();

            viewModel.ExceptionMessage = $"User has no access to project '{urlProjectId}'";
            var result = new ViewResult
            {
                ViewData = new ViewDataDictionary(viewModel),
                ViewName = "Error"

            };

            filterContext.Result = result;

        }

    }
}

任何明显错误的路线配置?

2 个答案:

答案 0 :(得分:0)

请检查您的自定义路由过滤应该注册的Global.asax.cs文件Application_Start()方法

 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
 RouteConfig.RegisterRoutes(RouteTable.Routes);

答案 1 :(得分:0)

我最终使用了这条路线配置:

Get

使用自定义约束:

routes.MapRoute(
    name: "NoProjectRoute",
    url: "{controller}/{action}",
    defaults: new { action = "Index"  },
    constraints: new { controller = new NoProjectRouteConstraint() }
);


routes.MapCustomRoutes("ProjectRoute", 
    "{project}/{controller}/{action}",
    defaults: new { action = "Index" }
);

routes.MapRoute(
    name: "Default",
    url: "{project}/{controller}/{action}",
    defaults: new { controller = "Portal", action = "Index" }
);