Determining where parameters passed from view to controller originated - ruby-on-rails

In my ApplicationController parent class I have an action method called dig which reads parameters via the params[] hash-like structure. I would like to have any view that builds a link to any controller via link_to helper function with the dig action to call the parent's action first. I'd like to then be able to determine either what that parameterized information represents (its type), or what created the link (the previous or originating view).
Any ideas on the best way to do this? Is there a way to pass an object via the link_to and then use its meta data? If so, would this break the rails paradigm?

If you're looking for what called what inside your code, Ruby's caller method is the basis for the stack dump that occurs when an exception occurs. You can tap into that and ask what the calling chain was at any point.
If you want to trace the incoming requests from the browser from an outside site, it becomes a lot more difficult because browsers don't like to reveal the last location any more. If the browser is being redirected around your own site you can use sessions or cookies to trace its movement.

I think that to have the originating link what you would need to do is something like this:
request.env['HTTP_REFERER']
This will get the URL where the action was originated, from there you can process the URL to get controller, action and id. Hope this helps!

Related

Correctly using services in Grails (per MVCS)

So I am a newbie grails developer, and was wondering how to properly use services per the MVCS design pattern. Right now in my service, I have a couple functions doing my application logic, and then I am referencing the service directly from my gsp. I read on a comment here that this is not good form. So right now I have
<g:set var="doc" bean="documentPartService"/> directly in my gsp.
So I am looking to change it, just like the comment states, by passing the information through the right channels.
One little hurdle I am coming across is passing information from my service to my controller to my gsp. For an example
<g:form name="inputForm" action="replace">
somecodehere...
<input value="Submit" type="submit">
</g:form>
then in my replace function in my controller
def replace(){
render documentPartService.replace(params)
}
then I have some logic in my service.
I have seen in some services I have looked at, they return the variable as a json (documentBody is a variable local to my service)
return documentBody as JSON
but I am a little puzzled on how to actually access this in my controller/view. I can't just
print documentBody in my controller, because it is only defined in my service. In the plugin services and controllers I was using as references, the controllers are 1 liners, just like I have, where the only thing they do is render servicefunction()
Can anyone shed some light on how I should properly design this?
When you post your form the action is replace - the replace action in your controller renders the output of your service back out as the output rather than returning a gsp bound to the replace controller definition.
In theory it should work as in the user will post - the post will return the action replace which will return that JSON response.
the alternative is
def replace(){
def myValue=documentPartService.replace(params)
render (view: 'replace', model: [myValue:myValue ])
}
then you could have a gsp page called replace which has ${myValue} defined in it which will be its results.
Also its probably a better idea to call your service with defined values passed via the controller to it:
def replace(String val1,String val2,String val3){
def myValue=documentPartService.replace(val1,val2,val3)
[myValue:myValue ]
}
UPDATE
It may seem much coming from me to be hinting the latter method considering you probably seen the first call i.e.
render documentPartService.replace(params)
calls similar to above methods within one of my plugins.. I guess if you took a look at the mailinglist plugin, you will notice with the help of Burt. The controllers/services are locked down to data types and exact expectations.
I can only tell you from experience that best practices are if they are to be defined functions that have no reason for expansion then stick with the latter method. In the case of for example ajaxdependancyselection using the render services output method, in some ways this helps keep it more backward/forward compatible. But actually thinking about it maybe those calls can be locked down. Will update the plugin soon

Zend Framework 2 restrict route to controller action

Have searched for this but i'm coming up short.
What is the best way to restrict access to an action in a controller in ZF2?
What i want is to only call a certain action in a controller (which is in a different module) if the request comes from a specific action - if not redirect to 404.
I see that i can use
$this->getRequest()->getUri()->getPath()
to get the current URL, but what i want is the previous one?
I could put this in a session variable, or pass it as a parameter in the route but is there a better way?
Reason is that i have a multi step form and i wish to only allow step 2 if the user came from step 1, otherwise 404.
What is the recommended ZF2 practice for this?
thanks
You should probably not handle this via urls. I'd look into Zend\Session\Container to manage state explicitly after you submit the first form.

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.

Make a URL available only from within application

