Need help with ReturnUrl in MVC 3 LoginPage - asp.net-mvc-3

I have this url in my login view: http://localhost:5550/login?ReturnUrl=/forum/456&theme=1
I get the right url value when I am in the login page.
As you can see I have 2 query string parameters: ReturnUrl and theme
So far good.
Now the login page posts a form to a controller action.
All I need is to read the values of these 2 query string params.
I just can't make it work.
This is my login page view:
#using (Html.BeginForm("try", "login"))
{
//set text boxes and button so user can try login
}
This is my controller where I need those 2 values:
[HttpPost]
public ActionResult Try(LoginModel model, string ReturnUrl, string theme)
{
//read all query string param values...but how?
//I am not getting anything in string ReturnUrl, string theme. They are null
}
Other part of the question:
I can debug other controller. But not this controller.
Is this because I am posting a form using BeginForm?
Doesn't make sense. But the breakpoint never hits even though I get error on browser.

Normally you shouldn't be reading from ReturnUrl. The flow works like this:
Request to controller action which requires authentication using the [Authorize] attribute
ASP.NET MVC adds the ReturnUrl and redirects to /Account/LogOn, automatically encoding and appending the ReturnUrl parameter
The Account/LogOn POST controller action, after authenticating the login information, redirects to the URL in the ReturnUrl.
I wrote about that in gory detail in the following posts:
Looking at how the ASP.NET MVC Authorize interacts with ASP.NET Forms Authorization
Preventing Open Redirection Attacks in ASP.NET MVC
The second post indicates one good reason why need to be careful with ReturnUrl - users can tamper with them.
In the example you showed above, I'd expect that, after authorization, the user would be redirected to the Forum / Index action, which would then be reading the values. If you are creating the ReturnUrl, you should be URL Encoding the second &.

Not sure on the exact reason the values don't get bound, but the easy workaround is to add those properties to your login model and add hidden fields on the form.

Try changing the decoration:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Try(LoginModel model, string ReturnUrl, string theme)
{
//read all query string param values...but how?
//I am not getting anything in string ReturnUrl, string theme. They are null
}

Related

Changing back the url to original if exception thrown and return back to form

I have a thymeleaf signup form, which if we submit then a controller at "/signup_do" is called which validates and saves the user to database:
<form action="/signup_do" method="post">
...
</form>
The controller at "/signup_do" passes the request to the accountRegistration service method, which does the validation:
#PostMapping("/signup_do")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "Success";
}
The account registration method can throw an exception SignupFormException, which is handled by the #ExceptionHandler defined in that controller class:
#ExceptionHandler(value=SignupFormException.class)
public String handle(HttpSession session, Model response) {
Account returnDataToForm = (Account) session.getAttribute("accountToRegister");
response.addAttribute("name", returnDataToForm.getFirstName());
session.invalidate();
return "signup";
}
Now the problem is that when exception occurs, the inputs entered in the form is passed back to the signup form, and the entered data remains intact, but the url still remains as /signup_do.
I have tried using return "redirect:/signup" instead, which does change the url, but it ends up making a get request to the /signup url like
/signup?name=John...
but my /signup controller is not designed to handle a get request, it just knows to display the form, so the information is lost.
#GetMapping("/signup")
public String signupPage() {return "signup";}
I also tried using forward:/signup, but that just ended up throwing 405 error.
I figured out a clean workaround a few hours after asking this question.
What I did is change the name of the controller that handles the signup process to ("/signup") as well. Since the controller that displays the page is a #GetMapping("/signup") and the one that handles the signup process is a #PostMapping("/signup") there is no clash.
Now even if the controller changes, the url remains the same, since both of them are signup...
#GetMapping("/signup")
public String signupPage() {return "signup";}
#PostMapping("/signup")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "success";
}
And this works just like I wanted!!
Redirecting will make a get request to the controller looking for the view to display, which in your situation means losing your data for the reasons you give. I can think of two workarounds:
Don't do the redirect and change the URL manually with javascript everytime you enter this view. If you dislike having a "wrong" URL in a view, editing it manually looks the most reasonable and direct approach. You can see how to do this here, including it in a script that executes everytime the page loads/the submit button is pressed.
Do the redirect and avoid losing your info by storing it in the session for a while longer, accessing it in thymeleaf in this way, instead of getting it from a model attribute. This would mean you would have to be careful to remove this session attributes later. It's also not very "clean" that your get request for the form view includes the user info, so I wouldn't go with this solution if avoidable.

handle direct url with history api in asp.mvc

