I have a particular Rails controller method that returns some JSON when I do an javascript ajax request in the front-end.
However, I want to prevent users from directly typing in the url, which displays the JSON that the method returns. I also want to still be able to perform my ajax requests. How can I go about doing this simply? Thanks!!
Just a thought... You could do something custom in your respond_to block for html requests.
respond_to do |format|
format.html { ... } # give them a 404 response?
format.js { render :json => #obj }
end
Or maybe your html.erb template with that name could just show some kind of access denied message. Then you'd just have this:
respond_to do |format|
format.html
format.js { render :json => #obj }
end
You could wrap your action with
if request.xhr?
...
end
The respond_to filter in rails3 controllers is pretty sweet.
YourJsonController < ApplicationController
respond_to :json
def index
#non-json requests will receive a 406 error
end
end
In your routes.rb, you can add :via => :post so your URL accepts only POST requests. See "HTTP Verb Constraints" at http://guides.rubyonrails.org/routing.html
Related
I have rabl up and running.
I have this in routes:
get 'biblios/collection/:biblio_urn' => 'biblios#biblio_rabl', as: 'collection_biblio'
in the controller:
def biblio_rabl
biblio = Biblio.where(biblio_urn: params[:biblio_urn]).take
end
This url points to the correct result :
http://localhost:3000/dts/biblios/collection/urn:cts:froLit:ed_desmarez:1900
I would like that url to always respond using rabl and showing the the template dts/biblios/biblio_rabl.json.rabl
I mean without adding .json at the end of the url.
I have tried this in the routes.rb, but it doesn't redirect :
get 'biblios/collection/:biblio_urn' => 'biblios#biblio_rabl', as: 'collection_biblio', to: redirect('biblios/collection/%{biblio_urn}.json')
Is that possible at all?
You can force the response to be json by changing the request format in the controller:
request.format = :json
Then make sure you have a respond_to block like this because it's always better to be explicit about your responses:
def biblio_rabl
respond_to do |format|
format.json { json: Biblio.where(biblio_urn: params[:biblio_urn]).take }
end
end
I have an AJAX form that sends a POST request to the controller. The controller responds in JSON.
Here, reponse is JSON:
def send_form_response(response)
render json: response
end
The above works fine but I keep seeing examples that use respond_to. My form still works when I wrap my response in the respond_to block.
def send_form_response(response)
respond_to do |format|
format.json { render json: response }
end
end
Does using respond_to give me any benefits? Will anything bad happen if I don't? Or does it make no difference in this case?
respond_to is used to handle multiple responses in the controller#action
If the client wants HTML in response to this action, just respond as
we would have before, but if the client wants XML, return them the
list of people in XML format." (Rails determines the desired response
format from the HTTP Accept header submitted by the client.)
Say for example, If you want send_form_response(response) to respond with HTML and JSON, then you would do it like this
def send_form_response(response)
respond_to do |format|
format.html
format.json { render json: response }
end
end
You can do the same with respond_with
respond_to :html, :xml, :json
def send_form_response(response)
respond_with response
end
So, to answer your questions
Does using respond_to give me any benefits?
Not in your case, where you are requesting only one response
Will anything bad happen if I don't?
Not in your case, no.
Does it make no difference in this case?
No, not at all.
In my action I wish to only respond with processing if it was called from an AJAX request. How do I check?
I want to do something like this:
def action
#model = Model.find(params[:id])
respond_to do |format|
if (wasAJAXRequest()) #How do I do this?
format.html #action.html.erb
else
format.html {redirect_to root_url}
end
end
You can check for a header[X-Requested-With] to see if it is an AJAX request. Here is a good article on how to do it.
Here is an example:
if request.xhr?
# respond to Ajax request
else
# respond to normal request
end
If you're using :remote => true in your links or forms, you'd do:
respond_to do |format|
format.js { #Do some stuff }
You can also check before the respond_to block by calling request.xhr?.
Update:
As of Rails 6.1.0, xhr?() does actually (finally) return a boolean value.
https://github.com/rails/rails/commit/0196551e6039ca864d1eee1e01819fcae12c1dc9#diff-60b77e427ea7ba142faa477fac10b8d0134cede4e35a3b1953c425200fadf1acL267-L269
Original Answer:
The docs say that request.xhr?
Returns true if the “X-Requested-With” header contains “XMLHttpRequest”....
But BEWARE that
request.xhr?
returns numeric or nil values not BOOLEAN values as the docs say, in accordance with =~.
irb(main):004:0> /hay/ =~ 'haystack'
=> 0
irb(main):006:0> /stack/ =~ 'haystack'
=> 3
irb(main):005:0> /asfd/ =~ 'haystack'
=> nil
It's based on this:
# File actionpack/lib/action_dispatch/http/request.rb, line 220
def xml_http_request?
#env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/
end
so
env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/ => 0
The docs:
http://apidock.com/rails/v4.2.1/ActionDispatch/Request/xml_http_request%3F
I like using before_action filters. They are especially nice when you need the same filter/authorization for multiple actions.
class MyController < AuthController
before_action :require_xhr_request, only: [:action, :action_2]
def action
#model = Model.find(params[:id])
end
def action_2
# load resource(s)
end
private
def require_xhr_request
redirect_to(root_url) unless request.xhr?
end
end
request.xhr?
if this return 0 then it means its an ajax request, else it will return nil.
if you are using :remote => true in links,form, then your response would come in js form instead of HTML, JSON, etc.
def action
#model = Model.find(params[:id])
respond_to do |format|
format.js
if (wasAJAXRequest()) #How do I do this?
format.html #action.html.erb
else
format.html {redirect_to root_url}
end
end
end
you also need to create, action appropriate js file, in your case action name is action, so file name would be - action.js.erb. also shift our action code inside action.js.erb file
Using rails and .js.erb to make an AJAX request (and append values to a div), how do you prevent rendering a new layout? In other words, stay on the same page without going anywhere and just append the fresh data from the server in a div. No reloading the same page, no redirecting.
At the moment my controller looks like this
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
respond_to do |format|
format.js
format.html
end
end
and my form like zisss:
<%= form_tag update_shipping_order_path(#order), method: :put, remote: true do %>
<%= select_tag 'shipping_options', #options_for_select, onchange: 'this.form.submit()' %>
<% end %>
and my routes look like a so:
resources :orders do
member do
put :update_shipping
end
end
But I get a 'Template is Missing' error
Please help!!
You need to add a update_shipping.js.erb file under app/views/your_controller/ directory. Note the name of the javascript file should be same as the action. Since you have a remote:true in your form so rails will try to render a javascript template in your case update_shipping.js.erb.
Now in your update_shipping.js.erb file write some basic javascript to update the page elements like
#update_shipping.js.erb
$('.some-div').html(<%=j #model.some_value' %>)
Try this:-
respond_to do |format|
format.js { render :nothing => true }
format.html
end
If you don't want to render a layout, you can use !request.xhr? like so:
respond_to do |format|
format.html { layout: !request.xhr? }
format.js
end
If you're looking to get your ajax-powered JS to fire, you just need to call your .js.erb file the same as your view:
#app/views/controller/update_shipping.js.erb
alert("This JS is returned & fired after the Ajax request");
You'll be best doing this in your routes.rb too:
resources :orders do
put :update_shipping
end
A little late, I came across this searching for the same issue. It must of slipped out of my mind at some point while working with action cable, but what is needed is a http response code of no_content. Http response codes tell the browser how to act when a request is returned. Here is a link to a list of them, and their symbols in rails. More on 204 no content
Here is how it would look:
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
head :no_content #or head 204
end
edit: what solved the solution for me was a link provided by William Denniss in this stack overflow question
In rails three I have the following code for my destroy action in a photos controller
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
respond_to do |format|
if request.xhr?
format.js
else
format.html {redirect_to photos_path}
end
end
else
flash[:alert] = t('.photo.error_deleting')
if request.xhr?
redirect_to(photos_url)
else
redirect_to(photo_path #photo)
end
end
end
The goal is essentially to redirect to the index page if this is called from a standard link and render destroy.js if called from a remote link.
This works but I was wondering if there is a cleaner way of doing this in rails 3. Possibly using the respond_with operator?
Thanks
This should work for you:
respond_to :html, :js
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
else
flash[:alert] = t('.photo.error_deleting')
end
respond_with(#photo)
end
There is a good blog post about it here:
http://ryandaigle.com/articles/2009/8/10/what-s-new-in-edge-rails-default-restful-rendering
Here's a quote from the post about the logic:
If the :html format was requested:
If it was a GET request, invoke render (which will display the view
template for the current action)
If it was a POST request and the resource has validation errors, render
:new (so the user can fix their
errors)
If it was a PUT request and the resource has validation errors, render
:edit (so the user can fix their
errors)
Else, redirect to the resource location (i.e. user_url)
If another format was requested, (i.e. :xml or :json)
If it was a GET request, invoke the :to_format method on the resource and
send that back
If the resource has validation errors, send back the errors in the
requested format with the
:unprocessable_entity status code
If it was a POST request, invoke the :to_format method on the resource and
send that back with the :created
status and the :location of the new
created resource
Else, send back the :ok response with no body
A little more on the to_format part from the documentation:
First we try to render a template, if
the template is not available, we
verify if the resource responds to
:to_format and display it.
There is also a Railscast about it:
http://railscasts.com/episodes/224-controllers-in-rails-3