tags:

views:

597

answers:

4

Hi

I have a login page. In my web.config I setup a loginUrl so that if a user tries to go to an "Authorized" page and are not authorized they will get redirected to the login page.

Now I noticed when this happens and the user gets redirected from a "Authorized" page the url from the page they are getting redirected from gets appended to the login url.

So that way when they do login I can use that I can send them back to the page they where trying to get too.

So this is how the Url would look:

http://localhost:2505/CMS_Account/LogOn?ReturnUrl=%2fCMS_Home%2fIndex

So I am trying to capture the ReturnUrl querystring part as a parameter in my View.

But I can't get it took work.

So I found out if I change my Form for the login to this:

<% using (Html.BeginForm()) ........

Then I can capture the ReturnURl for some reason no problem.

However how I have it right now I have this:

<% using (Html.BeginForm("Login","Authentication",FormMethod.Post,new { id = "frm_Login"})) .....

Once I try to pass the parameters into the BeginForm it stops capturing the ReturnUrl.

I don't know why it stops. Some people say that it is because I am using the default route and somehow if you don't put anything in the beingForm it magically can figure out the ReturnUrl with the default url.

Soon as you put something in BeginForm it seems to get dumb and you need to give it a route to tell it what to do.

I don't know how to write this route though. I tried quite a few different combinations and they all failed and everyone who tells me right a route never tell me how it should look like.

So I don't know what to try anymore.

What I tried

routes.MapRoute(
   "CMS_Account",  // Route name
   "CMS_Account/{action}/{ReturnUrl}",  // URL with parameters
   new { controller = "CMS_Account", action = "LogOn",}  // Parameter defaults
);

routes.MapRoute(
   "CMS_Account",  // Route name
   "CMS_Account/{action}/{ReturnUrl}",   // URL with parameters
   new { controller = "CMS_Account", action = "LogOn", ReturnUrl = ""}  // Parameter defaults
);

routes.MapRoute(
   "CMS_Account",   // Route name
   "{controller}/{action}/{ReturnUrl}",  // URL with parameters
   new { controller = "CMS_Account", action = "LogOn", ReturnUrl = ""}  // Parameter defaults
);

routes.MapRoute(
   "CMS_Account",   // Route name
   "{controller}/{action}/{id}",  // URL with parameters
   new { controller = "CMS_Account", action = "LogOn", id = ""}  // Parameter defaults
);

routes.MapRoute(
   "CMS_Account",  // Route name
   "{controller}/{action}/", // URL with parameters
   new { controller = "CMS_Account", action = "LogOn"}  // Parameter defaults
);
+1  A: 

When you are configuring the form in your 'current' state, you are forcing it to POST, but the data you want is on the querystring.

You could access it with ControllerContext.HttpContext.Request.Querystring["ReturnURL"]

or...

Add the ReturnURL as a hidden field in the form, and ensure that your Logon method that you will post to either:

  • Accepts ReturnURL as a parameter
  • Performs a Request.Form["ReturnURL"]

to get the URL value from the HTML form.

Jeff Fritz
Hmm Ic I have 2 actionResult methods one that is: //public ActionResult LogOn() //{ // return View(); //}then another actionResult with the same name but it has [AcceptVerbs(HttpVerbs.Post)] so I commented out the above one and took away the Post and it does now work.I don't get why if you use force a POST it can't grab the querystring but at the same time if I left the form with no parameters and has everything I left it(ie having a method with Post attribute).It works just fine.
chobo2
So there is no way around this? Other then what you posted it just seem so stupid that they put a way to only allow posts and stuff but if you specify it like this<% using (Html.BeginForm("Login","Authentication",FormMethod.Post,new { id = "frm_Login"})) .....It all of a sudden can't figure it out. but if that BeginForm is blank it somehow can figure out to go to the ActionResult that is marked "Post" and still grab the ReturnUrl.Then I can't believe this is not like written in the books I read or on the tutorials like this to me seems like something alot of ppl would do.
chobo2
I actually don't get what the advantage of actually specifying the stuff is?Like why bother writting all that extra stuff like I wrote?<% using (Html.BeginForm("Login","Authentication",FormMethod.Post,new { id = "frm_Login"})) .....Other then maybe if you want to give it an "id" or something.Seems like leaving it blank gets you more benefits and you still can label the ActionResults "Post" or "Get" and it still finds it.P.S this character limit is really gay.......
chobo2
Oh ya more thing the reason I don't think I want to do your first way is because I hear by passing it in my parameter is alot easier to write Unit tests for.The other ways I might look at but seems kinda stupid that you would have to go about hiding the information so you can later on grab it when it should be capable of grabbing it. Also I don't know how hard it would be to unit test the 2nd way either.
chobo2
+3  A: 

