Redirect to referer after a POST Request - scala

I have a web application in Play. The web application consists of several pages. In every page there is a small flag that enables the user to change the language (locale) from german to english and back.
I handle this with a redirect to referer:
def referer(implicit request: Request[AnyContent]) =
request.headers.get(REFERER).getOrElse(mainUrl)
def locale(l: String) = Authenticated { user =>
implicit request =>
Redirect(referer).withCookies(Cookie(LANG, if (l == "de" || l == "en") l else "de"))
}
It is working fine. Well, at least for GET requests.
I have a specific page where the user has to input data in a form. This form is then POSTed to the server. Were errors found, the form is displayed again with the error messages, as usual. Now, if the user wants to change the language (by clicking on the flag), the redirect to referer does not work, because it tries to use a GET request, and Play complains that a GET route does not exist for this method (which is true).
I am solving this by caching the form and defining another method where the form is taken from the cache:
# User data is POSTed to the server
POST /create/insert controllers.MyCreate.insert()
# After a redirect the cached form is displayed again
GET /create/insert controllers.MyCreate.insertGet()
It works, but I don't like this solution. It does not seem normal to have to create another entry in the routes and another method just to adress this problem. I would need to add this hack for every POST route in my application!
Is there a more elegant solution to this?

You could change it into something like this (untested):
def changeLang(lang:String, returnUri:String) = Action {
Redirect(returnUri)
.withCookies(Cookie(LANG, if (lang == "de" || lang == "en") lang else "de"))
}
In you template you would output the route to changeLang in the link, you can get the uri via the request
#routes.Application.changeLang("en", request.uri).url
I suggest you make request implicit in your action and define it as implicit in your template so you don't need to pass it on to each template.
// in the controller
def myUrl = Action { implicit request =>
Ok(views.html.myTemplate("something"))
}
// in the template
#(title:String)(implicit request:play.api.mvc.RequestHeader)
Edit
As for the POST requests, it common (for these types of framework) to have POST requests simple handle stuff and then redirect to another page. The usual flow is like this:
Form submits to a handler
Handler does something with the form information
Handler redirects to a page
An example:
// Hooked up to a GET route
def edit(id:Long) = Action {
// render the view with a form that displays the element with given id
// if the flash scope contains validation information, use that in display
}
// Hooked up to a POST route
def editHandler = Action {
// validate the form
// if validation succeeds
// persist the object
// redirect to edit
// else
// put the form information into the flash scope
// put any validation messages into the flash scope
// redirect to edit
}
If you do not want to use this flow you need to have both a GET and POST route anyway. The user might do a page reload on the resulting page.

Related

How can I redirect to an error page in my Play app?

Or more precisely...
I already have an error page route defined like so:
GET /error controllers.pages.ErrorController.page(msg: String, returnTo: String)
And a controller method like this:
object ErrorController extends Controller {
def page(msg: String, returnTo: String) = ReceiverRestricted { implicit req =>
val action = List(Button(F8, "Continue", Call("GET", returnTo)))
Results.Ok(views.html.base(Html("Oops"), List(Html(msg)), None, action))
}
}
If I programmatically call, say, ErrorController.page("You did something daft!", "/home") I get to a page that looks like I want, ie:
Oops
You did something daft!
F8 Continue
However the url is ugly:
http://localhost:9000/error?msg=You%20did%20something%20daft!&returnTo=/home
I want to change this so the msg= query parameter doesn't appear in the url. How can I accomplish this? I tried removing the query parameter and redirecting to the error page with the message passed in via the flash cookie - that worked but reloading the browser page loses the message. I can't use the session cookie because I already store other data in the session almost upto its limit.
You can use flash feature.
Here is a sample:
In your controller you can redirect the user to error page with:
Redirect("/error").flashing(
"reason" -> "The item has been created"
)
And in Error action:
def error = Action { implicit request =>
Ok {
val reason = flash.get("reason").getOrElse("General Error")
//DO your stuff with reason variable
}
}
Obviously you can have as many as flash variables you want.
Since Play is restful and stateless, I can't see an easy way to pass on an error message during a redirect without using Play's flash. Of course you could store the message in an temporary cookie in the browser. Another possibility could be to store it in your database (or whatever persistence technology you use), but this seems to be like cracking a nut with a sledgehammer.

playframework 2.x form redirect with prefill

I'm trying to implement a user login form, I want to achieve:
when username doesn't exist in DB, return a flash message with previous
form data prefilled.
if any server side validation errors happened, return back to previous page, with old data pre-filled, display the errors
messages along side.
The problem now is, if I use flash scope, I need to use Redirect after post, but this will lose the pre-filled data.
If I use any status other than Redirect, I can't put data into flash scope.
What did I missing?
Don't use a Redirect for a failed login. You can return the same Form back to the login view with extra errors attached to it.
Something like this:
loginForm.bindFromRequest.fold(
formWithErrors => views.html.login(formWithErrors),
credentials => {
if(authenticate(credentials)) // dummy implementation
Redirect(controllers.Application.index)
else
BadRequest(views.html.login(loginForm.fill(credentials).withGlobalError("Incorrect login credentials")))
}
)
Then your view signature would look something like this:
#(loginForm: Form[Credentials])
#* Displays the first global error from the form, if any. *#
#loginForm.globalError.map{error =>
<h3>#error.message</h3>
}
And you'd pre-fill the form with values as before (I hope).
If there are multiple global errors, you can access them with globalErrors as it will access a Seq[FormError] instead of Option[FormError].
You can also attach errors to specific Form keys.
loginForm.withError("email", "I don't like your email.")
And would access them similarly:
#loginForm.error("email").map{ error =>
#error.message
}
You are using wrong concept, take a look to the Handling form submission doc, section: Validating a form in an Action
If form contains errors you are returning BadRequest (not Redirect)
If form IS valid anyway next check (i.e. DB query) returns an error you should do exactly the same thing as in formWithErrors so render the view passing incoming form to it (userData)
Finally if everything's OK, you can make your operation and redirect the user i.e. to main page or something...
Pseudo code (basing on doc):
def userPost = Action { implicit request =>
userForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.user(formWithErrors))
},
userData => {
// Check whatever you need...
if (afterCheckSomethingIsWrong){
// if something's wrong fill the `userForm` with `userData` and render the same view again...
// You can use flash scope here i.e. for placing error message
BadRequest(views.html.user(userForm.fill(userData))).flashing("error" -> "The account doesn't exist")
} else {
// if everything is OK, redirect to some page, outside the form handling process, i.e. main page
Redirect(routes.Application.index).flashing("success" -> "Fine you're logged now")
}
}
)
}

