Benefits of Post-Redirect-Get - zend-framework2

When I started with ZF2 the first module I used was ZfcUser. When I debug it's controller's code I found a weird way (at least for me) to manage actions. I found code like
$prg = $this->prg('zfcuser/changepassword');
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return array(
'status' => $status,
'changePasswordForm' => $form,
);
}
//VALIDATE FORM AND DATABASE STUFF
(...)
The behaviour is as follows:
The first load $prg is false, so it returns the form.
When you submit the page, $prg is an instance of Response, so it returns $prg.
When $prg is returned, the same function is called again and $prg becomes an array with all the posted data, so it jumps to the validation of form and database stuff.
I thought it was a weird approach so I override all the needed functions replacing this with the simple request->isPost(). I found it easier to handle the first load/data posted.
I didn't give it more importance until now. I'm facing the Post-Redirect-Get approach again when I'm trying to upload files: it seems that is needed to prevent user to re-select the file and re-upload when a validation error rises on a form.
What's the point of the Post-Redirect-Get? When do you recommend the use of it (apart of the commented file upload)?

As the documentation states:
When a user sends a POST request (e.g. after submitting a form), their browser will try to protect them from sending the POST again, breaking the back button, causing browser warnings and pop-ups, and sometimes reposting the form. Instead, when receiving a POST, we should store the data in a session container and redirect the user to a GET request.
So the purpose of this plugin is to improve user experience. You must have came across this problem when you submit a form and try to refresh the page you get a pop-up message like (example from google chrome):
Confirm Form Resubmission: The page that you're looking for used information that you entered. Returning to that page might cause any action you took to be repeated. Do you want to continue?
You can get more details in the docs for Post/Redirect/Get Plugin, or File Post/Redirect/Get Plugin if your form handles files uploads.
NOTE: For the File Post/Redirect/Get Plugin - Example Usage there's a typo on line 16, you should use $this->filePrg() instead of $this->prg(). It should be like the line below.
$prg = $this->filePrg($myForm, '/user/profile-pic', true);

Related

How to properly save updates to domain objects in Groovy/Grails

I'm starting to touch the Groovy/Grails backend of my organization and am tasked with updating the User on our Document domain object. The problem is, after hitting the update endpoint from the frontend with the correct params attached, the backend responds with an unchanged Document object.
Here is the code:
if (requestParams.userEmail) {
def contact = User.findByEmail(requestParams.userEmail)
log.debug('Reading user found by passed email contact={} error={}',contact, contact.errors.allErrors.inspect())
if (!contact) {
response.status = 400
render WebserviceError.badInput as JSON
return
}
document.user = contact
document.user.save(flush: true)
}
document.save(flush: true)
render survey as JSON
The frontend returns a promise and I'm logging the promise response, and it shows an unchanged Document object with the same exact user attached. I don't receive a 400 so it looks like the contact is successfully found.
I tried adding flush:true to the user.save call and the document.save call and that did not help.
Are there any obvious wrongdoings in my code?
Well db operations should be in a service, not in a controller, using #Transactional, preferably the gorm version not the spring version. You shouldn't need to use flush: true. Then fron the service you can return to the controller, andrender as JSON.
You don’t state that you see the debug statement on the server indicating a found user, perhaps it’s never actually getting to this section?
I assume that the code provided is incomplete, as we don’t see that the survey being returned contains the document that’s being updated. And also the braces look unbalanced, as if there’s a control flow issue. (i.e. why are there 2 opening braces but 3 closing braces?)
I’d suggest that you use a debugger on your code to see how control is actually flowing. Most Java IDEs support easy debugging, essentially clicking the debug button rather than the run button. Set a number of breakpoints sprinkled through this code to catch requests and call the API endpoint from your frontend.
is Document the parent? User a child?
User.addTodocument(someUser)
then Document.merge()

Is it possible to resolve navigation outcome in order to validate it?

I've got a WebFilter that redirects to the login page in my application. In order to redirect back to the referring page I've also added a view parameter called redirectOnLogin which is then used on successful logins in order to perform the final navigation.
If one were to manipulate this query parameter, one could easily provoke JSF navigation errors. I would therefore like to pre-empt this by checking that the outcome is valid but I've not been able to uncover a mechanism for pre-validating a JSF outcome.
Easiest and best is to make sure the redirectToLogin parameter cannot be manipulated. Or that manipulation is detected.
You could solve this in (at least) two ways
Taking the original page name, adding a 'salt' to it and creating a hash.
Addin this has that in the request to the login server
Make sure it is returned by the login server (maybe adding it as # to the return page or as a param.
On receiving it on the 'redirectOnLogin' page, use the page name, the same salt and create a hash in the same way. Compare these and if they match you are fine, if they don't throw an error.
Or you could
Store the 'redirectOnLogin' page in a session to
Check on returning from the login server if it matches with the page you end-up on.

Update param and call action in controller on click?

I have been banging my head over this for the past 3 days (No kidding!!!)....It seems like a very simple thing but I am just unable to do it. So I'm putting the question out here, and am open to any method which would work.
BACKGROUND : An advanced search form on submission used to generate an URL too large for the server. So the form had to be submitted via POST. So far, so good. Form submitted successfully, and the result was displayed. Only remaining problem was pagination.
As it's a POST call, it ruled out will pagination. While will-pagination merges param page to the existing params, but the problem is that it shows on url which results in the same error.
QUESTION: So is there any way, if the user clicks a link NEXT, I can
a) Update the page param
b) Keep the existing params
c) While not changing the URL
d) Transfer control back to the action in controller?
The only solution so far suggested was have a hidden form, repopulate it's value and submit again. The form's pretty complex, so if there is a cleaner way I'd like to know.
I see what you want from your comment. So editing my reply accordingly. You want the information as to which column is being selected in the sort to be available to the controller without having that information available in the url string, and you want to use GET for this not POST
The challenge you have is that you want to preserve state between requests and you need a mechanism for doing this. POST preserves that information by sending it in the body of the POST request. GET does this by allowing you to populate the query string. The other method for preserving state is to use a cookie.
You can manipulate the url with javascript on the page. There are tutorials online for that.
But if you just want a simple controller hack as you say in your comment then the simplest one I can think of is to allow the user to make the GET request to the url with the query params in it, then handle this request in two steps - step one saves the query information to the cookie, step two redirects them to the url with that query string data stripped, and you look on the cookie to see if it has those fields before building your data for that page. It's hacky but it works.
def sort
session[:sort] = params[:sort]
redirect_to url_without_the_query_string
end
There is also the new html 5 feature:
window.history.replaceState(“Replace”, “Page Title”, “/mypage”);
from this stackoverflow answer here: How to remove query string from url using javascript
However I'm not sure I'd recommend messing with the url after load like that (I don't know enough about that solution so I'd recommend you read that answer and see if that fits). So, if you MUST lose that data from the url string, because you need to somehow pass it between requests you either have to use POST, or use the session cookie/redirect method I describe above.
Does your html <form> have an action attribute? If not, add one that points to the page.
If you want to preserve the current GET params so that results from the POST can use , you will also need to modify the javascript triggered on the heading links so that as well as (or instead of) modifying the query string, they write the same data to hidden form fields (which of course then get posted in the body of the request). Your JS library may already include helpful functions for that.

