How to hide URL parameters in Grails - grails

When a user is creating a new Load object, if the user checks the "Paid On Delivery" check box then they will be redirected to the Payment controller immediately after the new Load has been created. Several of the parameters needed to create a new Load are also used to create a new Payment, so I just pass the parameters in the redirect like this:
redirect(controller: "payment", action: "create", params: params)
This works fine, but it gives me a real nasty URL with all the parameters in it. How can I pass my parameters to another controller and keep them from appearing in the URL?
UPDATE:
I should say that I appreciate everyone's suggestions for such a little problem. Even with all the suggestions, it still seems the best way of doing this is the way I wanted to avoid, building the parameter map manually in the redirect call. It isn't that big of a deal, especially since there is only a few params, I just don't believe there is isn't a cleaner more automated way of fixing this.
def loadInstance = new Load(params)
if (loadInstance.save(flush: true)) {
Account.get(params.account.id).balance -= new BigDecimal(params.transactionAmount)
flash.message = "${message(code: 'default.created.message', args: [message(code: 'load.label', default: 'Load'), loadInstance.id])}"
if(params.paidOnDelivery){
redirect(
controller: "payment",
action: "create",
//There has to be a better way than this. Just writing "params:params" results in the values being wrapped in double quotes once they get to the Payment controller. If it wasn't for that then "params:params" would work great and I would not of had to ask this question :)
params: [
"account.id":params.account.id,
"dateOfTransaction":params.dateOfTransaction,
"dateOfTransaction_year":params.dateOfTransaction_year,
"dateOfTransaction_month":params.dateOfTransaction_month,
"dateOfTransaction_day":params.dateOfTransaction_day,
"dateOfTransaction_hour":params.dateOfTransaction_hour,
"dateOfTransaction_minute":params.dateOfTransaction_minute,
"transactionAmount":params.transactionAmount
]
)
return
}
redirect(action: "show", id: loadInstance.id)
}
else {
render(view: "create", model: [loggedByUsers:loggedByUsers, accounts:accounts, cargoProviders:cargoProviders, deliveredByUsers:deliveredByUsers, loadInstance:loadInstance])
}

You could do a server-side forward instead of a redirect. Simply replace:
redirect(controller: "payment", action: "create", params: params)
with:
forward(controller: "payment", action: "create", params: params)
Update
To fix the refresh problem you described in the comments, make the action that you forward to sends a redirect (instead of rendering a view), e.g.
class PaymentController {
def create = {
Integer paymentId = // Create a payment and get it's ID
redirect(action: 'show', id: paymentId)
}
def show = {
def id = params.id
// show the payment with this ID
}
}

Pass them via session parameters
or
Make the HTTP request server-side instead of a redirect and then just render the result to the client

The flash object can be used to achieve this (although I would leave the implementation as is).
Copy all your params to flash.
params.each {
flash[it.key]=it.value
}
Use the flash map instead of params map in the 'create' action.

i didn't try it but i think it will solve your problem.
redirect(controller: "payment", action = [POST:"create"], params: params)

