Redirect to URI sends back the POSTed params added in browser's URL bar - grails

Grails Version: 3.3.4,
Groovy Version: 2.4.14,
JVM Version: 1.8.0_161,
Kubuntu 14.04
I wrote a simple authentication form (in the end it will be POSTed through https):
<form action='auth' method='POST' id='loginForm' class='cssform' autocomplete='off'>
<p>
<label for='j_username'>Login ID</label>
<input type='text' class='text_' name='j_username' id='j_username' />
</p>
<p>
<label for='j_password'>Password</label>
<input type='password' class='text_' name='j_password' id='j_password' />
</p>
<p>
<input type='submit' value='Login' />
</p>
The controller is:
class LoginController {
def index() {
if (session.user?.name == 'test') {
render view: '/login/youarealreadyin'
}
else {
render view: '/login/auth'
}
}
def auth() {
def loginName = params.j_username?.trim()
def pass = params.j_password?.trim()
if (loginName == 'test' && pass == 'TEST' ) {
session.user = [name: loginName]
redirect uri: '/'
}
else {
render view: '/login/denied'
}
}
}
After correct login and redirect to uri: '/' - I see the name and password in the URL field of the browser:
http://localhost:8080/?j_username=test&j_password=TEST
I could swear that this didn't happen with grails 3 in the first versions... I cannot remember when...
It would be nice, not to send back the POSTed password as GET params in the URL.
If I render a specific view instead to redirect it doesn't happen.

If you are reporting this as undesired behavior, our GitHub issue tracker at https://github.com/grails/grails-core/issues is a better place to do that. This has already been reported though at https://github.com/grails/grails-core/issues/10965 and it looks like the fix has been verified in 3.3.5.BUILD-SNAPSHOT and looks good.
If you are simply asking if this is intended behavior, it isn't.

Related

Grails - hide URL paramters after redirect

I'm trying to set a gender filter on a list of users by using the form below for switching search preferences between male and female.
Filter form:
<div class="datePreferencesContainer">
<g:form controller="browse" action="updateDatingPreferences" method="post">
<b>Which Gender:</b>
<p>
<input type="radio" ${datingPreferences.whichGender==gender.MALE.code() ? "checked" : ""} name="gender" value="${gender.MALE.code()}">Male
</p>
<p>
<input type="radio" ${datingPreferences.whichGender==gender.FEMALE.code() ? "checked" : ""} name="gender" value="${gender.FEMALE.code()}">Female
</p>
<p><input type="submit" value="Update"></p>
</g:form>
</div>
(minimal) Browse Controller:
class BrowseController {
def list() {
DatingPreferences datingPreferences = springSecurityService.getCurrentUser().datePreferences
render(
view: "index",
model: [
users: User.list(params),
userCount: totalCount,
datingPreferences: datingPreferences,
gender: Gender
]
)
}
def updateDatingPreferences() {
try {
browseService.updateDatingPreferences(params)
} catch (ValidationException exception) {
log.fatal("Exception occured while updating Dating Preferences: " + exception)
}
redirect(
controller: "browse",
action: "list",
params: params)
}
}
Workflow:
I'm opening http://localhost:8080/foo/browse/list and see some users.
Changing gender from one to another.
Then I'm redirected to http://localhost:8080/foo/browse/list?gender=2&format=
Question:
How can I get rid of the URL parameters, gender and format that is. I'm using the pagination plugin. and I don't see the pagination parameters in the URL so .I'm sure I'm doing something wrong. What would that be?
Thanks!
Update #1
The pagination parameters are shown in the URL.
You're specifically putting params into target URL in your redirect. Just remove it from here:
redirect(
controller: "browse",
action: "list")

grails error message in an independent gsp view

