Rails redirect with html respond with js - ruby-on-rails

I know this question has been asked in different ways before, but in my case, I reviewed many answers and nothing seems to work. My case is simple:
I am making an api call to https://www.facebook.com/v4.0/dialog/oauth?... with a redirect to /auth which is a GET. It returns a code with which I am retrieving the access_token.
I added a auth.js.erb that should open a modal.
Very basic.
After storing the access_token, I want to redirect to the user profile and open up a modal with JS to show the user some more info.
For some reason my JS in respond_to never gets executed.
Here's my simple code:
def auth
response = RestClient.post(
"https://graph.facebook.com/v4.0/oauth/access_token",
{
grant_type: "authorization_code",
code: authorization_code,
...
}
)
...
respond_to do |format|
format.html {redirect_to edit_user_registration_path} # This works
format.js # Never gets executed
end
end
Any ideas?

You might need to specify the format in url. Tried this?
yoursite.com/auth.js
or
yoursite.com/auth?format=js
It might depend on your routes, and if you're accessing via browser url or ajax, etc.

Related

Rails not redirecting on PUT request, sending 406 back instead

I'm working on a bell schedule creator for a school project I'm working on. The bell schedule creation on the client is handled with a React component, and when it comes time to update, the component calls this fetch request:
fetch(`/bell-schedules/${this.state.updatingId}`, {
method: 'PUT',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/html',
},
redirect: "follow",
referrerPolicy: 'no-referrer',
body: JSON.stringify({ name: this.state.name, schedule: json})})
On the server, these requests are handled by the BellSchedulesController, and the method for handling this type of route is coded as so:
# PUT /bell-schedules/:id
def update
set_bell_schedule
respond_to do |format|
if #schedule.update(name: params[:name], schedule: params[:schedule])
redirect_to action: 'index'
else
head 400
end
end
end
Index is a simple method that gets all the bell schedules and renders them in a table; that method works fine, other routes point to it and it renders normally. Unfortunately, when I send this PUT request, rather than getting a redirect to that page showing the table of bell schedules, I instead get back a 406 Not Acceptable. I also know that the bell schedule is actually updating, I've inspected the entry in the rails console.
How do I handle redirects in PUT requests? I'm also having an issue similar to this in a POST request, but I've found a hack around it there; that hack won't work in this one.
Googling your problem it seems to come from the respond_to ... |format| block, try getting rid of it and see if it helps. You don't seem to be using it correctly, you are not doing anything with the format and maybe that's why it's blowing up.
Edit: the issue appears to be two-fold - the use of respond_to and the redirect status. Updating my answer as follows:
Wrap redirect_to in format.html. As #joel-blum suggested, removing the respond_to block entirely also solves this issue; however, I still required the 303 status for this to work locally
Use a 303 (:see_other) status code for the redirect
def update
set_bell_schedule
respond_to do |format|
if #schedule.update(name: params[:name], schedule: params[:schedule])
format.html { redirect_to action: 'index', status: :see_other }
else
head 400
end
end
end
Explanation
respond_to
When using respond_to in your controller actions, you need to specify what format(s) your responses are targeted for. In this case, your request is Accept: text/html, so you need to specify that as the target format. format.all or removing respond_to entirely would also work here, unless you need to return different responses based on the Accept header.
303 Redirect
Standard 301/302 redirects for dangerous HTTP methods (anything other than GET) generally won't be honored by the browser.
The browser will attempt to redirect to index using the same HTTP method that the request was initially made in: PUT. This will result in a 404. Setting the redirect status to 303 ensures that the browser performs the index redirect with a GET request.
A typical redirect response to a GET request indicates that the requested resource has moved (temporarily or permanently).
For PATCH/PUT/POST/DELETE, The browser has to assume that the resource was found at the expected location, but its data has changed since the initial request was made. The server is just informing the client that the response for this action exists at another location.
For the sanctity of the HTTP contract, a different status code is needed to indicate this situation. 303 informs the client of the location of this response location and suggests that it can be accessed with a GET request.

Ruby on Rails Ajax call runs controller code, returns success, but not rendering view

