I'm rendering a common Log-In form using Html.RenderAction, on every page of my site.
If the user enters their details into the text-box and clicks 'Submit', it does a POST to a controller that handles the log in.
If they make a mistake, such as entering an invalid email address, it will populate the ModelState with an error message and then redirect back to whatever page they were on before.
The problem is, because RenderAction occurs as a separate request, I'm losing the ViewModel.
Even when I put it into TempData it gets lost, since TempData is flushed on each separate request.
Is there a way of preserving data between consecutive Html.RenderAction calls?
If not, any suggestions on how I might be able to hack this? (Should put the data in Session?)
Here's what I've done for the time being. (This probably isn't the most ideal solution.)
I created a 'PreserveViewDataAttribute', which I put on any action for which I want to preserve the ViewData in the session.
In my BaseController, I overrode the 'Redirect' method with my own method, which does the following.
Gets a reference to the Action method that called it (a bit of reflection here)
Checks if this method has the 'PreserveViewDataAttribute' defined on it
If it does, copies the current ViewData to a Session variable. (The label of the variable is the same as the current action name, with '_ViewData' tacked onto the end.)
In either case, calls the base Redirect method.
Then I created a property in the BaseController called 'PreservedViewData', which returns the ViewData in session, relevant to the current action. (Or returns null if not found).
Thus, to preserve ViewData as long as I want, I need only decorate my action with 'PreserveViewDataAttribute', and then call 'PreservedViewData' whenever I need it.
Let me know if you want the source-code to this.
You might like this Post-Redirect-Get section's approach by Kazi Rashid.
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg
Related
I have a situation in which I need to reuse an action that has its functionality wrapped in a withForm closure.
Everything works well when submitting the form but when I try to reuse that action in another way I get redirect errors from my browser. Specifically, I need to redirect another action to it, possibly call it with chain, and I also want to call it from a hyperlink.
I'd really like to avoid creating a redundant action or having the invalidToken closure execute the same code. I've tried to find some more details about how withForm works and find out what happens if no token is passed to the closure but the Googles have let me down.
Is this possible? Am I trying to make it do something it can't?
More info:
I have a user edit controller action. It is wrapped with the withForm closure. There are three different cases in which I need to call this controller to render the user edit page:
An admin enters the user's id into an input and clicks the form
submit button (this form uses useToken). This needs to be secured
and protected from duplicate form submission.
An admin selects a user to edit from a list of employees by clicking
on the user's name (a hyperlink). Its possible I could turn this into a form submission with useToken and do some CSS styling to make it look like a link.
An admin creates a new user. When the user is successfully created
the create controller redirects (or uses chain) to the edit
controller. I can't find a work around for this, except to create a redundant controller.
If your code is used in more than one place a controller action isn't the best place to put it. I suggest you to move that piece of code to a service and call it from both actions.
Here is my solution. If anyone has some insight into other methods of solving this please contribute. I'm sure I'm not the only one that has had this problem.
The answer is due, in large part to #Sergio's response. It was far more simple than what I was thinking it would be. I created my edit action without withFormthen call it from another action that wraps the edit action in the withForm.
def editWT(Long uid, Long pid){
withForm{
edit(uid, pid)
}
}
def edit(Long uid, Long pid){
// Do lots of stuff to prep the data for rendering the view
}
This answer isn't innovative or ground-breaking but it works. I hope this helps someone else.
project type is MVC2. Let say that i have page1. after success it write somethink to row and get new inserted row id and redirect to another page and sends row id as parameter. and user can see this parameter on querystring. and can change it. i think so taht it is problem in some situation(pages). i use for it a hidden input and after post checking parameter from query string with hidden input value. if they are not equal then writing in to log and redirectiong to error page. does my way is correct. or have a good methods.
thanks...
Exposing IDs like this is pretty standard and is what lets browser bookmarking of specific items work. Your job is to ensure that the user can only see and modify records that they should be able to.
If the user does some URL-hacking and enters the URL to an item they are not allowed to see or modify, you can either just kick them back to the parent page, or give an Access Denied message, depending upon the app/context.
The bottom line is never trust user input, including hidden form parameters.
As above, I'm implemented the client side validation no problem, and it pops up the relevant messages as required. However, the submit button still appears to submit the form even though the form is not in a valid state.
The controller method throws it out immediately, because ModelState is not valid, but is there a flag/property I can check client side to prevent the post from happening at all?
UPDATE:
The problem appears to be because the form is submitted asynchronously using a jquery post, hence my javascript method is posting the data regardless of the validation state. So what I'm looking for is, before I do that post, to do something along the lines of if( [Property/Method which indicates MVC Model State] == false ) return false;, however, I'm struggling to find such a method or property.
I've considered implementing a check function which identifies whether the 'input-validation-error' class is applied to any of my form fields. With JQuery, it's pretty simple, but it doesn't sit to well with me. Does anyone have any opinions on doing this?
On the one hand, it seems a bit of a hack because I'm effectively iterating through the form after it's been validated to see if it's actually valid. On the other hand, I'm not sure how the MVC JS would identify the modelstate if there were multiple forms on the same page, whereas if I was to do it myself, I could identify 'for the post of this form, I'm interested in these fields'.
I have an ASP page that takes two arguments on the querystring. On the page is a form that posts back to itself.
If I do the form post once, and then try to refresh the page, it doesn't repost the form. It loads the page as though it were loading for the first time, missing the querystring values.
Is there a way to ALWAYS force a repost when I refresh a page that is the result of a FORM post?
It sounds like the problem you're having is loss of some essential parameters to your page when posting. In ASP there are two primary methods of passing parameters, in the url string via GET or from a form POST. The former passes you values in the QueryString dictionary while the latter gives them to you in the Form dictionary. Fortunately for you it is possible to accept a parameter that exists in EITHER dictionary by looking to Request object:
Request["a"] will find a regardless of being in Request.QueryString["a"] or Request.Form["a"].
This will help you in your current dilemma because you can simply write your querystring parameters to your Form on initial load of the page as <input type="hidden" fields. On subsequent posts your Request["a"] search for your parameters will find them regardless of being passed in the URL (on initial load) or via post on subsequent calls.
The problem was that I was going into the Firefox address bar and pressing Enter. This caused the URL to reload (and of course it didn't have the querystring after it reposted). So -- lesson is to do a check of the incoming vars and form vars to see if the page has been manually refreshed I suppose...
You could still maintain the submitted values in this situation.
What you would need to do is log the most recent request in either a Cookie, Session or data/file store, and on each request, check to see if the request was handled before you remove the data.
Since what you were after was the querystring it could just be something like this:
Response.Cookies("tempdata")("querystring") = Request.ServerVariables("QUERY_STRING")
Response.Cookies("tempdata")("querystring_handled") = false
then when you are done with that request you can clear the cookie value or set the querystring_handled = true.
There are probably situations where this could cause some conflicts, but just so you know, it is still going to be possible for you to remember the request once it is received by the server.
Which action does the form use: GET or POST? Normally, a form would use the POST action, but in this case, if you refresh the page with the posted form, you will not get anything in query string, because query string only gets passed via the GET action. Assuming that this issue is not caused by page caching, it seems to me like it works as designed (if the form POSTs data). Just make sure that you process the form variables if the query string is missing.
I am using T4MVC to redirect to another action return RedirectToAction(MVC.MyController.MyAction());.
In result it is doing get request.
Is there any way to make post request from controller. I want to keep all the same but only make post instead get. I cant find any methods for that. I found one post helper here http://geekswithblogs.net/rakker/archive/2006/04/21/76044.aspx but i cant pass any values i need using this post helper. I was trying to pass values through TempData but they are not coming when i using this helper. May be some one have any ideas?
The reason i want to do this because when user come from one controller to another and then if user click update or just click enter in browser address bar, page will break.
Should i use session for that reason?
A RedirectToAction will always perform a GET, never a POST (it returns a HTTP 302 to the browser, which will then issue a GET request).
To persist data across the redirect, if it is data that can be easily represented as a string and stored in the query string, then you can just add it to the route values of the redirect.
e.g.
return RedirectToAction("Search", new { searchString = "whatever" });
If it is a complex type, then you will need to store it in TempData. A number of other questions on StackOverflow (such as this one) give details on how.
If repeatedly storing to and reading from TempData across your application offends your code-sense, then you can encapsulate this by using the PassParametersDuringRedirect attribute and generic RedirectToAction available in the MvcContrib project. Some details on this technique are available here.
only way of doing post is by having a form and doing submit on that form, either with a submit button or with javascript, any info you want passed to that action must be in that form and you will find everything posted in FormCollection(hope I spelled it right).