Here's another suggestion :-)
Keep the redirect from Load to Payment as is. Check in Payment action if you receive a call with params, then save those in session or flash and redirect to same action without params. Restore params from session/flash using putAll and proceed with action.
Kind of like a Post-Redirect-Get in Payment, but actually a GetWithParams-Redirect-Get. Results in clean URL and no Refresh problems.
I use it in a app for clean URLs and for keeping states in different parts of the app (but since you don't need the latter you should remove it or keep it in flash).

Related

Appending String to end of URL - Beginner

I have a method where i am redirecting it to another action. Here's the code:
def redirectPage (String appNum) {
redirect(action: "showMan")
}
Now, once this the navigates to another page the URL will appear as http://myserver/myapp/showMan
I want this URL to appear as http://myserver/myapp/showMan?SomeIdentifier
I actually want to append this ID to the end of the page ?SomeIdentifier . How can i do it?
Use the param attribute of redirect according to the official documentation.
redirect(action: 'showMan', params: [param1: 'value1', param2: 'value2'])
This will result in
http://<host>/<your-app>/<controller>/showMan?param1=value1&param2=value2

Access Devise :user_id from Angular?

In my Rails app I'm using AngularJS and would like to display all submissions to the site that are associated to the :current_user; I use Devise.
Here is my factory:
var brainDB = angular.module('brainDB',['ngResource']).factory('userSubmission', function($resource){
var service = $resource('/api/:user_id/submissions/:id', {user_id: '#user_id'}, {id: '#id'} );
return service;
});
My Angular controller:
brainDB.controller('SubmissionsCtrl',['$scope', 'Submission', 'userSubmission',
function($scope, Submission, userSubmission){
$scope.submissions = Submission.query();
$scope.userSubmissions = userSubmission.query();
}]);
And here is relevant code from my submissions controller:
def usersubmission
#submissions = current_user.submissions
#submission = #submissions.find(params[:id])
render json: #submission
end
I have set up a route, "/api/:user_id/submissions/:id" which works just fine if you visit it in your browser with a valid user_id. However, I don't know how to tell Angular what the current_user.id is. How should I go about doing this? I've been trying all day and am desperate.
When your page is built you would have session scope already. We did something like this by simply passing session id to an app level JavaScript variable in view layout, in our case we use HAML. Something like below in head block before ng-app kicks in.
globalAppCtx = {}
globalAppCtx['user'] = JSON.parse("#{escape_javascript session[:user].to_json}");
In above code, session[:user] is ruby session with user literal containing relevant Hash that was required in ng-app. Else where, in the angular code or elsewhere in JavaScript, you can refer to globalAppCtx['user']
IMO this shouldn't be very expensive and saves you an AJAX request to get the value. I could not think of any downside of this approach.

UrlMappings not returning 405 for different URLs on same controller

I wrote a REST controller, and I want to allow DELETE on the controller for /rest/id, but not /rest/. Here's my UrlMappings:
"/rest"(controller: "restController", parseRequest: true) {
action = [GET: "list", POST: "save"]
}
"/rest/$id"(controller: "restController", parseRequest: true) {
action = [GET: "show", PUT: "update", DELETE: "delete"]
}
I would expect to get "method not allowed"/405 when my DELETE request hits /rest, but I get a 404. I thought maybe the request was getting translated to /rest/0, but it's not hitting a breakpoint I put in the delete method of the controller.
Any idea why it works this way, or things I could look at to troubleshoot the issue?
I think it's a bit ambiguos since grails gets the 405s from the allowedMethods closure of the controller.
Since there is no mapping to DELETE for rest/, the framework is returning a 404, which seems to be correct.
The documentation seems to want you to do something like
delete(Long id) {
if(!id){
response.sendError 405
} else {
...
}
}
http://grails.org/doc/latest/ref/Controllers/allowedMethods.html
Might be worth raising a JIRA if you don't think this behaviour is correct. From the tests in grails-core, it doesn't seem to be dealing with 405s at all.

Is there a "rails way" to retry a POST after authenticating a user?

My user experience involves users submitting a form before they've authenticated (using omniauth). I started doing something like this:
def self.require_facebook_authentication!(options={})
before_filter :redirect_to_facebook_if_not_authenticated options
end
def redirect_to_facebook_if_not_authenticated
if !logged_in?
session[:param_cache] = params
session[:original_destination] = request.fullpath
redirect_to '/auth/facebook'
end
end
Then, on hitting the auth callback, redirect to a page that submits a form with the post params inline, for a total of 3 redirects (/stuff/new/ on POST -> auth/facebook -> facebook -> /auth/facebook/callback [ html template with POST form ] -> /stuff/create). I'd rather not create an authentication popup; instead, I'd like to navigate to a separate page, log in, and redirect to the completed action.
I'm fairly new to Rails, so I'm still learning - is this already built in to another framework? Am I missing something really basic? Thanks in advance!
if you are asking as to whether or not there is a "RAILS" way that will automatically post the data after a redirect, the answer is no (see https://stackoverflow.com/questions/985596/redirect-to-using-post-in-rails)
In my opinion the safest, easiest, and most RESTful way to achieve what you want would be to simply have the params you are eventually posting stored in session so that you can redirect back to the original 'new' page and have the form automatically prefilled with the post data. Sure this is one extra step for the user, but since REST doesn't allow for redirects to POSTs it is imo the cleanest way to go about it
There may be a better way, but if you render this after authenticating, then the client will Ajax post the form contents then redirect.
<script>
new Ajax.Request(<%= session[:original_destination] %>, {
method: 'post',
params: '<%= session[:param_cache].to_query %>',
onSuccess: function(){
window.location = '<%= session[:original_destination] %>';
}
});
</script>

How do can you make redirect_to use a different HTTP request?

At the end of one of my controller actions I need to redirect to a page that only accepts put requests. I have been trying to figure out how to get redirect_to to use a put request but to no success.
Is this possible? Or is there another way to accomplish this?
I don't think you are able to do this, and I suspect that the limitation is part of HTTP itself.
When using redirect_to - the redirection happens as a "302 Moved" header unless otherwise specified in the parameters.
Having a look at the HTTP Spec itself doesn't reveal any way to change the type of request the browser makes via redirect.
HTTP Redirects:
This class of status code indicates
that further action needs to be taken
by the user agent in order to fulfill
the request. The action required MAY
be carried out by the user agent
without interaction with the user if
and only if the method used in the
second request is GET or HEAD.
I think you may need to use JavaScript to achieve this functionality, or perhaps rethink the flow of control in your application.
If the action is in the same controller as where you're trying to redirect from, simply call the action and render the template like so:
def show
index
render :action => "index"
end
If it's not, then I don't know how you do that.
Ok, so I found a solution to my problem. I found a very good write up on the situation here. My implementation looks like this:
private
def redirect_post(redirect_post_params)
controller_name = redirect_post_params[:controller]
controller = "#{controller_name.camelize}Controller".constantize
# Throw out existing params and merge the stored ones
request.parameters.reject! { true }
request.parameters.merge!(redirect_post_params)
controller.process(request, response)
if response.redirected_to
#performed_redirect = true
else
#performed_render = true
end
end
Then I called this method like this:
redirect_post :controller => 'registrations', :action => 'order', :_method => 'put', :authenticity_token => params[:authenticity_token]
So I was able to 'fake' a put request by making a post request (using redirect_post) and then assigning 'put' to a _method param. If you look at a normal put request all it is a post from a form with a _method param. So its a bit hackish but it gets the job done.
Also, you have to make sure that when you call redirect_post the values of your hash are strings otherwise errors will be thrown.
You could redirect to a different page that issues the put request from the client, using Javascript.

Resources