Say, I have a URL to edit a post .../posts/1/edit
I don't want this URL to navigate to a link when a user pastes it on his browser. However, if I have a link to this URL from within my application, the application should route to this URL.
Is it possible to achieve something like that?
I have no authentication system in the application.
I am just looking to see if there is an elegant solution to it. Else, I am sure I can find ways around. Thanks.
Use request.referer to get the page the user is coming from. This should allow you to achieve what you want. Note that this is not 100% reliable, sometimes the referer header might not be set. But in most cases it will work.
Alternatively, you can set a session variable that indicates whether the user is allowed (or not) to open a page (set a session variable on the main page with session[:allowed] = true, check its presence on the hidden page). When the user does not have a session variable set, render error or redirect to the main page (something like render :status=>403 and return unless session[:allowed]).
One of the easy ways would be send a unique header with the GET request and in the edit page, make sure you verify this header and only if it is present go ahead else throw a 401.
Ofcourse to make it much simpler, send a unique argument like /posts/1/edit?application="not-browser-1234"
and in edit page
if $_GET[application] != "not-browser-1234"
exit

How do I make a "back" link?

I have a detail page that gets called from various places and has a nice readable url like
"www.mypage.com/product/best-product-ever".
The calling pages (list of products) have a more complex url like:
"www.mypage.com/offers/category/electronic/page/1/filter/manufacturer/sony/sort/price" and
"www.mypage.com/bestseller/this-week".
How can I make a backlink from the detailpage to the calling product list?
I cannot use javascript
I don't want to have the calling page in the URL, because it gets to long
I really want links between pages, no http-post
I cannot use Sessionstate
EDIT:
Sessionstate is ruled out, because if there are 2 Windows open, then they would share the same "Back" page information.
Like Lee said, use the referrer value:
Back
If you don't want the URL in the link because it's too long, try running some sort of simple compression algorithm over the URL, display the compressed data as unicode text and then append the compressed URL as a parameter to a redirect page, e.g:
Back
What about using the referrer header value?
Here's a crazy idea that will require a fair but of work and may not be healthy for performance (depending on your users).. but here we go:
Create a repository for caching 'ListResults' (and wire it to persist to the DB of you like.. or just leave it in memory on the server).
In short what this Repo can do is store a ListResult which will include everything to persist the state of the current view of the list any given user is looking at. This might include routes and other values.. but essentially everything that is needed to redirect back to that specific page of the filtered and sorted list.
As the ListResult item is added to the repo a small unique hash/key is generated that will be url friendly - something like this "k29shjk4" - it is added to the item along with a datetime stamp.
ListResults are only persisted from the moment a list gets off the default view (ie. no filtering, sorting and Page 1) - this will help in a small way for performance.
A ListResult item may never actually get used but all detail actionlinks on the particular list view have the ListResult.Key hash value added to the route. So yes, it might end up as a querystring but it will be short (url friendly) and if you wanna mess with routes more, you can tidy it up further.
For navigation "back" to the list, you may need a new small controller which accepts simply the ListResult.Key hash value and redirects/re-creates the state of the list view (paging, filtering and sorting included) from the lookup in the repo.
So we have met the requirements so far: no calling page in the url (in the sense that its not the whole page - just a hash lookup of it); no POSTing, no sessions, no js.
To stop the ListResult repo from getting to big (and dangerous: if you persist it to the DB), you can use a ASP.NET background service to periodically prune the 'old' routes by way of the timestamp.. and 'extend' the life of routes that are continuously being used by adding time to the stamp of a ListResult item when it's requested via the new controller. No need to persist a route indefinitely coz if a user wants a permalink to a list view, they can bookmark the long list route itself.
hope this helps somehow
Do you have a cookie?
If so, you can put it in there, or use it to create your own session state.
I think this is more like a "Back to results" then a generic "<< back" link, because you would expect the generic back link to return to the genetic list, not the heavily filtered list you described, right?
I don't know if this falls into your "no post" condition, but the only option I can see is having the Detail action be POST-only ([AcceptVerbs(HttpVerbs.Post)]) and include another parameter like string fullRoute which is converted to the 'link' on the detail page for "Back to results". Overload the Detail action missing the fullRoute param and have the overloaded action be a GET action so that the POST fullRoute value is not required (for when users are ok with the 'generic' "Back" link). This should serve both 'generic' GET requests to the Detail page and the POST request which will include the specific "Back to results" link for the filtered list.

Resources