I have my site like http://mysite.com/ and on the index page i have search box and for the result i am using jqgrid. When user click row in the jqgrid row I am taking data from cells and do ajax call to server and fetch json data and once data arrived I hide the search box and jqgrid and show another div which is I kept for result. In short, user will be on the same page just div's hide/show.
Now I have seen history api and used pushState and popstate so my url becomes in the addressbar like http://mysite.com/controller/action/para1/para2 (here para1 and para2 are the parameters i am passing to action). Everything is ok so far.
Now the problem is if I copy this URL "http://mysite.com/controller/action/para1/para2" and if I open this with let's say different browser and hit enter it display just json data. So, I am confused that how to handle when user directly use that url in controller.
I was thinking to check in the controller action if the request is AJAX then return json data otherwise full page, is that right approach? OR something on the client side we have so that it load the same way as earlier.
Thanks
if I copy this URL "http://mysite.com/controller/action/para1/para2" and if I open this with let's say different browser and hit enter it display just json data.
it only display json data because its ajax call only so how to deal with that so that it also display the same page even user directly access the url.
I think what you're looking for is Request.IsAjaxRequest():
public class FooController : Controller {
public ActionResult GetFoo(int id) {
var model = _fooService.Get(id);
if (Request.IsAjaxRequest())
return PartialView("_Foo", model);
return View("Foo", model);
}
}
Note: It's recommended to use WebAPI controllers to handle only json data. So if the user got there by typing the url the mvc controller will handle it and if you need to get the json data for that view you could call the webapi controller.
Use a separate controller or action method for AJAX and for Views. The View controller should match the URL. The Ajax controller should be the less "pretty" URL since it's behind the scenes.
You need to set up a routing definition in global.asax (MVC 3) or App_Start/RouteConfig.cs (MVC 4) to handle the parameters if you haven't already done that.
routes.MapRoute(
"MyRoute",
"MyUrlController/{action}/{para1}/{para2}",
new
{
action = "Index",
para1 = UrlParameter.Optional,
para2 = UrlParameter.Optional
}
);
Then in the View controller:
public ActionResult Index(string para1 = "Default Value", string para2 = "Default Value")
{
// ...Handle parameters...
return View("_MyView", viewModel);
}
Returning a View object type is the key. The History API URL doesn't get it's data from the same AJAX source controller which returns a PartialViewResult.

How to pass Return URL in MVC3?

I have the following LogOn Action
[HttpPost]
public ActionResult LogOn(LogOnModel user, string returnUrl)
{
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
In which My returnUrl is null..
Can any body tell me .Why my return URL is null
I wrote up a very fix fix to this. Basically add a route value needs to be added to the Html.BeginForm as follows.
Html.BeginForm("LogOn",
"Account",
new { returnUrl = Request.QueryString["ReturnUrl"] })
For more details see http://blog.nicholasrogoff.com/2013/12/10/net-mvc-3-logon-does-not-redirect-out-the-boxquick-fix/
That's depends how you call the controller action method.
If you have an URL like below
http://www.somedomain.com/LogOn/LogOn?returnUrl=user/userList
In this call, your returnUrl parameter of an ActionMethod (LogOn will be replaced by querystring parameter returnUrl.
If you're using form authentication, there is an [Authorize] attribute which validated the authentication. If user is NOT authenticated, then it will redirect to the LogIn page with the querystring parameter returnUrl which will have a requested page url in it.
At this moment, you will also get returnUrl value in controller's action method parameter with returnUrl paramter with value which will have requested page Url
Hope this helps!
It depends on who is calling this method and whether this parameter is supplied in the POST request.
For example if the user tries to access a controller action decorated with the [Autorize] attribute and he is not authenticated the framework will automatically redirect to the LogOn action (the one that renders the form, not the one with [HttpPost]) and supply the returnUrl parameter.
Then you could have a hidden field in the logon form to persist its value so that when the user enters his credentials and submits the form to the LogOn action, in case of successful login, he is redirected to the initially requested page.

Submit button which RedirectToAction to previusly page.

I have one method which I use in some place. Now I make RedirectToActio.... and back me to the concrete page. Is possible to make, it back to previusly page?
You could use the Referer HTTP header but it's not very reliable. A better way is to pass the url you want to redirect to the controller action (it's the way the POST LogOn method on the AccountController is implemented when you create a new ASP.NET MVC 3 application using the built in wizard. Take a look at it):
public ActionResult Foo(string returnUrl)
{
...
return Redirect(returnUrl);
}
Then when you call this action you pass the url of the current page. For example you could generate the following anchor:
#Html.ActionLink(
"do some processing and redirect back here",
"foo",
new { returnurl = Request.Url.AbsoluteUri }
)

MVC3 Recommended Controller, Action query re-use

In an MVC3 app what is the recommended method to store the Controller, Action & query details of a page and then re-use them in an actionlink on a subsequent page?
e.g. from page ~/Home/BlogsByTag?tag=Cloud%20Services listing a series of blogs, what is the 'best' way to capture the Controller, Action & query and then how can these then be applied to a 'Back to List' actionlink in a subsequent edit page (~/Admin/Edit/?Blog=1234)?
If i understand you right, you want to generate an ActionLink with the url from the referring page?
You can read the referrer from the request in terms of a complete url:
Request.UrlReferrer
If you want to have access to the action, controller and query parts, you have to pass them in the link as parameters. For example:
Html.ActionLink("Linktext", "Edit", "Admin", null,
new {
Blog = 1234,
ReferringController = "Home",
ReferringAction = "BlogsByTag",
ReferringQueryParams = ...
});
Then you can read that vals in the Edit Action and pass it to the View to generate the back-link.
But maybe using history.back() is a simpler approach.
You can use:
Url.RouteCollection["controller"]
Url.RouteCollection["action"]
...
For the variables you need. For the querystring you can use:
Request.QueryString["variable1"]
Alternatively you can declare the controller:
public ActionResult MyController(string controller, string action, string variable...)
and the variables will be automagically filled.
Then you can store this info (or the full url) on Session["refurl"] for example, which is valid for the rest of the user's session

Resources