且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

带有路由参数的ASP.Net MVC RouteAttribute错误

更新时间:2023-02-16 17:57:15

如果您打算这么做,那么您还没有完全理解使用属性路由的概念.这是如何配置所需路由的示例.

  [RouteArea("Admin")][RoutePrefix(用户")]公共类UsersController:控制器{私有只读IUsersService _usersService;私有只读IRolesService _rolesService;公共UsersController(IUsersService usersService,IRolesService roleService){_usersService = usersService;_rolesService = RolesService;}//获取管理员/用户//获取管理员/用户/索引[HttpGet][路线("")][Route("Index")]public ActionResult Index(){var model = new UsersViewModel {用户= _usersService.GetUsers()};返回View(model);}//GET Admin/Users/User/1[HttpGet][Route("User/{id:long}",Name ="GetUser")]public ActionResult GetUser(long id){var model = new UserViewModel {用户= _usersService.GetUser(id),角色= _rolesService.GetRoleDropdown()};返回View("User");}//POST管理员/用户/用户[HttpPost][Route("User")]public ActionResult PostUser(UserViewModel model){如果(ModelState.IsValid){_usersService.UpdateUserRoles(model.User);返回RedirectToAction("Index");}返回View("User",model);}} 

如果您同时使用具有路线属性的区域和同时具有以下属性的区域基于约定的路由(由AreaRegistration类设置),然后您需要确保区域注册发生在MVC属性之后路由已配置,但是在基于默认约定的配置之前路线已设定.原因是应该订购路线注册从最具体的(属性)到更一般的(区域)注册)到Mist通用(默认路由)以避免通用通过匹配传入来从隐藏"更具体的路由中选择路由在管道中过早提出请求.

 //首先在RouteConfig中route.MapMvcAttributeRoutes();//然后注册区域.AreaRegistration.RegisterAllAreas();route.MapRoute(名称:默认",url:"{controller}/{action}/{id}",默认值:new {controller ="Home",action ="Index",id = UrlParameter.Optional}); 

路由名称

您可以为路由指定名称,以便轻松允许URI它的一代.例如,对于以下路线:

  [Route("User/{id:long}",Name ="GetUser")]公共ActionResult GetUser(长ID) 

您可以使用 Url.RouteUrl 生成链接:

 < a href ="@ Url.RouteUrl(" GetUser,新的{Area =" Admin,id = user.UserId})" class ="btn btn-warning">< i class ="fa fa-pencil"</i></a> 

I'm getting a lot of different errors while trying to add Route mapping to a certain action!

I'm trying to get this route for GET /Admin/Users/User/1 and POST /Admin/Users/User

But sadly there is already a User property in Controller! So i cannot use public ActionResult User(long id) because i need to hide the user property (that i need to keep because it's the IPrincipal of the Controller and i still get the same error).

Defining route this way :

// First in RouteConfig
routes.MapMvcAttributeRoutes();

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

// Then i'm registering my Areas.

this controller is in Admin Areas. UsersController : Controller

[HttpGet]
[Route(Name = "User/{id:long}")]
public ActionResult GetUser(long id)
{
    var model = new UserViewModel
    {
        User = _usersService.GetUser(id),
        Roles = _rolesService.GetRoleDropdown()
    };

    return View("User");
}

[HttpPost]
[Route(Name = "User")]
public ActionResult GetUser(UserViewModel model)
{
    if (ModelState.IsValid)
    {
        _usersService.UpdateUserRoles(model.User);
        return RedirectToAction("Index");
    }

    return View("User", model);
}

Here is the error i'm getting :

The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method 'System.Web.Mvc.ActionResult User(Int64)' in 'MyProjectName.Web.Areas.Admin.Controllers.UsersController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters

I'm not sure to really understand what's wrong!

I checked this page that explain the attributes and i see nothing wrong https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/

EDIT 1

It still doesn't work, i changed my registration to

routes.MapMvcAttributeRoutes();

AreaRegistration.RegisterAllAreas();

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

Here is my full controller code because now it doesn't work for my Index action that was not in the initial code.

[RouteArea("Admin")]
[RoutePrefix("Users")]
public class UsersController : Controller
{
    private readonly IUsersService _usersService;
    private readonly IRolesService _rolesService;

    public UsersController(
        IUsersService usersService,
        IRolesService rolesService)
    {
        _usersService = usersService;
        _rolesService = rolesService;
    }

    [HttpGet]
    [Route(Name = "Index")]
    public ActionResult Index()
    {
        var model = new UsersViewModel
        {
            Users = _usersService.GetUsers()
        };

        return View(model);
    }

    [HttpGet]
    [Route(Name = "User/{id:long}")]
    public new ActionResult User(long id)
    {
        var model = new UserViewModel
        {
            User = _usersService.GetUser(id),
            Roles = _rolesService.GetRoleDropdown()
        };

        return View("User");
    }

    [HttpPost]
    [Route(Name = "User")]
    public new ActionResult User(UserViewModel model)
    {
        if (ModelState.IsValid)
        {
            _usersService.UpdateUserRoles(model.User);
            return RedirectToAction("Index");
        }

        return View("User", model);
    }
}

Now, i'm getting while trying to go to my index action:

The current request is ambiguous between the following action methods: System.Web.Mvc.ActionResult Index() on type EspaceBiere.Web.Areas.Admin.Controllers.UsersController System.Web.Mvc.ActionResult User(Int64) on type EspaceBiere.Web.Areas.Admin.Controllers.UsersController System.Web.Mvc.ActionResult User(EspaceBiere.Web.Areas.Admin.ViewModels.Users.UserViewModel) on type EspaceBiere.Web.Areas.Admin.Controllers.UsersController

and this while trying to go to User Action

A public action method 'User' was not found on controller 'EspaceBiere.Web.Areas.Admin.Controllers.UsersController'.

And my link to the index action was :

tried with /Admin/Users for my Index Action

tried with /Admin/Users/User/1 for User Action

EDIT 2

Ok, my index is now working perfectly, but my user action still is not working! I've removed all the Name properties of the RouteAttribute to keep them in the constructor (as template) -> [Route("User/{id:long}")]

Sorry, if i didn't see them on first read!

Here is the link to the action

                    <a href="@Url.Action("User", "Users", new { Area = "Admin", id = user.UserId })" class="btn btn-warning">
                    <i class="fa fa-pencil"></i>
                </a>

Here is the error

No matching action was found on controller 'EspaceBiere.Web.Areas.Admin.Controllers.UsersController'. This can happen when a controller uses RouteAttribute for routing, but no action on that controller matches the request.

It does work if i write in the URL /Admin/Users/User/1 So how should i write my Url.Action ?

You haven't completely understood the concept of using attribute routing if that was your intention. Here is an example of how to configure the routes you wanted.

[RouteArea("Admin")]
[RoutePrefix("Users")]
public class UsersController : Controller {
    private readonly IUsersService _usersService;
    private readonly IRolesService _rolesService;

    public UsersController(
        IUsersService usersService,
        IRolesService rolesService) {
        _usersService = usersService;
        _rolesService = rolesService;
    }

    //GET Admin/Users
    //GET Admin/Users/Index
    [HttpGet]
    [Route("")]
    [Route("Index")]
    public ActionResult Index() {
        var model = new UsersViewModel {
            Users = _usersService.GetUsers()
        };

        return View(model);
    }

    //GET Admin/Users/User/1
    [HttpGet]
    [Route("User/{id:long}", Name = "GetUser")]
    public ActionResult GetUser(long id) {
        var model = new UserViewModel {
            User = _usersService.GetUser(id),
            Roles = _rolesService.GetRoleDropdown()
        };

        return View("User");
    }

    //POST Admin/Users/User
    [HttpPost]
    [Route("User")]
    public ActionResult PostUser(UserViewModel model) {
        if (ModelState.IsValid) {
            _usersService.UpdateUserRoles(model.User);
            return RedirectToAction("Index");
        }

        return View("User", model);
    }
}

If you are using both Areas with route attributes, and areas with convention based routes (set by an AreaRegistration class), then you need to make sure that area registration happen after MVC attribute routes are configured, however before the default convention-based route is set. The reason is that route registration should be ordered from the most specific (attributes) through more general (area registration) to the mist generic (the default route) to avoid generic routes from "hiding" more specific routes by matching incoming requests too early in the pipeline.

// First in RouteConfig
routes.MapMvcAttributeRoutes();

// Then register Areas.
AreaRegistration.RegisterAllAreas();

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

Route Names

You can specify a name for a route, in order to easily allow URI generation for it. For example, for the following route:

[Route("User/{id:long}", Name = "GetUser")]
public ActionResult GetUser(long id)

you could generate a link using Url.RouteUrl:

<a href="@Url.RouteUrl("GetUser", new { Area = "Admin", id = user.UserId })" class="btn btn-warning">
    <i class="fa fa-pencil"></i>
</a>