Spring mvc url issues - spring-mvc

I am using Spring mvc and I have this problem:
Every method in my controller returns a name of a jsp, and as for presentation - all good. The problem is with links (hrefs). The window's link does not change and the relative links gets me to unwanted places.
For example (not real):
I have a view which I can access by:/test1/{id}
and an update view which I access by /test1/{id}/update
The post method, saves, and return the first view (getting you back to the viewing screen).
The presentation is OK and I can see the updated data. But the window url was not changes and if I try to update again, I am sent to this location: /test1/{id}/update/update.
How can I solve this?

Short answer: use redirects!
You MUST use redirects while doing something persistant. I guess you are modifying data when POSTing to those url. So you absolutely need to use redirects. This resolves the problem, when user refreshes the page after making an update. Have you ever noticed browser's warnings about resending the data?
Sample code:
#RequestMapping("/test1/{id}/update")
public ModelAndView update(#PathVariable("id") String id){
// make an update
return new ModelAndView("redirect:/test1/" + id);
}
This will send 301 or 302 HTTP status to the user. And the browser will imeddiately follow to the redirect url. Note that you should use not a view name, but url.

Related

Detecting redirect to a different URL

I am using ASP.NET MVC with AngularJs framework. In my home controller I'm checking if there is a valid license and if not, I'm re-directing to a page to import license with this code:
public ActionResult Index()
{
var retVal = _licenseProvider.ValidateExistingLicense();
if (!retVal.Item1)
{
Response.Redirect("DataMaintenance/ImportLicenses", true);
return View("DataMaintenance/ImportLicenses");
}
So, in my ImportLicenses controller I want to detect that I was re-directed vs. called from the menu. I found an older thread about redirecting to a different action, but that solution doesn't apply. What are my options here?
You have a couple of options here:
Add a query string parameter in the ImportLicenses action that
determines whether the user got here via a redirect or the menu
Set a TempData variable before redirecting your user to the
ImportLicenses action.
Use a Session variable
Read this for more information about passing data between Action methods.
It really depends on your constraints, there are a number of options. Without knowing much about your project. My first suggestion would be to drop a cookie before redirecting, then when the request comes in to the ImportLicenses action you can check for the check, and delete it, but include whether or not the cookie was found in your view model so you can reflect this in the UI.
There are other options like using session state, or a query string parameter. A querystring parameter could be just as effective as the cookie idea i mentioned above, and it would be a bit cleaner.
If you can provide more information about your use case, I may be able to expand my answer.
Hope this helps.
In the meanwhile I decided to do a little bit of cheating, but it worked. In the menu I changed the original call to this
dataMaintNodes.SubNodes.Add(new MenuMapNode() { LabelKey = "importLicense", Action = "ImportLicenses", Controller = "ImportLicenses", Area = "DataMaintenance", Icon = "", Roles = "IMPORTLIC" });
In other words, instead of normal Index action I introduced a different action. I added that new action to my controller and just set the ViewBag property to false when called from the menu and to true in the Index action that is called by Redirect. So, it's a cheating, but it works. I only wanted to display Cancel button on that modal dialog when I am calling from the regular menu and no Cancel button when called from the home controller. Now, my second problem is that Modal Dialog doesn't really prevent from escaping from it and continuing working with the application. So, we may want to introduce more logic.

ASP.NET MVC3 URL and hiding Id's

I have a heap of entities that are keyed by ID. In one screen I load a "Subject" based on the ID like so: "/Results/Subject/53" On that same screen I have a combo box that maintains a list of all subjects. The user can change the selection in this combo to another subject and I load the results for that subject using ajax. When I do this the URL in the browser is stale as I might be now be looking at /Results/Subject/45 If a user reloads I reload the original document and not the new one.
I would either like to update the URL (which sounds hacky as) or I would like to base my navigation on something else besides the Id as part of the URL. How can I do this? How can I load controllers for specific items without specifying the Id as part of the URL.
You can also pass the id via the HTTP GET parameter: /Results/Subject?id=45 or HTTP POST parameter.
However, I doubt that this will answer your question.
A colleague of mine has encountered the similar problem, and he solved it by altering the browser URL string (by attaching a specific #anchor to it).
Easiest way would be to ditch the ajax when that drop-down changes, and just navigate to a new url (/Results/Subject/NewID).
If you want to keep the ajax, you could just put the key at the end of the url with a # like "/Results/Subject#53", then load it via Ajax (the #53 will not be passed over to the server). That way, when the user changes the drop-down, you can navigate to "/Results/Subject#NewID" without it reloading the page.

Redirect to webapp default document when another page is specified?

IIS6, ASP.NET 2.0, No Forms Authentication
I'm calling Response.Redirect("~/foo.aspx"), but the default document ("Default.aspx") for my site is appearing. To make matters worse, it only happens intermittently. Sometimes the redirect displays the right page.
I've checked session state, and I don't see any values in the web.config (that is, I'm assuming I'm using the 20-minute defaults).
I wish I had more relevant information to share (I'll do my best to answer any questions).
Any ideas? Why isn't it redirecting to the specified page?
EDIT: I've looked deeeeeper into the code and learned more details.
Ok. There's foo.aspx and foo2.aspx (and the default document, Default.aspx). All pages extend from BasePage, which extends Page.
BasePage has a property named ReturnPage:
protected string ReturnPage {
get {
if (Session["ReturnPage"] == null) {
Session["ReturnPage"] = "";
}
return Session["ReturnPage"].ToString();
}
set { Session["ReturnPage"] = value; }
}
Users click on a LinkButton on foo.aspx, and the click event handler ends with two lines of code:
ReturnPage = ResolveUrl("~/foo.aspx");
Response.Redirect(ResolveUrl("~/foo2.aspx"));
The Page_Load of foo2.aspx has problems, and its error handling calls Response.Redirect(ReturnPage).
When I view the response headers of foo2.aspx, the 302 location is string.Empty (that is, there isn't one). That same response header has the same ASP.NET Session ID as the response of foo.aspx.
And remember -- this is intermittent. Sometimes, you can click on that LinkButton and go effortlessly to foo2.aspx, no problem. You can process the click with the exact same data once, and it will fail. You'll navigate from the default document (Default.aspx, where you were sent by the "bug") back to foo.aspx, click again with the same data (the same row in the grid/table -- the same LinkButton, essentially), and you'll be redirected to foo2.aspx without issue.
Placing a value in the session immediately before a Response.Redirect() is risky.
Changing foo.aspx's Response.Redirect() to the following might retain the session value more reliably:
Response.Redirect("~/foo2.aspx", false);
UPDATE: This ended up being fixed only by moving our session state into SQL Server. See related question: Why/when are session writes vulnerable to thread termination?
When you say:
Sometimes the redirect displays the right page.
Does it just happen, and you are not sure if there are certain pages that are affected by the problem? If this is the case, then you probably have a addressing problem. You can use either a relative path or an absolute path rather than an Application-relative path. I would also guess that you are trying to either direct to a page from a subdirectory on your site or to a subdirectory on your site. If you choose to stick with the Application-relative path make sure that are taking the subdirectory into account. (ex: ~/FooPages/Foo.aspx)
Here is a good reference page I just found:
http://nathanaeljones.com/129/types-of-asp-net-paths/
I'm a little confused here. What exactly are you trying to accomplish? You're getting the default document exactly because the 302 is blank. Your "inconsistent" behavior is almost certainly due to the way you are saving data in the Session.
The real issue here is why you're redirecting when foo2.aspx "has problems". What's the problem here? Why redirect? If you really need to redirect, why is the redirect target changed? Make it a static error reporting page and you'll be fine.
Once you redirect and get a new instance of the BasePage from foo2.aspx, won't that ReturnPage property be null again? Then once your page load errors out and tries to redirect it will be accessing a null string. Maybe try throwing that property in the Session
Session.Add("ReturnPage","~/foo.aspx")
instead of
ReturnPage = ResolveUrl("~/foo.aspx");
Ofcourse you would have to modify that error handling in the page load to grab it out of session rather than the property and you may have to rethink how the whole redirect is working in your system if this turns out to be the issue.
EDIT:
To test this idea about the property not getting set, or getting set correctly....(just to test I am not suggesting you should hard code the path in there), change your getter to the example below, then check to see if it works. Hope this helps, I am curious to find out what the problem is if this is not the issue.
get {
if (Session["ReturnPage"] == null) {
Session["ReturnPage"] = "~/foo.aspx";
}
return Session["ReturnPage"].ToString();
}

ASP.NET MVC partial views and redirecting

I have a partial view called Login.ascx that has my login boxes that I include on a number of pages throughout my site. It works fine when the information is correct but I'm trying to do validation so if the login info is incorrect, I want to redirect the user back to the view they were in before to show them the login errors. What is the correct way of saying, return to the view you came from?
If a login fails from any page, I think I would direct them to a login view for the errors instead of the previous page. A dedicated login page is likely to have more UI space to display errors, etc. than a login control on another page. Having said that, you may want to include a returnUrl parameter to the Login action so that when the login is actually successful, the user is directed back to the place they were (or were attempting to get to).
Sounds like instead of asking how I do this, you should be asking yourself WHY am I doing it this way. Maybe it's a design decision rather than a technical question.
Though if you're really going to have one controller actions for multiple login pages you can try...
return Redirect(Request.UrlReferrer.ToString());
Or keep the route name in TempData and just use a RedirectToRoute(TempData["LoginRoute"]);
Both solutions have a bad code smell though.
Note that if you're not checking for cross-site injections that is just going to refer back to the other site. You may want to do some validation on the referring URL.
For the built-in Login method of the AccountController there is a parameter named returnUrl which you can use like so:
Return Redirect(returnUrl);
or
Return RedirectToAction(returnUrl);
if you specify the returnUrl parameter as a valid actionlink.
I recently had similar problems - you might be able to find something here...

How to disable browser postback warning dialog

I have an asp.net application that runs exclusively on IE7 (internal web site).
When a user needs to enter data, I pop up a child window with a form. When the form closes, it calls javascript:window.opener.location.reload(true) so that the new data will display on the main page.
The problem is that the browser complains that it must repost the page. Is there any way to turn this feature off?
No, but there is a solution. Its generally considered good design to use a 302 redirect immediately after someone posts data to a page. This prevents that popup from ever occuring. Allow me to elaborate.
1) The user fills in a form and submits data via POST.
2) The backend receives the data and acts upon it.
3) Instead of returning the content to the user, the backend issues a 302 redirect as soon as its done processing the page (possibly redirecting the user back to the exact same url, if need be)
4) The page that the user will see is the page you told their browser to redirect to. They will load up the redirected page with a standard GET request. If they try to refresh the page, it will not repost the data. Problem solved.
This is a problem with the usual "postback" way of ASP.NET. If you need to reload a page without this warning, this page must come from a GET, not a POST. You could do a Response.Redirect("...") yourself. But this will destroy the use of viewstate.
asp.net mvc fixes this issue, not an ie7 only problem but a security feature of most browsers. No fix that I know of except you could just update the content in the main form with js rather than reloading the whole page
It's because the page in window.opener comes from a POST Request
Maybe you can use
javascript:window.opener.location = window.opener.location; to do just a GET request if the data can be fetched without a POST.
I do not believe that there is a way to do that. Instead, why not direct the parent window to a page without a reload.
javascript:window.opener.location='your url'
AFAIK, not via your scripts.
You might try:
window.opener.location = '#';
It should circumvent the browser reposting. And, you can adjust the hash name as needed.
If you move from page1 to page2, and want to disable the browser from going back to page 1,then add the following at the top of page1.
<script>
if(window.history.forward(1) != null)
window.history.forward(1);
</script>

Resources