I have a working search mechanism on my page; a form with a text field and 'search'-labelled submit button.
I’ve also got an animated raphael.js-based item on my page, with clickable items on it that I want to result in an executed search.
On click, I send to my controller params data intended to be search terms taken from the raphael objects.
The data is being passed, and the controller code runs, but the corresponding view fails to render so the click appears to the user to do nothing.
So on click, I make an Ajax call like this:
File searchthing.js
// Add a search function
circle.click(function (e) {
var csrch = this.data('label');
// alert('clicked ' + csrch);
e.preventDefault();
$.ajax({
type: "GET",
url: "/bubbleBar",
data: $.param({ srchterm: csrch} ),
success: function(){
// window.location.replace("/pictures");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("--- error: " + errorThrown);
}
});
});
The ‘bubbleBar’ action in my controller fires successfully, transfers the search terms, and redirects to my working index action, which handles searches:
File myController.rb:
def bubbleBarSearch
redirect_to pictures_path(:srchterm => params[:srchterm])
end
def index
# Initialize the pictures array
#
#pictures = Picture.first
if params[:srchterm].present?
# If the search button was hit, search pictures for the terms
#pictures = []
results = Picture.search(params[:srchterm], page: params[:page])
results.each do |x|
#pictures << x
end
# Also search keywords for the search terms
#
keywords = Keyword.search(params[:srchterm], page: params[:page])
# Retrieve images based on keywords
#
keywords.each do |kw|
pics = Picture.where(:id => kw.picture_id)
pics.each do |pic|
# Only add to results if unique
#
if !#pictures.include? pic
#pictures << pic
end
end
end
respond_to do |format|
format.html {
puts '---------------- ding -----------------'
# Redirect_to pictures_path
}
format.js {alert('dong');}
end
else
#pictures = Picture.all.order("RANDOM()")
end
end
The problem I’m having is that Ajax returns success, but the corresponding view for the ‘index’ action does not render.
If I uncomment the 'window.location.replace("/pictures”);' line in my JavaScript code, I can force the render on success, but I lose the search results that the controller code got for me. If I uncomment the 'redirect_to pictures_path' line in my controller, I of course get an infinite loop that errors out.
Research I’ve do done so far tells me I should be catching the response in the controller.
I’ve not worked with respond_to before, and it appears that format.html gets hit with the Ajax call, but ALSO fires when I manually do the search using the search field/button mechanism already set up on the page. I'm unclear as to why my Ajax call comes in as HTML rather than JavaScript.
So that means if I handle the rendering of the view manually in the controller, I’m not catching the case when the search hasn’t come through the already working mechanism and running the risk of rendering a page twice or otherwise introducing malfunction into my site.
I’m lost on how to make my click/Ajax call successfully render the desired view from the click function after the controller action happens. What am I missing here? What is the ideal methodology to achieve this, and not lose the controller data I need to successfully render?
Outputs from Ruby on Rails indicate that the server believes it is rendering the proper Haml view file, however I placed a JavaScript tag at the bottom of the file to log output to console and the console is empty after a click. If I navigate to the same view through the site, the statement is output to the console.
I've modified my Ajax success callback to have a parameter, and upon examination the variable contains the HTML code for the view. So is Ruby on Rails directing the output to Ajax instead of the browser? How can I then render that output from the Ajax success callback?
Successful calls to the controller contain a utf8 parameter that is not in my Ajax call. Is this a factor?
Redirects work by returning a status code of redirected to the browser, which causes the browser to make a subsequent request to the url returned by in the response header. So, the problem is that your bubbleBarSearch action is returning with a status of redirected to your ajax call. It is not actually running the code in your index action. Why not just make a route for your index action and call it directly?
Edit - So that was wrong, I see what you are asking now. To answer your question in your reply:
Ah, I see, it looks like you want to do what AlexeyKuznetsov suggested.
Instead of respond_to do |format| you'll want if request.xhr. When you make an ajax (xhr) request, the request.xhr condition will be true, otherwise rails knows its a standard request. I think your issue is that you are not responding correctly, this will render and return whatever html it's view renders, and return it to your ajax request. You can tell rails to render differently for different types of requests. To respond with a javascript snippet, you can use render js: (http://guides.rubyonrails.org/layouts_and_rendering.html search for Rendering Vanilla JavaScript) like
if request.xhr
render js: "alert('dong')" # this is an ajax request
else
puts '-------- ding ---------` # this is not
or you can render json render json:, which will pass back a json response.
if request.xhr
render json: {message: "ding"}
else
puts '-------- ding ---------` # this is not
then in your axax success function:
success: function(data){
console.log(data) // will have message: ding
// window.location.replace("/pictures");
},
format.js it is not your case. I think you should go this way:
if request.xhr?
# render data on ajax request
else
# render view
end
I have a working solution that accomplishes my goal. It yields the following console warning in chrome:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
However, it does render the appropriate results to my browser. If there is a more appropriate method to do this, I'd love to learn it.
I discovered that the entire HTML of the rendered page to redirect to - including the site headers and footers in application.html.haml - was being returned to the Ajax success callback, and not recognized as HTML by rails. So I am now replacing the entirety of the page body's HTML with that returned data. This was accomplished with three lines of code:
$.ajax({
type: "GET",
url: "/bubbleBar,
data: $.param({ srchterm: csrch} ),
> dataType: "html",
> success: function(response, status, request){
> $('body').html(response);
)};
I am still unsure if this - the page being 'rendered' to the success callback - is normal and expected. Also if my solution is the ideal way to change the browser to the desired page, or if there is something I could do instead to have this page rendered to the browser outside of the callback. Likely not as this is an asynchronous request.
I can imagine that this may cause problems for my site when deployed if a user is in the middle of other tasks while awaiting a search to complete, but I don't currently have a better solution. I'll be looking into restructuring the site layout to prevent such a problem.
I have also removed the respond_to block from the controller, as I could not determine any way to have it perform the action I required solely when I required it. It's possible that now that I'm aware of the dataType: ajax property, I could make a case that only this request could ping, but I was unsuccessful in determining what the appropriate response would be in any case of that statement, I only got ajax errors in all my attempts.
Thanks everyone for your suggestions, they helped me get as far as I have.

Redirecting to an external URL with Rails doesn't work

In a controller I attempt to do
redirect_to #url
#url is an https url which is correctly formatted
When I do it nothing happens in the browser
Logs show that there is a redirection, but in chrome inspect I see
Request URL:https://..myurl...
Request Headers CAUTION: Provisional headers are shown.
Origin:http://localhost:3000
Referer:http://localhost:3000/cars/105
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36
Query String Parametersview sourceview URL encoded
tId:6f6bfeaf-fd47-42ae-8e43-6b7118d21b0b
The network tab shows that it was canceled..
Am I missing something? Why isn't the redirection happening?
Based on your statement that you saw the request was canceled in the network tab in Chrome, it sounds like you're trying to do this in an AJAX request. Cross-domain AJAX requests are not supported by default. If you have control over the https://..myurl... application, you can implement CORS or JSONP.
Edit
A third approach, which is what you mentioned doing in one of your comments, is to get around the restriction on cross-site redirects for AJAX requests by redirecting on the client side, after the AJAX request completes (by setting window.location). However, if your ultimate goal is to have the redirect happen transparently—as part of the AJAX request rather than after it—you will need to implement CORS or JSONP.
After searching a while on (CAUTION: Provisional headers are shown)
I think it is related to some chrome extensions such as ad blocker or net-internals
Quoting another stackoverflow question"The message is there because the request to retrieve that resource was never made, so the headers being shown are not the real thing. As explained in the issue you referenced, the real headers are updated when the server responds, but there is no response if the request was blocked."
You can make sure that all the extension that are capable of blocking requests are disabled and try it again.
I think you need to specify that the protocol you want to use is using SSL. The ActionController::Base#redirect_to method takes an options hash, one of the parameters of which is :protocol which allows you to define:
redirect_to :protocol => 'https://'
If all of your redirects are going to be done via SSL it would make sense to put this in a before_action in your controller. You could define a method like:
def redirect_via_https
redirect_to :protocol => "https://" unless (request.ssl? || request.local?)
end
And add this to your controller:
class SpecificController
before_action :redirect_via_https
end
Can you visit the page and see it in Chrome without any security warnings?
I've had css etc. not load because I was requesting it via https to a local server with a unverified certificate. It's possible Chrome is checking behind the scenes before going to the url but I don't know that for sure.
Try redirect_to https://www.google.com and see if that works.
what is your rails version, because redirect_to method is working for external urls.
class HomeController < ApplicationController
def index
#url = "https://facebook.com"
redirect_to #url
end
It works.
I would suggest several troubleshooting steps:
Try another browser (Safari, Firefox), if it works on that browser, then perhaps a Chrome extension is blocking your visiting that redirected URL.
Try redirecting to other URLs, try both with and without https.
Check if your Rails action is an AJAX call or not. If it's an ajax request then redirect_to wouldn't work.
I think that the suspicions around AJAX are well founded. It's really hard to guess at what might be going on, given the narrow view into your application that we are presented with. Try changing
redirect_to #url
to
if request.xhr?
render :update do |page|
page.redirect_to #url
end
else
redirect_to #url
end
Also, as an aside, #url probably has excessive scope, given the redirect. You can most likely use url just as effectively.
Edit:
In light of information I previously overlooked, I am certain that the previous solution will correct the issue. Basically, some browsers are stricter about how they handle responses to xhr requests. With a standard redirect_to, you can potentially send a "regular" response for a xhr request. This can happen even if you have a *.js.rjs or *.js.erb that would typically contain the redirect code, as the redirect_to call will prevent that code from even being executed. If you have an app that has js that gracefully degrades, there is a likelihood that you will end up running into this issue sooner or later, as you appear to have a couple of times. I've found a couple of good ways to handle the basic issue.
Option 1:
One way is to simply adjust the redirect to look like this: redirect_to( #url ) unless request.xhr?, and then add the appropriate redirection logic to your *.js.erb file. This method is nice if you have additional javascript that needs to be run, in addition to the redirect.
Option 2:
Another solution that works well is to add something like this to your application controller:
def redirect_to(args)
if request.xhr?
render js: "window.location = '#{ _compute_redirect_to_location args }'"
else
super args
end
end
This is nice, because you don't need to sift through and modify your existing code, it just starts working everywhere. The other nice thing is that you can still qualify the redirect as the previous option, to allow for more complex javascript. You can even go crazy and add some additional application specific options, like a flash message, if you want to further extend the method and really make it work for you.
Option 3:
If you're not down with overriding existing methods, you could add something like:
def firm_redirect_to(url)
if request.xhr?
render js: "window.location = '#{ url }'"
else
redirect_to url
end
end
But then you have to modify all of your code.
I personally prefer a variation of option 2... Anyway, hope this helps - good luck.

Rails 3: HTTP 406 and blank page on trying to view json in browser

I am a little new with Rails and ajax, and I'm encountering the following frustration whilst trying to debug some fairly simple ajax requests in my controllers.
I have the following code:
respond_to do |format|
#format.html { render :json => db_clean_response.to_json }
format.json { render :json => db_clean_response.to_json }
end
When I try to debug this by manually entering the URL and parameters, the controller correctly prepares the response, but Rails responds with HTTP/406 Not Acceptable unless I uncomment the format.html command. (Note that the format.html stuff is just for trying to fix this.)
I don't want this function responding with html, as its only for ajax stuff. Besides, Rails responds with the json uglyprinted onto empty html, and I'd like to use a plugin like JSONView to prettify it.
What's going on here? I feel like the desired result is very simple, but something, somewhere is messing it up. When I try to debug ajax I want my browser to pull up the damn json without being lame :-(
It turns out that adding format=json to the URL parameters does what I want: forces Rails to return json with all the right headers

How to modify the default RoR behavior to AJAX CRUD?

I want a table to do CRUD, but I want it in an Ajax way, how can I modify the default RoR behavior, any suggestions on that?
I tried comment these code in destroy method, but it don't work, it saw missing the destroy.erb:
respond_to do |format|
format.html { redirect_to(categories_url) }
format.xml { head :ok }
end
It will default to HTML if you don't give (and accept) a different response. Check out this railscast to see how to set up an AJAX request and response with RJS (there's another on how to do it with jQuery).
Basically you'll need a format.js line. You can also make a view of the form .js.erb where you write JavaScript to run on the server response.
As a side note though, unless you have a very specific website, I'd suggest not doing only AJAX. It's not very accessible for users without JavaScript, like blind users with a screenreader.

Resources