I have rails applications where I am loading comments using Ajax after page load.
class CommentsController < ApplicationController
respond_to :js
def index
#comments = Comments.all
respond_with #comments
end
end
It is working as expected. But bingbot is trying to access this url with which it leads to
An ActionController::InvalidCrossOriginRequest occurred in comments#index:
Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
like that it is coming for all url's which are only responding to js format.
I know about rack-cors, but it is for allowing cross side script access, but here it is not.
app/views/comments/index.js.erb
$('.comments_container').html("<%=j render 'comments' %>");
comments.js
jQuery(function() {
return $.ajax({
url: $('.comments_container').data('url')({
dataType: "script"
})
});
});
Assuming you need some help with CORS(Cross-origin resource sharing), You are getting error because your CORS policy is default to "denying" every direct XHR access.
You can use the rack-cors gem https://github.com/cyu/rack-cors to avoid this. Hope this help!
Related
At my work, we are in the middle of breaking up our Rails monolith. We are currently serving our react app through the asset pipeline. I am implementing JWT for authentication. I am trying to pass the token in the url without using sessions/cookies as part of an admin impersonating a user.
class ReactPagesController < ApplicationController
def index
render :index #this will open up the react app
end
end
Is it possible to render a view and pass along parameters in the url?
I want this to open up the index page with url parameter (i.e. localhost:4000/users?jwt=abc.def.ghi
I've tried doing something like render :index, jwt: abc.def.ghi, but that doesn't work. Is the only way to do this via redirect_to?
You are actually defining a redirection:
You want to go to localhost:4000/users
The page display the index page, and URL becomes localhost:4000/users?id=123
For the normal webpage, changing URL will make the browser redirect. As you can see the result when executing this JS in the Chrome Console:
window.location.href = "google.com"
the browser will redirect you to google.com
So, for a Rails's application, you should do a redirection by redirect_to to achieve the current needs.
However, if you really want to change the URL without redirection, you can do it via Javascript. Just use window.history to change your URL
Your controller:
# app/controllers/users_controller.rb
def index
#desired_id = 123
end
and your view
<%-# app/views/users/index.html.erb %>
<!-- rest of HTML -->
<script>
window.history.pushState("", "", "?id=<%= j #desired_id %>");
</script>
you can use redirect_to
redirect_to users_url, id: 5
will get you to /users?id=5
I don't think so, render is for rending the view and has nothing to do with the setting the url.
ruby docs
I'm using the rails-api gem to have just a Rails API and using Angular to power my frontend. Whenever I use $http, it will only work if I pass in params instead of data. Here's an example with trying to log in a user and create a new session:
'use strict';
app.controller('LoginCtrl', function($scope, $location, $http, tokenHandler) {
$scope.login = function() {
$http({
url: 'http://localhost:3000/api/admins/sign_in',
method: 'POST',
params: $scope.admin
}).success(function(data) {
if (data.success) {
$scope.ngModel = data.data.data;
tokenHandler.set(data.data.auth_token);
$location.path('/admin/blog');
} else {
$scope.ngModel = data;
$scope.user.errors = data.info;
}
}).error(function(msg) {
$scope.admin.errors = 'Something is wrong. Please try again.';
});
};
});
If instead of params I used data: { admin: $scope.admin }, Rails complains to me that params[:admin] is nil. It seems to not be coming through at all.
However, if I use params, I get this:
Started POST "/api/admins/sign_in?email=raderj89#gmail.com&password=[FILTERED]" for 127.0.0.1 at 2014-09-07 20:08:04 -0400
Processing by Admin::SessionsController#create as HTML
Parameters: {"email"=>"raderj89#gmail.com", "password"=>"[FILTERED]"}
Which I can work with. It's just weird that it seems to only work when the request is processed as HTML. When I use data, I get this:
Started OPTIONS "/api/admins/sign_in" for 127.0.0.1 at 2014-09-07 20:36:24 -0400
Processing by Admin::SessionsController#create as */*
Is it suppose to say processing by */*? I'd think it should understand it's supposed to process by json specifically.
My sessions controller looks like this:
class Admin::SessionsController < Devise::SessionsController
skip_before_filter :verify_authenticity_token
before_filter :authenticate_user!, except: [:create]
respond_to :json
# ...
end
The weird thing is I definitely got it working the first time just using data: { admin: $scope.admin }, but ever since, the params seem to never come through unless I use params: $scope.admin.
ALSO:
I'm using Devise for authentication, and I had to add this to my ApplicationController:
class ApplicationController < ActionController::API
include ActionController::MimeResponds
before_filter :set_cors_headers
before_filter :cors_preflight
private
def set_cors_headers
headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = "3628800"
end
def cors_preflight
head(:ok) if request.method == :options
end
end
Anyone ever dealt with this before?
I've finally got it working and while I'm still confused, I think I've got somewhere close to what the problem was: My CORS configuration in my Rails API.
From what I've learned, Angular sends data in JSON format by default. This goes through as "Content-Type:application/json;charset=UTF-8", whereas in jQuery AJAX requests, it goes through as "Content-Type:application/x-www-form-urlencoded; charset=UTF-8", and is converted to a query string using $.param(). I'll admit, I've probably heard this before, but haven't truly registered this fact and its effects until now.
In my application controller, I configured my CORS settings like so:
def set_cors_headers
headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = "3628800"
end
def cors_preflight
head(:ok) if request.method == :options
end
AppConfig is just an OpenStruct that tells my Rails API what origin to accept requests from. And then everything else was supposed to simply set the CORS headers.
For some reason of which I'm still not sure, this wasn't working for JSON requests. I got the above code from a tutorial using Angular and Rails, and in the case of the tutorial, they manually stripped out the asset pipeline, leaving everything else about Rails in, whereas rails-api strips out some Rails configuration. This may be why setting the CORS headers in ApplicationController wasn't working.
What did work was to use the rack-cors gem and then add this bit to development.rb:
config.middleware.use Rack::Cors do
allow do
origins 'localhost:9000'
resource '*', :headers => :any, :methods => [:get, :post, :options, :delete]
end
end
This tells my app to accept requests from localhost:9000, and to accept any headers. I thought I was accomplishing that with headers['Access-Control-Allow-Headers'] = '*' in my ApplicationController, but I guess not. Once I specified Rails to use those middleware settings, everything worked perfectly. My Rails API can now accept application/json from my Angular app.
If someone could fill in the gaps where I'm still confused, I'd appreciate it. But I hope this helps others.
You can send either :params or :data (or both, I guess). According to the angularjs docs at https://docs.angularjs.org/api/ng/service/$http
params – {Object.} – Map of strings or objects which
will be turned to ?key1=value1&key2=value2 after the url. If the value
is not a string, it will be JSONified.
data – {string|Object} – Data to be sent as the request message data.
The controller is expecting http-type parameters/form data, so passing the object via params works - it gets converted, whilst passing the same via :data doesn't because it doesn't get converted.
I don't know if there is a smart way to unpack the data format at the Rails Controller end, but you can convert the object within your $http request into serialized parameters using $.param(data)http://api.jquery.com/jQuery.param/
data: $.param($scope.your_data_object) e.g. $scope.admin
and then unpack params[:data] at the controller.
I am making an application where each text box on a page has a "translate" button next to it. When the user clicks on this button, the text in the box would translate to english, via a function I would define in that page's controller. (If the text is already english, the button would not show up)
Where would I start going about how to do this? There are several rails/ajax tutorials on the web. Are there any that go about solving a use-case semi-similar to mine? I'm contacting the server for translating the text, so is this even an ajax request anymore?
I'm contacting the server for translating the text, so is this even an
ajax request anymore?
Yep.
Ajax is basically just a technology which allows Javascript to send requests on your behalf. It stands for Asynchronous Javascript And XML - basically meaning it can send data / requests "behind the scenes"
When you say your function needs to send a request to the server, you're going to be using ajax to do that, unless you want to reload the page.
--
Button
The way you'd handle the translation is as follows:
#app/assets/javascripts/application.js
$(document).on("click", ".translate", function(){
var text = $(".text_element).val();
$.ajax({
url: "pages/translate",
data: {text: text},
success: function(data){ }
});
});
This will allow you to send the request you require to the server, of which you can then process the response.
--
Controller
#config/routes.rb
resources :controller do
collection do
get :translate
end
end
To do this, you'll be best using the respond_to block in your controller:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
respond_to :js, :json, only: :translate
def translate
respond_with ...
end
end
I have a simple scenario where I want to request a page. The request format is AJAX. If there is some error in the controller/action logic for that request, I want to redirect to an error page. The issue is that a redirect is not a JavaScript response type, so I am not sure whether it will work.
If there are no errors, then I want the page to be updated via the appropriate JavaScript response type.
What is best practice to achieve redirect responses given that the request format is AJAX?
This blog post enlightened me on what I think is the right way to do this, if your ajax response is ajax; at least, in the unobtrusive javascript paradigm. In essense, the ajax call always returns a standard json package, which can be parsed for information payload or a redirect url.
You can also put this in your ApplicationController to redirect properly for AJAX requests:
class ApplicationController < ActionController::Base
# Allows redirecting for AJAX calls as well as normal calls
def redirect_to(options = {}, response_status = {})
if request.xhr?
render(:update) {|page| page.redirect_to(options)}
else
super(options, response_status)
end
end
end
If you use jQuery, you can use .ajaxError()
$(document).ajaxError(function(event, request, settings){
location.href = '/error.html';
});
or assume you do a ajax post
var jqxhr = $.post("example.php", function() {
// Do what it completed will do
})
.error(function() {
location.href = '/error.html';
})
I've built out a fairly complex Rails (2.3.8) app with a lot of jQuery ajax requests. There is an occasional bug, which I have difficultly replicating, where a jQuery $.ajax({..}) request will request a page it shouldn't (like the dash page, which is never called with an ajax request)...
What ensures is absolutely madness. Incredibly strange and terrible errors happen.
For at least a stopgap solution (the app is in production), how can I set up a before filter than will detect any unsolicited xhr/ajax request (or ANY such request on the given controller actions) and kill it before it hits the controller?
In any controller:
before_filter :stop_ajax, :only => [:dashboard]
application_controller.rb:
def stop_ajax
if request.xhr?
render :file => "#{Rails.root}/public/404.html", :status => 404
end
end