Play Framework sending POST request data to GET

How would I send request data from a POST request to a GET request using Scala Play?(Using Play Framework 2.1.1)
My goal is to have a single page "Reports" that works like this: The report is a GET request, so if needed you could bookmark this report. The report consists of a table of models, and each model row has a delete button at the end. I want to click the delete button, have it post the id to my controller then reload the page with the reports filter parameters still on.
Currently I have the delete button just adding to the get, which works correctly but the remove=id parameter stays in the request/address bar. Therefore it tries to delete this model every time the page is reloaded. What I would like to do is have this form POST and then remove the model, then send all the request parameters other than remove to a GET request.
I would rather do this without javascript/AJAX.
You could reconstruct a URL through using queryString and path from the request object. Then redirect that back (without the delete parameter)
How to get query string parameters in java play framework?
Or if you have the call setup in the routes file to parse out, use the reverse route minus the delete parameter.
Play Framework - Redirect with params
http://www.mariussoutier.com/blog/2012/12/10/playframework-routes-part-1-basics/
def index() = Action { request =>
import play.api.Play.current
println(request.queryString)
val allWithoutDel = request.queryString - "del" //del is the query parameter
println(allWithoutDel)
val url = request.path + // fold or map the allWithoutDel down to a URL string again
redirect(url)
}

CSRFGuard - request token does not match session token

I am trying to incorporate the CSRFGuard library in order to rectify some CSRF vulnerabilties in an application. However after configuring as specified here I am now getting the below messages in the log, when I navigate the application:
WARNING: potential cross-site request forgery (CSRF) attack thwarted (user:<anonymous>, ip:169.xx.x.xxx, uri:/myapp/MyAction, error:request token does not match session token)
Through including the:
<script src="/sui/JavaScriptServlet"></script>
On my main.jsp page the links have all been built incorporating the CSRFGuard token, e.g.
......./myapp/MyAction?CSRFTOKEN=BNY8-3H84-6SRR-RJXM-KMCH-KLLD-1W45-M18N
So I am unable to understand what I'm doing wrong that could cause the links to pass a token other than the expected value.
Please let me know if any additional information would make it easier to understand.
In case anyone stumbles across a similar issue:
Turned out that accessing the app using IE wasn't passing a token to an AJAX call, this would in turn result in the tokens being refreshed but the links in the already rendered page remained, causing the mismatch when clicked.
Found out the issue by building CSRFGuard myself from source and adding extra logging.
The primefaces commandlink and commandbutton seem to cause the csrfguard javascript to malfunction, if you have use these two component with ajax set to true (which is the default), it can prevent the token being injected after the ajax call
One of the possible fixes is to change the following 2 lines in Owasp.CsrfGuard.js file.
Change
function injectTokenForm(form, tokenName, tokenValue, pageTokens) {
var action = form.attribute("action");
To
function injectTokenForm(form, tokenName, tokenValue, pageTokens) {
var action = form.attributes["action"].value;
AND
Change
function injectTokenAttribute(element, attr, tokenName, tokenValue, pageTokens) {
location = element.getAttribute(attr);
To
function injectTokenAttribute(element, attr, tokenName, tokenValue, pageTokens) {
var location = null;
if (attr == "action") {
location = element.attributes[attr].value;
} else {
location = element.getAttribute(attr);
}

GET and POST request in one action. Playframework. Scala

Action create shows form:
def create = Action {
Ok(html.post.create(postForm))
}
How can i modify this action so that for GET request it would give out form and for the POST request it would process user input data, as if it were a separate action:
def newPost = Action { implicit request =>
postForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Posts.all(), errors)),
label => {
Posts.create(label)
Redirect(routes.Application.posts)
}
)
}
Wthat i mean is i want to combine this two actions.
UPDATE1: I want a single Action that serves GET and POST requests
It is recommended not to merge both actions, but modify routes to get the behavior you are expecting. For instance:
GET /create controllers.Posts.create
POST /create controllers.Posts.newPost
In case you have several kind of resources (post and comments, for instance), just add
a prefix to the path to disambiguate:
GET /post/create controllers.Posts.create
POST /post/create controllers.Posts.newPost
GET /comment/create controllers.Comments.create
POST /comment/create controllers.Comments.newComment
I tried once to accomplish similar thing, but I realized that I wasn't using framework like it was meant to be used. Use separate GET and POST methods like #paradigmatic showed and in cases like you specified "If we take adding comments to another action, we wouldn't be able to get infomation on post and comments in case an error occured (avoding copy-paste code)." - just render the page at the end of controller method with the view you like? and for errors etc. you can always use flash scope too? http://www.playframework.org/documentation/2.0.2/ScalaSessionFlash you could also render this form page with two or more beans and send them to controller side to catch related error messages and data.?