Reputation: 916
How do I prevent a user from manually typing in the URL of a page in MVC?
For instance, I have a controller method which returns an error view:
// GET: /Checkout/Error
public ActionResult Error()
{
return View("Error")
}
EDIT: and a controller method that returns a success view:
// GET: /Checkout/Complete
public ActionResult Complete()
{
var order = /* get the order */
return View("Complete", order)
}
I redirect to it if something goes wrong in the order process:
public ActionResult Submit()
{
if (/*order succeeds*/) {
return RedirectToAction("Complete");
}
else
return RedirectToAction("Error");
}
But a user can manually type in "www.mysite.com/Checkout/Error" and get the error view outside of the context of the checkout workflow.
I did some looking around and found ChildActionOnly, but that only seems to apply to calling actions from within views, which doesn't apply here.
I suppose I could manually check at the beginning of the Error action method to see if indeed there was a problem with the order, and return the error view in that case and redirect the user otherwise, but it seems like there has to be a simpler way to prevent users from manually navigating to pages like this.
EDIT: The same applies to the completed view. A user can type in the URL which corresponds to the action. How do I prevent this action from being invoked like this?
Upvotes: 3
Views: 3145
Reputation: 218852
You may remove the Error Action method from your controller,(but still keep the view) and use return View()
instead of the RedirectToAction
. In this case your url stays same unlike RedirectToAction where it is a 302 response which tells the browser to make a new GET request to the Error
action method.
public ActionResult Submit()
{
if (/*order succeeds*/)
{
return RedirectToAction("Complete");
}
else
{
// TO DO : LOG ERROR
return View("Error");
}
}
Assuming you have a view called Error in the ~/Views/CurrentControllerName/
or in ~/Views/Shared
If your view is in a different directory, you can give the full path to the directory.
return View("~/Views/MyErrors/OrderError.cshtml");
If needed, you can pass some information to your Error view.
EDIT : As per the comment.
If you want to pass an order object to the Complete view, You can use TempData
to pass your order object. In the Completed action method, you can check whether your specific TempData value is available and if yes, show the proper view, else tell the user that they are trying to access a page which does not exist/ or any other relevant message/view.
public ActionResult Submit()
{
if (/*order succeeds*/)
{
// assuming newOrderId stores the Id of new order somehave.
OrderVM orderVM=GetOrderFromSomeWhere(newOrderId);
TempData["NewOrder"] =orderVM;
return RedirectToAction("Complete");
}
else
{
// TO DO : LOG ERROR
return View("Error");
}
}
and in your Complete
action method, check you have something in your TempData and then show the appropriate view to the user.
public ActionResult Complete()
{
var model=TempData["NewOrder"] as OrderVM;
if(model!=null)
{
return View(model);
}
return View("NotFound");
}
TempData
uses Session object behind the scene to store the data. TempData is meant to be a very short-lived instance, and you should only use it during the current and the subsequent requests only!
Rachel has written a nice blog post explaining when to use TempData /ViewData. Worth to read.
Upvotes: 3