Redirecting to same ActionResult from different controllers
I have a User entity, and in various views, I want to create links to a user home page basically. This functionality should be available in different co开发者_C百科ntrollers, so I can easily redirect to the user's home page. Each user in my site has a role ; for example reader, writer, editor, manager and admin. Ideally, I want to try to achieve something like this:
In a controller, for example
public ActionResult SomeThingHere() {
return View(User.GetHomePage());
//OR
return RedirectToROute(User.GetHomePage());
}
in a View, I also want to use the same functionality, for example:
<%= Html.ActionLink("Link to home", user.GetHomePage() %>
Is it possible to achieve such a design in MVC? If so , how should I go about it?
I currently use a method like this, but it is only in one controller at the moment. Now I need to use the same code somewhere else and I am trying to figure out how I could refractor this and avoid repeating myself?
....
private ActionResult GetHomePage(User user){
if (user.IsInRole(Role.Admin))
return RedirectToAction("Index", "Home", new { area = "Admin" });
if (user.IsInRole(Role.Editor))
// Managers also go to editor home page
return RedirectToAction("Index", "Home", new {area = "Editor"});
if (user.IsInRole(Role.Reader))
// Writer and reader share the same home page
return RedirectToAction("Index", "Home", new { area = "Reader" });
return RedirectToAction("Index", "Home");
}
...
How about something like this:
private string GetArea(User u)
{
string area = string.empty;
if (User.IsInRole(Admin)) area = "admin";
else if (...)
return area;
}
I would suggest a custom extension to the HtmlHelper class. Top of my head (liable to have syntax errors), something like this
public static class RoleLinksExtension
{
public static string RoleBasedHomePageLink(this HtmlHelper helper, string text)
{
if (user.IsInRole(Role.Admin))
return helper.ActionLink(text, "Index", "Home", new { area = "Admin" });
// other role options here
return string.Empty; // or throw exception
}
}
Then it's just
<%= Html.RoleBasedHomePageLink("Link to home") %>
in your markup.
You don't really want to have a link to somewhere that simply redirects somewhere else, if you can avoid it.
Edit: No idea why I didn't think of this earlier, but if you do need to redirect (perhaps if you need some functionality before going to the home page), you could extend IPrinciple instead
public static class AreaHomePageExtensions
{
public static string GetArea(this IPrinciple user)
{
if (user.IsInRole(Role.Admin))
return "Admin";
// Other options here
}
}
Then you can do
return RedirectToAction("Index", "Home", new { area = User.GetArea() });
whenever you like.
Well I finally came up with a design that seems to work. I have written an controller extension, with a GetHomePage Method. This extension can also be used in your views. Here is how I did It:
public static class UserHelperExtension {
public static string GetHomePage(this ControllerBase controller, User user) {
return = "http://" + controller.ControllerContext
.HttpContext.Request
.ServerVariables["HTTP_HOST"] + "/"
+ GetHomePage(user);
}
//need this for views
public static string GetHomePage(string httphost, User user) {
return = "http://" + httphost + "/" + GetHomePage(user});
}
private static string GetHomePage(User user) {
if (user.IsInRole(Role.Admin))
return "/Admin/Home/Index";
if (user.IsInRole(Role.Editor))
return "/Editor/Home/Index";
if (user.IsInRole(Role.Reader))
return "/Reader/Home/Index";
return "/Home/Index";
}
}
The action method in the controller looks like this:
using Extensions;
...
public ActionResult SomethingHere() {
return Redirect(this.GetHomePage(user));
}
...
In the view I have this:
...
<%@ Import Namespace="Extensions"%>
<%=UserHelperExtension.GetHomePage(Request.ServerVariables["HTTP_HOST"], user)%>
...
The advantage is that I can easily use this "GetHomePage" method in various controllers, or views thoughout my application, and the logic is in one place. The disadvantage is that I would have preferred to have it more type safe. For example, in my orignal tests, I had access to RouteValues collection:
public void User_should_redirect_to_role_home(Role role,
string area, string controller, string action) {
...
var result = (RedirectToRouteResult)userController.SomeThingHere();
Assert.That(result.RouteValues["area"],
Is.EqualTo(area).IgnoreCase);
Assert.That(result.RouteValues["controller"],
Is.EqualTo(controller).IgnoreCase);
Assert.That(result.RouteValues["action"],
Is.EqualTo(action).IgnoreCase);
...
}
But now that I am using a string so it is not type safe, and checking the RedirectResult.Url.
...
var result = (RedirectResult) userController.SomethingHere();
Assert.That(result.Url.EndsWith("/" + area + "/" + controller + "/" + action),
Is.True);
...
精彩评论