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.
Related
I have a user control that creates a new record in a database. After it creates the new record, I would like to redirect it back to the view its from. The purpose of this is to refresh the view so that it can show the newly created record. The problem is that user control can exists in more than one view, so how can i know which view is the user control from? so that I can achieve the above scenario? Thanks,
RWendi
I have a user control that creates a new record in a database.
No, it doesn't. It just renders HTML, and nothing else.
It's the controller action which creates a new record in the database, and thus this is where the redirect should happen.
The [HttpPost] action which accepts the model/form should perform the redirect after the save has been completed.
E.g:
public ActionResult Save(SomeModel model)
{
db.Save(model);
return RedirectToAction("Index");
}
I'm assuming that the "view" you want to refresh is the same page, regardless of which page the user controller was rendered on, therefore the above code is fine.
On a side note, you shouldn't be using user controls (e.g partials) for rendering forms.
You should be using editor templates. The presentation code which renders the form (and specifies which action to post to) should be in the view, not in the user control.
EDIT - example of how to render form on Views:
Instead of doing this on a View:
#Html.Partial("_SomeModel")
Do this:
#using (Html.BeginForm()) {
#Html.EditorFor(model => model.SomeModel)
}
And place the form markup in the editor template. The key thing here is IMO the Views should be responsible for setting up the form, not the user control.
There are several ways,
1) You can use this.Request.UrlReferrer.AbsoluteUri , which will always give you the Url of your page.
2) Or, you can have a hidden property in ... in PartialView, which will hold the value for current url. (#this.ViewContext.ParentActionViewContext.HttpContext.Request.UrlReferrer.AbsoluteUri)
But only if you are using #Html.RenderAction.
In the child action, you can redirect to either of above urls. (First approach is much more better and will work in all scenarios)
I think the refresh is responsibility of the action that renders the view your user control is from.
You can have scenarios (or views) where the inserting is done in one action and then pass the request to another action (maybe a list) that shows the newly created record.
Or you can have other scenarios where the inserting is just part of what you want to save to the db and refreshing the original view may cause the loss of data.
Maybe what I'm saying is not what happens with your project right now but it's something you should consider before giving the user control a responsibility it shouldn't have.
When the user clicks the submit button on a form I want to return a success / failure message to the user and i'm wondering what the best way to send the message back to the user is.
For example on a login form if the user enters an incorrect password i'd want the view to reload with a message telling them that login failed. Or on an update form the original view they had would show up with a message saying update was successful.
My idea is to have a few partial views or HTML helpers that look for a ViewBag property like "ErrorMessage" or "SuccessMessage" and conditionally show themselves when these properties have a value. These components could be added to the _Layout or manually each form where they are required.
Edit
I have since found out that the ValidationSummary method takes a parameter of whether to exclude property errors which means you can use it to show when login / registration has failed. But there seems to be a bug where the validation summary is still generated even if there are no errors.
This is a good question and one that I've been trying to solve in a new MVC application recently. For me, ModelState.AddModelError is ok for errors but doesn't work when communicating things like success or warning messages.
Here's an approach that I decided to take recently and I welcome any feedback on it:
I created a class called Notification and an enum called NotificationType. NotificationType has 4 values: Success, Error, Warning, & Info. Notification has the properties 'Message' and 'Type'.
I have a static class called NotificationManager that holds instances of Notification objects for the life of an HttpRequest (using the user's Session object). The idea is that any controller can add any number of Notification objects during the execution of an Action depending on what it needs to "tell" the user.
I have a custom HtmlHelper which I call once in a master page (i.e. Html.NotificationSummaryFor()) that takes a IEnumerable of Notifications (i.e. NotificationManager.Notifications) and renders a UL with child LI's for every Notification. I then use CSS classes to style those notification LI's based on the 'NotificationType' (Red for Error, Yellow for Warning, Green for Success, etc). The HtmlHelper is also responsible for clearing the notifications from the manager after rendering them to the View.
This implementation is pretty simple and has limitations but in general, I find that having a framework like this ensures that a team of developers working on the same application are able to provide notifications in a consistent fashion which is important to usability and maintainability.
asp-net-mvc-2-model-validation
This is a really easy way to set up Validation.
Also if you are doing things like login you can do something like this where you add a modelError to the model state and then check for that in your code
if (ModelState.IsValid) {
if (!FormsAuthentication.Authenticate(userName , password))
ModelState.AddModelError("" , "Incorrect username or password");
}
if (ModelState.IsValid) {
FormsAuthentication.SetAuthCookie(userName , false);
return Redirect(url ?? Url.Action("Index" , "Admin"));
} else
return View(); //goes right back to the log on screen
I have a weird issue. I loaded a page using "LoadMyPageController" and there is an ajax_submit_button1 which can be used submit the page.
Also there is another ajax_submit_button2 to print the page. This button submits the view model of the page as a whole to the "PrintController" which has a "PrintData" action.
Now when I hit the "ajax_submit_button2", my PrintController.PrintData is not invoked. Instead when I check my fiddler tool the request is made as
http://localhost:18484/LoadMyPage/PrintData?Length=14
which is an invalid URL.
I have contructed my ajax_submit_button2 in such a way that it should invoke
http://localhost:18484/Print/PrintData?Length=14
But I don't know why LoadMyPage controller is present in my URL.
Any thoughts or comments would be appreciated. By any chance does asp .net MVC decides that it will take a default controller on its own if it can't find the controller action for any reason.
The code is a kind of tightly coupled so can't post it. I want to know if anyone experienced a problem like this.
This has nothing to do with the routing on the server since the request being made by the client has the wrong controller in it. I suspect that your code generating the url for the submit button is not correct -- i.e., not specifying the controller to be used -- or that you have a form around the submit button that is actually being invoked instead of (or in addition to) the ajax code. Note that if your submit handler doesn't return false, the default form action will be invoked and the form submitted normally. If you do have a form, make sure that the url on it is correct and that your submit handler returns false.
$('#printForm').submit( function() {
$.ajax({
url: $(this).attr('action'),
...
});
return false; // this is important!
});
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();
}
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...