In my grails project I've built up a new view in which user can perform a search of entities.
I've created the gsp adding the method search() in controller and automatically creating the gsp as described here
In this gsp there is only one input field and a g:actionSubmit button. If I fill form with correct data everything works well, but if data does not have any correspondance I would see an error message in the view like the validation error messages with popups...but I don't know how to show it, because I'm not using any bean with this gsp.
In addition, after an error, I would render the same view, but with render(view: "search", model: [patientInstance: patientInstance]) the view is the same, but path is /index and not /search...
How can I show an error message? How can I have the right path?
here is the search()
def search()
{
def patientInstance = new Patient()
if(params.patient_textField == "" || params.patient_textField == " " || params.patient_id =="")
{
//here I would like to show message
//the redirect works correctly
redirect(controller: "patient", action: "search")
}
else {
def patientToShow = Patient.findById(params.patient_id)
redirect(controller: "patient", action: "show", params: [id: patientToShow?.id])
}
}
here is the snippet of gsp
<g:form>
<div id="patientDiv">
<label for="patient">
<g:message code="event.patient.label" default="Patient" />
</label>
<input style=" margin: 0px 10px 10px 0px;" type="text" name="patient_textField" id="patient_textField" value="" placeholder="${g.message(code: 'patient.choose', default: 'Insert Patient...')}" />
<input type="hidden" id="patient_id" name="patient_id" value="" />
<g:actionSubmit class="search" value="${g.message(code: 'default.search.label', default: 'Search Patient')}" action="search" ></g:actionSubmit>
</div>
</g:form>
EDIT:
solved problem of path changing render with redirect(controller: "patient", action: "search")
In the error portion of your code you can do flash.error = "Your error message here"
And in the gsp do something like:
<g:if test="${flash.error}">
<div class="alert alert-info">
${flash.message}
</div>
</g:if>
There is already a flash bean in scope. http://grails.org/doc/latest/ref/Controllers/flash.html

Grails - 404 File not found - but why?