How do i filter and validate form fields in symfony 1.4?

Im trying to integrate a content filtering API. My plan was to use pre/post validators but I've lost may way somehow.
What i need to do is send the values to the content filtering service. If the response comes back that the content has been filtered it will also return a modified value for the field (basic profanity filtering... matches are replace with asterisks). Thats all well and good i can throw validation errors no problem - simple stuff.
However i dont want just throw errors. What needs to happen is that validation errors are thrown as normal, but the values are modified in the form for re-display.
Basically if someone posts something naughty i want them to get a validation error saying their post has been modified, they can re-submit the now "clean" post, or they can go about editing it to make it clean without the word replacements.
But do clean on a validator either throws an error OR returns cleaned values, not both. How can i go about implementing both? This will be used on many different forms with many different field names, so modifying methods on the form or a form base class isnt really an option - it needs to happen in the validation sub-framework somehow.
You can adjust this plugin for your needs http://www.symfony-project.org/plugins/WebPurifyPlugin

Create a new delete object_action in Symfony 1.4's admin generator

I'm using Symfony 1.4/Doctrine's admin generator.
There's a list of questions and I'd like to be able perform a custom object_action on each of them.
What I'm looking for is to mimic the _delete object action but doing some calculation before that.
So I created a new action :
public function executeListDeleteAndRecalculate(sfWebrequest $request)
{
// Do the calculation
// Then delete the question
}
And I'm adding it to my generator.yml:
object_actions:
delete_and_recalculate: ~
the new action shows in the admin generator but the delete part doesn't work.
I tried a bunch of thing to make it work:
Once all the calculation was done, I first tried to redirect to the questionActions/delete action.
I also tried to copy the executeDelete code to my new action.
But everytime I get the infamous
500 | Internal Server Error |
sfValidatorErrorSchema
_csrf_token [Required.]
So I'm guessing Symfony is doing some magic before actually deleting an object.
Do you know what I'm missing and what's the best way to implement a deleteAndRecalculate kind of action?
Edit:
Of course if I remove the $request->checkCSRFProtection(); everything works just fine. But I assume it's pretty important so I'd like to find a prettier solution.
This is because the delete link from the admin generator uses a token to prevent CSRF attacks.
Basically, it sets a token into your session and into an hidden field of a form then checks them one against another on the request. This is possible because the delete link in the admin generator is actually a (javascript generated) form (this is done to add a sf_method hidden field to simulate REST behavior).
For more information on how CSRF works and can be prevented, you can read further on Wikipedia: http://en.wikipedia.org/wiki/Cross-site_request_forgery
What you can do is use the same kind of link, you just have to pass a method parameter to link_to for it to generate a form, have a look at lib/generator/sfModelGeneratorHelper.class.php line 32 to see how it's done in the admin-gen.
You would then execute $request->checkCSRFProtection() in your executeDeleteAndRecalculate method, and proceed with whatever you want to do, including deleting the object by hand.
To properly generate the link, you would add a linkToDeleteAndRecalculate method in the Helper class of your module (that should lie in the lib/${YourModule}GeneratorHelper.class.php file of your module directory) and add the following code (directly taken and adapted from sfModelGeneratorHelper):
public function linkToDeleteAndRecalculate($object, $params)
{
if ($object->isNew())
{
return '';
}
return '<li class="sf_admin_action_delete">'.link_to(__($params['label'], array(), 'sf_admin'), 'delete_and_recalculate', $object, array('method' => 'delete', 'confirm' => !empty($params['confirm']) ? __($params['confirm'], array(), 'sf_admin') : $params['confirm'])).'</li>';
}
Please note that you have to change the route (I've put delete_and_recalculate by default but you might want to prefix it with your module's name) from the link_to call.
You can then use your delete_and_recalculate nearly like a builtin method from the admin generator (and pass it a label from the generator.yml for example)
Now that was the hard-way.
The easy way would be to subscribe to the admin.delete_object event, from your module's pre-execute for example, and to your job there :-)

Resources