In my project I have to integrate the library and parse the files presented in csv format. To access the library and get the information form that file I use $ajax as follows:
<script>
$(document).ready(function(){
$.ajax({
type: "GET",
url: "http://stats.xxx.tv/osexternal/reports/xxxxx/xxx_2014_YTD/2014-03-12.csv",
contentType: 'application/json',
dataType: 'json',
username: 'xxxx#xxxx.com',
password: 'dT$xxxx%949',
success: function (){
console.log('success');
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
}
});
});
<script>
Can anyone let me know what's the wrong with this approach as I am getting cross domain problem.And please let me know any alternatives by using gems.
Thanks for your help in advance!
What you're running into appears to be a CORs issue of some kind. Things to note about CORs issues:
It is a security policy for Javascript, so it only affects calls in/from JS.
Being able to access it from the browser 'directly' doesn't have anything to do with CORs
CORS can be really irritating
Now, on how to solve it, you can try adding:
with_credentials: true to the Ajax arguments, but I have a feeling it's going to be something weirder than that... as well, since you have to include a username and password it's probably best not to expose those on the client for anyone to have...
So, what I'd do is make the call on the server (example is for a rails controller action, but the method could be used in a Sinatra app just the same) then return the CSV to the browser:
require 'net/http'
class MyController < ActionController::Base
# ...
def get_csv
uri = URI('http://stats.adap.tv/osexternal/reports/xxxxx/xxx_2014_YTD/2014-03-12.csv')
csv_request = Net::HTTP::Get.new(uri)
csv_request.basic_auth("username", "password")
csv_data = csv_request.request.body
csv
end
end
I'm assuming you are using Ruby because of your "gems" reference. Here's the doc for Net::HTTP
http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html
and a slightly easier to digest version:
http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
In general, it'll always be easier (and safer) to have your server make a request to an external host (this is a broad generalization and there are absolutely cases where this isn't what you want). If you need to make a cross domain request I'd suggest starting with:
http://www.html5rocks.com/en/tutorials/cors/
It'll probably give you some good tips to figure out why it's not currently working.
Best,
Related
Before going into more detail about the title, I just want to describe the problem at a basic level. I'm getting the dreaded 422, Unprocessable Entity error (ActionController::InvalidAuthenticityToken) after asynchronous requests in my app. What's the best way to handle this?
Loads of answers will say do:
skip_before_action :verify_authenticity_token
(which is now skip_forgery_protection in Rails 6), and then usually in the comments people get way more upvotes asking about whether or not that's a security risk. There are probably 5 threads like that.
The alternatives to doing this though, is sending the csrf token along with the POST request. So, most answers say make sure to include these headers
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
and also a csrf token like:
'X-CSRF-Token': csrfToken
The issue is getting the csrf token itself. I've tried displaying the contents of the header object with
const getHeaders = () => {
let headers = new window.Headers({
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
})
const csrfToken = document.head.querySelector("[name='csrf-token']")
if (csrfToken) { headers.append('X-CSRF-Token', csrfToken) }
return headers
}
and
let headers = getHeaders();
headers.forEach(function(value, name) {
console.log(name + ": " + value);
});
and that doesn't work. I'm guessing however I'm actually getting the token isn't working. Namely:
document.head.querySelector("[name='csrf-token']")
What's the best way to do this with plain ol' JavaScript, no other libraries? I've tried other suggestions and methods to attempt to get the token but so far all have failed. I'm assuming this lives somewhere on my browser but where? I can see all the data in Redux dev tools so the issue is definitely just this token as opposed to sending the correct data correctly (not to mention skip_forgery_protection completely solves it).
In the mean time sadly skip_forgery_protection works perfectly fine added to my Rails controllers, as security isn't the biggest concern in this stage, but I would rather fix the token issue. Thanks!
Note: I'm using Grails 2.5.5.
This is my method in the controller (I know save() shouldn't be a GET, but I'm just testing things out):
def save(Test cmd) {
println cmd.duration
println params.duration
}
This is my client code:
let data = JSON.parse($('#req').val());
$.ajax({
url: url,
data: data,
method: 'GET',
contentType: 'application/json'
});
When this flow is executed, on the controller side, cmd.duration does not print what was sent from the client side (instead it's the default value of zero since duration is typed as an int). On the other hand, params.duration does print what was sent from the client side.
So this indicates that it's not a problem with how the data is getting sent, but instead has to do with some data binding issue?
Also, just for reference, POST works perfectly fine with the above server-side code. The command object gets populated appropriately as long as I change the client code accordingly (changing method type and stringifying the JSON):
let data = JSON.parse($('#req').val());
$.ajax({
url: url,
data: JSON.stringify(data),
method: 'POST',
contentType: 'application/json'
});
I know there are similar questions out there, but it seems like most of them deal with issues with POST requests. So this is a bit different.
Any help is appreciated!
It looks like I just needed to remove the contentType in the ajax call on the client side for the GET request. Once I did that, everything worked as expected.
Not sure if that is expected behavior, but it works for me for now.
I'm trying to get rows that match a var, In my case a url. in the database and return the whole row as a json format.
Basically if url in table1 matches the url under eventurl in table2. Then the whole row is passed through to the ajax request as a jsonformat.
Heres what i have so far.
Routes.rb
resources :gig do
scope constraints: { format: "json" } do
get :gigdata, on: :member
end
end
In my ajax call i have this
url: 'gigdata/' + gigurlofevent , (no need to include the whole url ajax file here as its working elsewhere)
and in my controller i have this
respond_to :json, only: :gigdata
def gigdata
gig = Gigstable.where(eventurl: (params[:gigurlofevent]))
render json: gig
end
Now at the moment, I can't get into the gigdata with a byebug.
I'm wondering what i need to do/ what ive missed
Thanks
Sam
edit
Heres the start of the ajax call down the the success function
$.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader("Content-Type", "application/json");
},
type: 'GET',
crossDomain: true,
dataType: 'json',
url: 'gigdata/' + gigurlofevent ,
success: function(json) {
debugger;
Common problem:
You wrote:
(no need to include the whole url ajax file here as its working
elsewhere)
Actually you need to include complete code because common problem lays in Content-Type field. You need to specify content type of request in header like 'application/json'.
I am testing my backend api with curl
For example:
curl http://localhost/api/v1/some_action -H 'Content-Type: application/json' -vvv
Last parameter -vvv very useful to debug requests because it makes curl to output request and response with headers in console.
Another way to debug ajax
Open developer tools (for chrome it's: F12 or ctrl+shift+i), click on network tab and click on XHR filter button. Reload page with ctrl+r and execute your ajax request one more time. Information about your ajax request will appear in the window below filters. Now you can check out what kind of data comes from server and whats going wrong.
Golden rule
Rails server outputs all requests in console. Don't be shy to read output when something works not like you expected.
I have a Rails 4.0 app with an Ember.js frontend. I'm using Ember-Auth in conjunction with Devise to handle authentication. For the most part, everything works. However, if I use Jquery File Upload, then all subsequent queries to the server result in an InvalidAuthenticityToken error. The file upload itself works perfectly, but if for instance I visit the Organizations index page afterwards, I'll get the error. If I reload the page, then the errors stop coming and everything works fine again until I perform another upload.
The uploader looks like this:
didInsertElement: ->
$('#image_upload').fileupload
url: "/images"
formData: [{ name: 'auth_token', value: Whistlr.Auth.get('authToken') }]
success: (response) =>
#get('parentView').get('controller').set('image_token', response.token)
Even if I remove everything but the url, I get the InvalidAuthenticityToken afterwards. Any idea what's happening?
Following my suspicion that the session was getting reset, I tried passing the authenticity_token back the client side from the server. Surely enough, it was changing. So I manually placed the new authenticity_token in the headers. The code looks like this:
def create
image = Image.create(image_params)
render json: {image: image, authenticity_token: form_authenticity_token}, status: 201
end
$('#image_upload').fileupload
url: "/images"
dataType: "json"
formData: [{ name: 'auth_token', value: Whistlr.Auth.get('authToken') }]
success: (response) =>
#get('parentView').get('controller').set('image_token', response.image.token)
$('meta[name="csrf-token"]').attr('content', response.authenticity_token)
Note the last line of the javascript, which replaces the csrf-token. Thankfully, this works. As far as I can tell, it's also secure. (If you see a security flaw, please let me know!) But it still seems weird that this is necessary. Nowhere else in my app do I have to manually replace the csrf-token. Why is this happening here? Is it because the session is getting reset, and if so, why is that happening here but not elsewhere?
I am doing a site which submits a form to a different server. For upload progress tracking I use: for server side the NginxHttpUploadProgressModule und for client side - jquery-upload-progress. I have tested the setup by submitting the form to the same server and everything worked fine. Submitting to another server doesn't show the progress tracking(cross domain scripting). After hours of investigating this matter I came to the conclusion that the GET request generated by JQuery is at fault.
The query looks like this:
http://domain.com/upload/progress/?X-Progress-ID=39b2825934dbb2f33fe936df734ff840&callback=jsonp1249230337707&_=1249230345572
From the NginxHttpUploadProgressModule site:
The HTTP request to this location must have either an X-Progress-ID parameter or X-Progress-ID HTTP header containing the unique identifier as specified in your upload/POST request to the relevant tracked zone. If you are using the X-Progress-ID as a query-string parameter, ensure it is the LAST argument in the URL.
So, my question is how do I append the X-Progress-ID parameter to the end of the jquery GET request or set the X-Progress-ID header?
This doesn't work with jsonp(code from jquery.uploadProgress.js):
beforeSend: function(xhr) {
xhr.setRequestHeader("X-Progress-ID", options.uuid);
}
Currently the request is generated this way(code from jquery.uploadProgress.js):
jQuery.uploadProgress = function(e, options) {
jQuery.ajax({
type: "GET",
url: options.progressUrl + "?X-Progress-ID=" + options.uuid,
dataType: options.dataType,
success: function(upload) {
...
I solved the GET parameter problem(code from jquery.uploadProgress.js):
jQuery.uploadProgress = function(e, options) {
jQuery.ajax({
type: "GET",
url: options.progressUrl,
dataType: options.dataType,
data: "X-Progress-ID=" + options.uuid,
success: function(upload) {
...
Modified GET request looks like this:
http://domain.com/upload/progress/?callback=jsonp1249230337707&_=1249230345572&X-Progress-ID=39b2825934dbb2f33fe936df734ff840
The nginx webserver is now correctly responding.
However as Ron Evans pointed out the client side progress tracking part won't work unless NginxHttpUploadProgressModule is modified.
You simply cannot fire an XmlHttpRequest from a webpage, to a domain different from the page's domain. It violates security definitions that are default on all browsers.
the only thing that I can think of that you can do is to use Flash or Silverlight to initiate the progress calls (Flash and Silverlight can, given the correct crossdomain.xml setup, send async requests from the browser to preset list of domains)
or, setup a browser addin (say Firefox plugin, or IE ActiveX, or Embedded WinForm control) that can initiate calls without the same-domain restriction (as the request will not originate from the webpage, but from the browser itself)
You need to install the Apache module for upload status as well, just using the jQuery plugin will not work.
To respond to Ken, I suggest you familiarize yourself with JSONP spec, since JSONP was created specifically to handle cross-domain Javascript calls.
Anyhow, this code works great in Passenger/Apache WITH my modified Apache module. Without modifying the extension for Nginx it will not work with a JSONP call.
I made a minor modification that solved the problem for me, you can check it out here:
http://github.com/tizoc/nginx-upload-progress-module/commit/a40b89f63b5a767faec3c78d826443a94dc5b126