I am developing an ASP.Net MVC Website which the expected routing behavior is like Facebook
When user is not logged in and access the site he is showed the welcome page under
www.domain.com/
If he logs in we want him to access the website content still in
www.domain.com/
How should i define the routes?
Edit: To clarify more as instructed
I want to serve a different Index action if a user is logged in and a different if he is not.
An approach that doesn't work
[Authorize]
public ActionResult Index()
{
return View();
}
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
So with two different Index Actions the default Route
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }
will perform as i want.
By looking in the MSDN Documentation i found out i can pass the name of the View i want in the constructor.For the sake of completeness here is my implementation.
public ActionResult Index()
{
if (User.Identity.IsAuthenticated)
{
return View();
}
else
{
return View("Welcome");
}
}
Related
I am trying to create an MVC 4 ASP.net site. As I am new to programming I would like to know what is the correct way of rendering a view based on if a user is logged in or not.
My Code: I am trying to restrict a user from going to the Index, About and Contact pages. It will only go to those pages(views) if the user is Logged In. My question is, "Is this the right way of doing it or is this wrong? Is there a more secure, effective, and acceptable way of doing this?"
Please let me know if there is. Thank You
public class HomeController : Controller
{
public ActionResult Index()
{
if (User.Identity.IsAuthenticated)
{
return View();
}
return RedirectToRoute(new { controller = "Account", action = "Login" });
}
public ActionResult About()
{
if (User.Identity.IsAuthenticated)
{
ViewBag.Message = "Your app description page.";
return View();
}
return RedirectToRoute(new { controller = "Account", action = "Login" });
}
public ActionResult Contact()
{
if (User.Identity.IsAuthenticated)
{
ViewBag.Message = "Your contact page.";
return View();
}
return RedirectToRoute(new { controller = "Account", action = "Login" });
}
The Authorize attribute lets you indicate that authorization is restricted to predefined roles or to individual users.
This gives you a high degree of control over who is authorized to view any page on the site.
If an unauthorized user tries to access a method that is marked with the Authorize attribute, the MVC framework returns a 401 HTTP status code.
If the site is configured to use ASP.NET forms authentication, the 401 status code causes the browser to redirect the user to the login page.
You can use [Authorize] on your controller if all the methods require login as below:
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
You can also put the attribute on certain methods if required instead of putting on the controller itself. For example, if you want the user to login for Index() method only then you could do it as below:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
}
The common way for this case is usage of [Authorize] (AuthorizeAttribute)
You may add it to specific Actions or whole Controller. It either supports specific users restrictions and Roles as well.
You may start with default MVC solution from Visual Studio, which will create all basic functionality based on SimpleMembership provider.
You may refer to NerdDinner project for full explanation: http://nerddinnerbook.s3.amazonaws.com/Part9.htm.
To start out, I have seen this question: ASP.NET MVC one route, two different views but it doesn't answer my exact question.
I want to have one view but with the same url/route depending on whether or not you are logged in.
I want it so if a user goes to:
mydomain.com then they will either be taken to the marketing landing page or they will be taken to the users logged-in dashboard.
The question I posted above suggests that I should use:
if (Request.IsAuthenticated) {
return View("Dashboard");
} else {
return View("Index");
}
However, one of my views requires that I go through their particular actions because it requires the view model data that the action provides.
Now, if I do this:
if (Request.IsAuthenticated) {
return RedirectToAction("Index", "Dashboard");
} else {
return View("Index");
}
This works, but the redirect will cause the user's url to be the route for the dashboard-index action which is mydomain.com/dashboard.
How can I both go through the action and still maintain the root url for my domain?
You'll want to populate the model data in your current action
if (Request.IsAuthenticated) {
// populate your model to send it to the Dashboard.
// to keep it DRY, you'll want to use a data service.
var model = DataService.GetData();
return View("Dashboard", model);
} else {
return View("Index");
}
I know this is not exactly what you want but it results in the Dashboard being on the root of the domain and login redirecting to a different URL instead. And when you consider that you will probably want this functionality for every "Authorised" page putting it in an if statement looks like a painful way to go about it:
In web.config
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="15" slidingExpiration="true" />
</authentication>
then Decorate controllers:
[Authorize]
public ActionResult Index()
{
return View();
}
[Authorize(Roles="SysAdmin")]
public ActionResult ManageUsers()
{
return View();
}
This is the quick idea which i think can help you. I haven't tested this in depth
Situation looks similar to having same action, and decorating them with [HttpGet] or [HttpPost] action method selector attributes. If there's match with post, it takes priority and action under post is executed, else get.
I would apply the same logic with using custom route constraint
First create the constraint class
public class IsAuthenticatedRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return httpContext.Request.IsAuthenticated;
}
}
Then, in global.asax, register two routes. First one is higher priority and has the authenticated constraint, so it is matched when user is logged on. Else the second one. By giving correct default values, i think you could get the desired results.
routes.MapRoute(
"DefaultAuthenticated",
"{controller}/{action}/{id}",
new { controller = "Default", action = "Dashboard", id = UrlParameter.Optional },
new { isAuthenticated = new IsAuthenticatedRouteConstraint()}
);
routes.MapRoute(
"Default", //Matches if not authenticated
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Default", action = "Index", id = UrlParameter.Optional }
);
P.S this may still need more configuration. Hope idea helps
learning mvc and building a full blown ecommerce app.
Administrators should be able to add-edit whatever they like.
Let's take an example Employees.
I have added an Area called "Admin".Within admin I have Employees
In here an administrator should be able to add-edit info about employees.
Now in the user section the user should not be able to add-edit.etc..
At them moment I have 2 controllers?
Areas-Admin-Controllers-EmployeeController
Areas-Aboutus-Controllers-EmployeeController
It does not seem right to me.How do you handle such a code repetition?
I would like to have only one controller.How do you structure your mvc app in this case?
any example I can download?
Looking for a good examples where you can see areas working and running themes dynamically etc..
thanks for any suggestions
You could use a single ~/Controllers/EmployeesController controller to handle the Employee resource in your application. Inside this controller actions that require administrative (or some logged in user) privileges could be decorated with the [Authorize] attribute by specifying the required roles in order to execute this action:
public class EmployeesController: Controller
{
// Anyone can list employees, even anonymous users
public ActionResult Index()
{
IEnumerable<Employee> employees = _repository.GetEmployees();
return View(employees);
}
public ActionResult Show(int employeeId)
{
Employee employee = _repository.GetEmployee(employeeId);
return View(employee);
}
// Only administrators can update employees
[Authorize(Roles = "admin")]
[HttpPost]
public ActionResult Update(Employee employee)
{
_repository.Update(employee);
return RedirectToAction("Index");
}
// Only administrators can delete employees
[HttpDelete]
[Authorize(Roles = "admin")]
public ActionResult Destroy(int employeeId)
{
_repository.Delete(employeeId);
return RedirectToAction("Index");
}
... and some other actions following the Simply RESTful pattern:
http://mvccontrib.codeplex.com/wikipage?title=SimplyRestfulRouting
}
I'm assumuing your doing it this way so that you can use authentication and have the pretty urls:
/Admin/Employee
/Aboutus/Employee
How about only having a single controller in /Controllers/EmployeeController. You can set [Authorize] attribute on any methods that you need authentication for and control the urls with custom routes?
routes.MapRoute( _
"Admin_Employee", _
"Admin/{controller}/{action}/{id}", _
New With {.controller = "Employee", .action = "Index", .id = UrlParameter.Optional} _
)
routes.MapRoute( _
"AboutUs_Employee", _
"Aboutus/{controller}/{action}/{id}", _
New With {.controller = "Employee", .action = "Details", .id = UrlParameter.Optional} _
)
Consider two methods on the controller CustomerController.cs:
//URL to be http://mysite/Customer/
public ActionResult Index()
{
return View("ListCustomers");
}
//URL to be http://mysite/Customer/8
public ActionResult View(int id)
{
return View("ViewCustomer");
}
How would you setup your routes to accommodate this requirement?
How would you use Html.ActionLink when creating a link to the View page?
In global.asax.cs, add following (suppose you use the default mvc visual studio template)
Route.MapRoute("Customer",
"Customer/{id}",
new { Controller = "CustomerController", action="View", id="" });
Make sure you put this route before the default route in the template
You then need to modify your controller. For the view,
public ActionResult View(int? id)
{
if (id == null)
{
return RedirectToAction("Index"); // So that it will list all the customer
}
//...The rest follows
}
For your second question, ActionLink is simple.
Html.ActionLink("Link Text", "View", "Customer", new {id=1}, null);
I must be dense. After asking several questions on StackOverflow, I am still at a loss when it comes to grasping the new routing engine provided with ASP.NET MVC. I think I've narrowed down the problem to a very simple one, which, if solved, would probably allow me to solve the rest of my routing issues. So here it is:
How would you register a route to support a Twitter-like URL for user profiles?
www.twitter.com/username
Assume the need to also support:
the default {controller}/{action}/{id} route.
URLs like:
www.twitter.com/login
www.twitter.com/register
Is this possible?
What about
routes.MapRoute(
"Profiles",
"{userName}",
new { controller = "Profiles", action = "ShowUser" }
);
and then, in ProfilesController, there would be a function
public ActionResult ShowUser(string userName)
{
...
In the function, if no user with the specified userName is found, you should redirect to the default {controller}/{action}/{id} (here, it would be just {controller}) route.
Urls like www.twitter.com/login should be registered before that one.
routes.MapRoute(
"Login",
"Login",
new { controller = "Security", action = "Login" }
);
The important thing to understand is that the routes are matched in the order they are registered. So you would need to register the most specific route first, and the most general last, or all requests matching the general route would never reach the more specific route.
For your problem i would register routing rules for each of the special pages, like "register" and "login" before the username rule.
You could handle that in the home controller, but the controller method would not be very elegant. I'm guessing something like this might work (not tested):
routes.MapRoute(
"Root",
"{controller}/{view}",
new { controller = "Home", action = "Index", view = "" }
);
Then in your HomeController:
public ActionResult Index(string view) {
switch (view) {
case "":
return View();
case "register":
return View("Register");
default:
// load user profile view
}
}
OK I haven't ever properly tried this, but have you tried to extend the RouteBase class for dealing with users. The docs for RouteBase suggest that the method GetRouteData should return null if it doesn't match the current request. You could use this to check that the request matches one of the usernames you have.
You can add a RouteBase subclass using:
routes.Add(new UserRouteBase());
When you register the routes.
Might be worth investigating.
i think your question is similar to mine. ASP.NET MVC Routing
this is what robert harvey answered.
routes.MapRoute( _
"SearchRoute", _
"{id}", _
New With {.controller = "User", .action = "Profile", .id = ""} _
)
Here is an alternative way to standar route registration:
1. Download RiaLibrary.Web.dll and reference it in your ASP.NET MVC website project
2. Decoreate controller methods with the [Url] Attributes:
public SiteController : Controller
{
[Url("")]
public ActionResult Home()
{
return View();
}
[Url("about")]
public ActionResult AboutUs()
{
return View();
}
[Url("store/{?category}")]
public ActionResult Products(string category = null)
{
return View();
}
}
BTW, '?' sign in '{?category}' parameter means that it's optional. You won't need to specify this explicitly in route defaults, which is equals to this:
routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });
3. Update Global.asax.cs file
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoutes(); // This do the trick
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
How to set defaults and constraints? Example:
public SiteController : Controller
{
[Url("admin/articles/edit/{id}", Constraints = #"id=\d+")]
public ActionResult ArticlesEdit(int id)
{
return View();
}
[Url("articles/{category}/{date}_{title}", Constraints =
"date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
public ActionResult Article(string category, DateTime date, string title)
{
return View();
}
}
How to set ordering? Example:
[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
return View();
}
[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
return View();
}