ASP.NET MVC Actions that return different views, or just make a ton of Actions?
So, I am in a situation, where I need to display a different view based on the "Role" that the authenticated user has.
I'm wondering which approach is best here:
[Authorize(Roles="Admin")]
public ActionResult AdminList(int? divID, int? subDivID)
{
var data = GetListItems(divID.Value, subDivID.Value);
return View(data);
}
[Authorize(Roles = "Consultant")]
public ActionResult ConsultantList(int? divID, int? subDivID)
{
var data = GetListItems(divID.Value, subDivID.Value);
return View(data);
}
or should I do something like this
[Authorize]
public ActionResult List(int? divID, int? subDivID)
{
var data = GetListItems(divID.Value, subDivID.Value);
if(HttpContenxt.User.IsInRole("Admin"))
{ return View("AdminList", data ); }
if(HttpContenxt.User.IsInRole("Co开发者_C百科nsultant"))
{ return View("ConsultantList", data ); }
return View("NotFound");
}
In the case where the action is conceptually the same, but the display is different, I would have one action and return different views depending on your discriminator. I'd go with your second example, slightly modified. Note that there is no need to get the data if the user isn't in an appropriate role.
[Authorize]
public ActionResult List(int? divID, int? subDivID)
{
var view = HttpContext.User.IsInRole("Admin")
? "AdminList"
: (HttpContext.User.IsInRole("Consultant")
? "ConsultantList"
: null);
if (view == null)
{
return View("NotFound");
}
var data = GetListItems(divID.Value, subDivID.Value);
return View( view, data );
}
You realize, of course, that you have the potential for an unhandled exception when you refer to the Value of a potentially null Nullable<int>
, correct?
Also, you could, if doing this frequently, refactor the construction of the view prefix into a common method.
public string GetRolePrefix()
{
return HttpContext.User.IsInRole("Admin")
? "Admin"
: (HttpContext.User.IsInRole("Consultant")
? "Consultant"
: null);
}
Then call it as
...
var prefix = GetRolePrefix();
if (prefix == null)
{
return View("NotFound"); // more likely "NotAuthorized" ???
}
...get model...
return View( prefix + "List", data );
I prefer the second method, however I think your controller could be tidied up a bit. There's too much logic in there for my liking, which could potentially grow as more roles are added.
One approach would be to refactor the ugly role checking code into a service layer (which could be injected if you're using an IoC container):
[Authorize]
public ActionResult List(int? divID, int? subDivID)
{
var permission = _userService.GetKeyRole(HttpContext.User);
if(permission != null)
{
var data = GetListItems(divID.Value, subDivID.Value);
return View(permission + "List", data );
}
return View("NotFound");
}
And the extracted method:
public class UserService : IUserService
{
public string GetKeyRole(IPrincipal user)
{
if(user.IsInRole("Admin")) return "Admin";
if(user.IsInRole("Consultant")) return "Consultant";
return null;
}
}
Here is a third approach. It's a combination of both of the approaches suggested by Nate, retains single place for logic like in the second approach, but splits users into actions like in the first one.
[ChildActionOnly]
public ActionResult List(int? divID, int? subDivID)
{
var data = GetListItems(divID.Value, subDivID.Value);
return View(data);
}
[Authorize(Roles="Admin")]
public ActionResult AdminList(int? divID, int? subDivID)
{
return List(divID, subDivID);
}
[Authorize(Roles = "Consultant")]
public ActionResult ConsultantList(int? divID, int? subDivID)
{
return List(divID, subDivID);
}
The trick here is that MVC will try to search for views named by the request action rather than the one that produces output. So when you run AdminList it will actually return List action with AdminList view.
Edit: Was the downvote for not answering the question properly? Let me rephrase my answer.
精彩评论