You don't need to alter your routes.

The easiest way to get the ReturnURL parameter (or any other query string parameter) is to add it to your LogOn action like so:

public ActionResult LogOn(string ReturnURL) {
    ViewData["ReturnURL"] = ReturnURL;
    return View();
}
Dennis Palmer
The thing is I have to LogOn actionResult and one of them has the "post" and when I try to pass in my ReturnUrl like you have it just comes up null.
chobo2
A: 

Have you tried to use the built in FormsAuthentications system?

If you can use this, you dont have to implement the functionality you describe.

I did not check this in MVC but it should work:

FormsAuthentication.RedirectFromLoginPage(LogInAsUserName.Text, False)

FormsAuthentications - 4guysfromrolla

Malcolm Frexner
I will have to check that out since technically when they get redirected from the authorized page that should be the last page they came from so that might work. I will try that out.Just sucks the you got this stupid ReturnUrl querstring floating around that does nothing. Anyways to get rid of it if I find out this way works?
chobo2
Plus any advantages of using ReturnUrl instead like one reason I seen people use like ReturnUrl is so they can write some unit tests to see if the user gets redirect or something like that.
chobo2
You will still have the Querystring with the page that needs authorization. But there is no need to handle it in your route and action.
Malcolm Frexner
+3  A: 

You don't need to change your routes. The cool thing with the routing engine is that if you add an additional route value that isn't declared in the route itself, the routing engine will throw it on the end as a get variable.

E.g. Have you tried putting the ReturnUrl into BeginFrom?

Controller as Dennis suggested:

public ActionResult LogOn(string ReturnURL) {
    ViewData["ReturnURL"] = ReturnURL;
    return View();
}

Then in your view you'll need to use the BeginForm(string action, string controller, object routeValues, FormMethod method, object htmlAttributes) overload. E.g.

Html.BeginForm("Login",
               "Authentication",
               new { @returnUrl = ViewData["ReturnUrl"] },
               FormMethod.Post,
               new { @id = "frm_Login" })

HTHs, Charles

EDIT: Just on a side note, the other way around it would be to put the ReturnUrl into a hidden input field - after initial getting it from the querystring. This means the value is then in your post collection and you don't have to worry about getting it back into your querystring.

Charlino
Hmm interesting. so basically put this in the default ActionResult(I have 2 one for load up without a "Post" request and one for.)For one I would not do you first way since that would cause w3c validation to fail(form has no returnValue attribute) and I rather have that passing.The hidden one I guess I could do that one but I am still trying to understand why leaving BeginForm empty can magically figure out how to take it from the querystring but when I have "Post" set it can't.I just find that so weird.
chobo2
Sorry... made the mistake of adding the returnUrl to the HtmlAttributes collection rather than the RouteValueCollection. I've edited the answer to reflect this.
Charlino
The reason why having an empty BeginForm() works is because when you use an empty BeginForm() just takes the current URL and uses that for the form's 'action' attribute / url - i.e. where it posts to. And because the current URL contains the ReturnUrl querystring value, it'll be in the form's 'action' attribute. But when you start passing stuff into BeginForm() the routing engine takes over and constructs the form's 'action' attribute / url itself, so you'll lose the ReturnUrl querystring unless you put it back in yourself - through the action attribute / url querystring or in a hidden input.
Charlino
Ah thanks for clearing up about that I did not know that. I wish the books and blogs I been reading would have mentioned that seems pretty import thing to know. Where did you read that from? Routes still confuse me and such so I am not sure what the "RouteValueCollection" really does nor do I understand why you put "@" infront of id and ReturnUrl.
chobo2
I just learnt all that stuff myself during my travels - sorry, can't point you at any particular blog, book or anything. I really need to get mine back up and running. Anyway, RouteValueCollection: Say you have a route that looks like "/Post/View/{year}/{month}/{day}" you can then use Html.ActionLink("View", "Post", new { @day = 11, @year = 1982, @month = 12 }) which in turn uses the routing engine to construct the correct route for you - which will be /Post/View/1982/12/11.
Charlino
Oh, and I put "@" in front of everything just by habit... you have to put it in front of any of the C# reserved words so I just put it in front of everything :-) E.g. if you want to give an element a class of outside-link you'd have to pass in an HtmlAttribute collection like so: new { @class = "outside-link" } because new { class = "outside-link" } would generate an error.
Charlino
ic but in your example don't you want to use the reserved keyword "class" like I would have figured it would just allow you add reserved keywords like "class" since that's what your doing your adding a css class to this object.
chobo2