i start to write a simple login formular. This is the code for the view:
<g:form controller="login" action="checkUsernameAndPassword">
<input type = "text"name="userNameField" value="userName"/>
<input type = "password"name="passWordField" value="passWord"/>
<input type = "submit" name="loginButton" value="Login"/>
</g:form>
this is the code for the controller:
class LoginController {
def index = {
render(view: "login")
}//endMethod Index
def checkUsernameAndPassword = {
[userName = params.userName ,passWord = params.passWord];
}//endMethod checkUsernameAndPassword
}
as you can see, it doesnt do anything yet, i just wanted to print the values on the screen, however i get a 404 message (i run the file on local host)
The requested resource (/projectName/hello/checkUsernameAndPassword) is not available.
I just cant figure out why. Would be great if any of you guys have a tip for me.
beste regards,
Daniel
Edit (Change 1):
def checkUsernameAndPassword = {
render(view: "login",model: [userName: params.userName ,passWord: params.passWord])
}//endMethod checkUsernameAndPassword
}
(Change 2)
//added this line in view
<div>Username: ${userName} Passwort: ${passWord}</div>
<g:form controller="hello" action="checkUsernameAndPassword">
means that you have HelloController with checkUsernameAndPassword action
But in your code sample you have LoginController so to get your form work, you must write:
<g:form controller="login" action="checkUsernameAndPassword">
<input type = "text" name="userNameField" value="userName"/>
<input type = "password" name="passWordField" value="passWord"/>
<input type = "submit" name="loginButton" value="Login"/>
</g:form>
P.S. In Grails world is sometimes much better to use GSP Tags instead of plain HTML because it will generate proper(in 99.99% of cases) HTML code for you.
So the best way to implement your form is:
<g:form controller="login" action="checkUsernameAndPassword">
<g:textField name="userNameField" value="userName" />
<g:passwordField name="passWordField" value="passWord" />
<g:submitButton name="loginButton" value="Login" />
</g:form>
P.S.2 Proper LoginController code(for the form described before)
class LoginController {
def index = {
render(view: "login")
}//endMethod Index
def checkUsernameAndPassword = {
[userName: params.userNameField ,passWord: params.passWordField];
}//endMethod checkUsernameAndPassword

How to submit form with multi buttons with same value asp.netMvc

I need to build voting web site so, I have couple candidates and below them a vote button,
how I can find which of the buttons was submitted
thanks
Give each of your buttons a name, like so (notice they are both "submit" buttons)::
<input type="submit" name="buttonYes" value="Yes" />
<input type="submit" name="buttonNo" value="No" />
Then, in your controller, capture a parameter for each of the two button names like this:
public ActionResult Index(string buttonYes, string buttonNo) { ... }
You can then tell which button was pressed by checking to see which of these two parameters is not null; the one which is pressed with a have a value equal to the "value" attribute of the button, the other one will be null:
if (buttonYes != null)
{
// Then the yes button was preseed
}
else if (buttonNo != null)
{
// Then the no button was pressed
}
else
{
// Neither button was used to submit the form
// and we got here some other way
}
The reason this works is because the web browser sends the information for the submit button that was pressed as part of the HTTP post to the web server. The button that was not pressed will not be sent with the post, and therefore the parameter will be null.
There are lots of ways to rewrite and optimzie this, but this is the essence of it and shows the fundamentals that are at work--you can play with it from there.
I wouldn't use the button value, I would set it up so that the url used to do the post encodes the vote itself. You could do this a couple of ways.
Use links
<div class="left">
<img src="/images/candidate/#Model.Candidates[0].ID" alt="#Model.Candidates[0].Name" />
#Html.ActionLink( "Vote for " + Model.Candidates[0].Name, "count", "vote" )
</div>
<div class="right">
<img src="/images/candidate/#Model.Candidates[1].ID" alt="#Model.Candidates[1].Name" />
#Html.ActionLink( "Vote for " + Model.Candidates[1].Name, "count", "vote" )
</div>
Use separate forms
<div class="left">
#using (Html.BeginForm( "count", "vote", new { id = Model.Candidates[0].ID } ))
{
<img src="/images/candidate/#Model.Candidates[0].ID" alt="#Model.Candidates[0].Name" />
<input type="submit" value="Vote" />
}
</div>
<div class="right">
#using (Html.BeginForm( "count", "vote", new { id = Model.Candidates[1].ID } ))
{
<img src="/images/candidate/#Model.Candidates[1].ID" alt="#Model.Candidates[1].Name" />
<input type="submit" value="Vote" />
}
</div>
Either of the above can be adapted to work with AJAX as well. Note, if you care, you'll need to build in some mechanism to detect vote fraud, e.g., add a one-time nonce to the url to verify that it isn't used more than once; track the number of times a user has voted if they are authenticated, etc.

View issue - multiple 'delete forms'

I have a really strange ‘bug’. I use this in my view:
<% foreach (var QualitativeGlobalFeatureValue in Model.PossibleValues)
{ %>
<% using (Html.BeginForm("DeleteQualitativeGlobalFeatureValue", "Features", FormMethod.Post, new { #class = "deleteForm" }))
{ %>
<%= QualitativeGlobalFeatureValue.Value %>
<%= Html.ActionLink("Edit", "QualitativeGlobalFeatureValueForm", new { FeatureId = Model.Id, Id = QualitativeGlobalFeatureValue.Id })%>
<%= Html.Hidden("QualitativeGlobalFeatureValueId", QualitativeGlobalFeatureValue.Id)%>
<%= QualitativeGlobalFeatureValue.Id %>
<%= Html.Hidden("FeatureId", Model.Id)%>
<input type="submit" value="Delete" class="link_button" />
<% } %>
<% } %>
This produces a bunch of forms which post to an action which then redirect to an action which in turn produces this view.
Here is some HTML:
<form action="/Features/DeleteQualitativeGlobalFeatureValue" class="deleteForm" method="post">b
Edit
<input id="QualitativeGlobalFeatureValueId" name="QualitativeGlobalFeatureValueId" value="3004" type="hidden">
3004
<input id="FeatureId" name="FeatureId" value="2103" type="hidden">
<input value="Delete" class="link_button" type="submit">
</form><form action="/Features/DeleteQualitativeGlobalFeatureValue" class="deleteForm" method="post">aa
Edit
<input id="QualitativeGlobalFeatureValueId" name="QualitativeGlobalFeatureValueId" value="9010" type="hidden">
9010
<input id="FeatureId" name="FeatureId" value="2103" type="hidden">
<input value="Delete" class="link_button" type="submit">
</form>
Now if I delete the value with the Id 9010 the resulting HTML is as follows:
<form action="/Features/DeleteQualitativeGlobalFeatureValue" class="deleteForm" method="post">b
Edit
<input id="QualitativeGlobalFeatureValueId" name="QualitativeGlobalFeatureValueId" value="9010" type="hidden">
3004
<input id="FeatureId" name="FeatureId" value="2103" type="hidden">
<input value="Delete" class="link_button" type="submit">
</form>
For some unexplainable reason it contains value="9010" rather than value="3004" although it uses the code QualitativeGlobalFeatureValue.Id
It just does not make sense. Is this some browser/caching issue? – I am using Firefox. Thanks!
Best wishes,
Christian
PS:
Actions:
[MembersOnlyAttribute]
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult GlobalQualitativeFeature(string Id)
{
QualitativeGlobalFeature QualitativeGlobalFeature = null;
if (TempData["ViewData"] != null)
{
ViewData = TempData["ViewData"] as ViewDataDictionary;
}
try
{
QualitativeGlobalFeature = FeatureService.GetQualitativeGlobalFeature(Id);
}
catch (Exception e)
{
ModelState.AddModelError("Exception", e.Message);
}
return View("GlobalQualitativeFeature", QualitativeGlobalFeature);
}
[MembersOnlyAttribute]
[AcceptVerbs(HttpVerbs.Post)]
public RedirectToRouteResult DeleteQualitativeGlobalFeatureValue(string QualitativeGlobalFeatureValueId, string FeatureId)
{
try
{
FeatureService.GetQualitativeGlobalFeatureValueRepository().DbContext.BeginTransaction();
FeatureService.DeleteQualitativeGlobalFeatureValue(QualitativeGlobalFeatureValueId);
FeatureService.GetQualitativeGlobalFeatureValueRepository().DbContext.CommitTransaction();
}
catch (Exception e)
{
ModelState.AddModelError("Exception", e.Message);
FeatureService.GetQualitativeGlobalFeatureValueRepository().DbContext.RollbackTransaction();
}
TempData["ViewData"] = ViewData;
return RedirectToAction("GlobalQualitativeFeature", new { Id = FeatureId });
}
I suspect the following. You click on the delete button for the 9010. The form is posted and the POST request contains QualitativeGlobalFeatureValueId=9010. In the controller action the same view is rendered. Here's the gotcha. When you write this:
<%= Html.Hidden(
"QualitativeGlobalFeatureValueId",
QualitativeGlobalFeatureValue.Id)
%>
The HTML helper (and not only this one) will first look if there's a request parameter with the same name as the name of the field (QualitativeGlobalFeatureValueId) and will use this value instead of the one you specified as the second argument (that's the way it is, don't ask my why, it's by design). So to fix this the only way is to manually render the hidden field:
<input
id="QualitativeGlobalFeatureValueId"
name="QualitativeGlobalFeatureValueId"
value="<%= QualitativeGlobalFeatureValue.Id %>"
type="hidden"
/>
You can put breakpoints in the markup and debug as it renders through, though it doesn't allow putting breakpoints on client markup or <% lines, so you need to find a line continuation.
Are you sure that it isn't a sort reordering or something like that, maybe the results aren't sorted, and that result is later on?
HTH.

Resources