How do I use OAuth with Javascript to access Khan Academy API? - ajax

Note: I do not have enough reputation to post all the links, so I just removed the first letter so that the text editor would not read it as a link. To go to them please just paste it into your address bar and add an h in the front.
I am trying to access user data from Khan Academy using their API, however, this is the first time I have used their API or AJAX. So far I have managed to retrieve data on videos or exercises, but I am having trouble with using OAuth to retrieve user data. Here is info on Khan Academy authorization. They use OAuth 1.0. Here is the OAuth library I am using. Here is the documentation for the KA API: http://api-explorer.khanacademy.org/ I have tried:
var oauth = OAuth({
consumer: {
public: '****************',
secret: '****************'
},
signature_method: 'HMAC-SHA1'
});
var request_data = {
url: 'http://www.khanacademy.org/api/v1/exercises/logarithms_1',
method: 'GET'
};
$.ajax({
url: request_data.url,
type: request_data.method,
headers: oauth.toHeader(oauth.authorize(request_data))
}).done(function(data) {
alert("done");
});
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>KA API Test</title>
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="generator" content="Geany 1.23.1" />
<!-- sha1 -->
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>
<!-- sha256 -->
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
<script src="oauth-1.0a.js"></script>
<script src="jquery-1.12.0.min.js"></script>
</head>
<body>
</body>
</html>
I have also tried this Javascript with the same HTML:
var oauth = OAuth({
consumer: {
public: 'qdMdMjjJQKrwJw2S',
secret: '7XeknfpVBzx8fGMK'
},
signature_method: 'HMAC-SHA1'
});
var request_data = {
url: 'http://www.khanacademy.org/api/v1/exercises/logarithms_1',
method: 'GET'
};
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.khanacademy.org/api/v1/exercises/logarithms_1", true);
xhr.setRequestHeader("Authorization", oauth.toHeader(oauth.authorize(request_data)));
xhr.send();
xhr.addEventListener("readystatechange", processRequest, false);
function processRequest(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
for (var key in response) {
console.log(key + ":" + response[key]);
}
alert(response.translated_description_html);
}
}
Both give this error in the console: "XMLHttpRequest cannot load http://www.khanacademy.org/api/v1/exercises/logarithms_1. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access."
Note: The XMLHttpRequest works fine and returns what I want before adding xhr.setRequestHeader("Authorization", "OAuth " + oauth.toHeader(oauth.authorize(request_data))); to it. However, that is just a test link which does not require authorization and I will change it later.
P.S. I would like to fix this error, but I also just need to use OAuth correctly and I think they are related because I did not have the error before using OAuth.

The problem is with Cross Domain Access Origins. You need to ask the API Owner to allow Cross Domains Origin.
Access-Control-Allow-Origin is a CORS (Cross-Origin Resource Sharing) header.
When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin response header to tell the browser that the content of this page is accessible to certain origins. (An origin is a domain, plus a scheme and port number.) By default, Site B's pages are not accessible to any other origin; using the Access-Control-Allow-Origin header opens a door for cross-origin access by specific requesting origins.
For each resource/page that Site B wants to make accessible to Site A, Site B should serve its pages with the response header:
Access-Control-Allow-Origin: http:// siteA.com
Modern browsers will not block cross-domain requests outright. If Site A requests a page from Site B, the browser will actually fetch the requested page on the network level and check if the response headers list Site A as a permitted requester domain. If Site B has not indicated that Site A is allowed to access this page, the browser will trigger the XMLHttpRequest's error event and deny the response data to the requesting JavaScript code.
Non-simple requests
What happens on the network level can be slightly more complex than explained above. If the request is a "non-simple" request, the browser first sends a data-less "preflight" OPTIONS request, to verify that the server will accept the request. A request is non-simple when either (or both):
using an HTTP verb other than GET or POST (e.g. PUT, DELETE)
using non-simple request headers; the only simple requests headers are:
Accept
Accept-Language
Content-Language
Content-Type (this is only simple when its value is application/x-www-form-urlencoded, multipart/form-data, or text/plain)
If the server responds to the OPTIONS preflight with appropriate response headers (Access-Control-Allow-Headers for non-simple headers, Access-Control-Allow-Methods for non-simple verbs) that match the non-simple verb and/or non-simple headers, then the browser sends the actual request.
Supposing that Site A wants to send a PUT request for /somePage, with a non-simple Content-Type value of application/json, the browser would first send a preflight request:
OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
Note that Access-Control-Request-Method and Access-Control-Request-Headers are added by the browser automatically; you do not need to add them. This OPTIONS preflight gets the successful response headers:
Access-Control-Allow-Origin: http:// siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
When sending the actual request (after preflight is done), the behavior is identical to how a simple request is handled. In other words, a non-simple request whose preflight is successful is treated the same as a simple request (i.e., the server must still send Access-Control-Allow-Origin again for the actual response).
The browsers sends the actual request:
PUT /somePage HTTP/1.1
Origin: http:// siteA.com
Content-Type: application/json
{ "myRequestContent": "Response in JSON" }
And the server sends back an Access-Control-Allow-Origin, just as it would for a simple request:
Access-Control-Allow-Origin: http:// siteA.com
See Understanding XMLHttpRequest over CORS for a little more information about non-simple requests.

Related

Unable to access Authorization header in Svelte

I am new to Svelte, and am trying to create a login page to an API. The API takes a username and password and returns an Authorization header. I see the authorization header in the F12 developer console, and I am able to access other headers via code, but not the Authorization header. I have enabled CORS on the server for localhost:8080.
<script>
const BASE_URL = ...;
export let username;
export let password;
let result;
let status;
let body;
let token;
let contentType;
async function doPost () {
const res = await fetch(BASE_URL + 'authenticate', {
method: 'POST',
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
'username': username,
'password': password
})
});
const text = await res.text();
status = res.status;
result = text;
token = res.headers.get('Authorization');
contentType = res.headers.get('Content-type');
if (!res.ok) {
throw new Error(result);
}
}
</script>
Please log in<br>
<input type="text" bind:value={username}/>
<br>
<input type="password" bind:value={password}/>
<br>
<button type="button" on:click={doPost}>Log in</button>
<br>
Result: {result}
<br>
Status: {status}
<br>
Token: {token}
<br>
Content-type: {contentType}
Response headers are as follows:
HTTP/1.1 200
Server: nginx/1.20.0
Date: Tue, 31 May 2022 18:59:09 GMT
Content-Type: text/plain;charset=UTF-8
Content-Length: 8
Connection: keep-alive
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:8080
Authorization: Bearer xyz...
The page displays as follows after logging in:
Result: Welcome!
Status: 200
Token: null
Content-type: text/plain;charset=UTF-8
Server side (spring boot) has the following annotation on the authenticate method:
#CrossOrigin(origins = "http://localhost:8080", allowedHeaders = "*", allowCredentials = "true")
As you can see, I am able to access the content-type header but not the authorization header. Any assistance would be greatly appreciated.
I figured it out. I needed to add exposedHeaders = "Authorization" to the #CrossOrigin annotation on the server side.
I literally solved this exact problem today. So... I'm not entirely sure why you cannot access the cookies sent back in the HTTP response, I believe it has something to do with not allowing js access to cookie related data for security reasons.
A preliminary issue I see, is that you should be sending the API auth token to the frontend, in the 'set-cookie' header, along with sending it in the HTTP response body, which I assume is JSON for your API.
I've never seen anyone suggest sending it in the 'Authorization' header like you have. I believe you are confused. I'll try and clarify the right way to do this and why you're most likely confused.
Your backend will generate an access token of some sort upon a successful login. Like I said, you send it back in the 'set-cookie' header, aswell as in the HTTP body.
Now when you read the response on the frontend, you can retrieve the Auth token from the HTTP response body, and use it in subsequent requests to authenticate to your backend server. The way JWT tokens are expected to be sent is in the 'Authorization' header of your request. This is where you're mixed up, the 'Authorization' header is used in subsequent authenticated requests to the server, not to send the Auth token from the backend to the frontend.
Now along with setting up the 'Authorization' header, you'll most likely need to send that same token in the 'cookie' header. You can do this by using the {withCredentials: true} option with fetch. This will send the cookie you sent in the 'set-cookie' response header after a successful login attempt, back to the server on all subsequent requests where you set this option.
Hope this helps, sorry I'm on my phone, so restricted with what I can write.

Stop sending preflight requests from axios.post

I have my micro-service developed using spring-boot and spring security and frontend is designed on react-hooks.
Now, while I am send some data to my micro-service using axios.post method, it send CORS preflight method i.e. options method because axios by default send content-type as application/json and application.json leads to send options request to server before any other request.
I have tried sending my request with different headers and content types as 'application/x-www-form-urlencoded' also I have used #cross-origin(*) at my server end.
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const response = await axios.post(ps.user_ms_url+ps.user_login,
{
username:values.email,
password:values.password
// headers:{'tokenvalue':'token'}
},
config);
I expect my browser to send only post request to the server, for that I am ready to change my headers as well.
Any help will be appreciated. Thanks in advance!
I found the solution for my query. As I mentioned above, our browser sends preflight request (means options request) before any other request if our request is not simple (here simple means: if request contains content-type : application/json or custom headers etc) and if we are sending this request to some other domain/ URL.
And our axios.post method carries content-type as application/json by default, that's why, my browser was sending multiple requests (means preflight request before any other request).
Now, I have changed my request content-type to application/x-www-form-urlencoded by sending data as params, as shown below:
var params = new URLSearchParams();
params.append('username', values.email);
params.append('password', values.password);
const response = await axios.post(ps.user_ms_url+ps.user_login,
params);
And handling this request at backend using #ModelAttribute annotation (Spring-boot). So, keeping request simple can stop preflight requests.
You can avoid CORS preflight request by proxying the request. Add this in your webpack development config
devServer: {
port: process.env.PORT || 3000,
proxy: {
'/api': {
target: 'http:localhost:8080',
pathRewrite: { '^/api': '' },
changeOrigin: true,
},
},
}
This means your request to /api/users will forwarded to http://localhost:8080/users.
If you are using create-react-app. just add "proxy": "http://localhost:8080" to your package.json. Check more info here
This looks to be server side CORS issue. You have to allow domains to access resources by providing correct response headers.
You can look at adding CORS headers in spring boot. Refer to this link
Hope that helps!!!

CSRF Token Name on Django Documentation is not Matching the Actual Name of the Variable in AJAX Header

I was struggling to send and recieve CSRF token, and I found, in the end, that Django was not able to get the token value because its name was different from the recommended one in its documentation. Why?
(I am doing AJAX on a HTTPS address and requests are cross-site.)
Django documentation recommends that I add token to AJAX header in following way:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Here, the name is X-CSRFToken, which somehow becomes HTTP_X_CSRFTOKEN.
On the other hand, Django is looking up the cookie under CSRF_COOKIE.
Line 278 in csrf.py of CsrfViewMiddleware:
csrf_token = request.META.get('CSRF_COOKIE')
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
I cannot change the variable name because I get this error:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
So, I ended up changing the variable name in the source code from CSRF_COOKIE to HTTP_X_CSRFTOKEN. Are there any way to make this work?
(I do not do #csrf_exempt, so please do not recommend.)
The problem is not from Django, if you read closely here: https://docs.djangoproject.com/en/2.0/ref/csrf/#how-it-works you will understand how it works and what kind of logic they follow.
The problem is that you are not allowing the headers:
Request header field CSRF_COOKIE is not allowed by Access-Control-Allow-Headers in preflight response.
If you search for this ACAH you will find that you must edit your server config file to allow this kind of posts.
The other case is that you may not be sending properly the header and that's why it's looking for the cookie. In that case you can try adding this to your header:
xhr.setRequestHeader('X-CSRFToken': $('meta[name="token"]').attr('content') });

Access-Control-Allow-Origin in preflight response doesn't enable cross-domain access

I am trying to send a CORS request using AJAX to a nodeJS server. I want to return some JSON data. I've found numerous tutorials online that all say the same thing, which I've tried, but I can't get this to work. Here's the AJAX request:
$.ajax({
url: "http://some.other.url.com:8880",
type: "GET",
crossDomain: true,
contentType: 'application/json'
}).then(function(response) {
$scope.allData = jQuery.parseJSON( response );
console.log($scope.allData);
}).fail(function(response) {
});
And here is the code on the server:
var path = url.parse(req.url).pathname,
match = router.match(path),
rescode;
console.log("---: " + req.method);
if (req.method === 'OPTIONS') {
var headers = {};
headers["Access-Control-Allow-Origin"] = "*";
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = false;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept";
res.writeHead(200, headers);
return res.end();
}
I'v also tried it without the return on res.end() i.e. not returning the OPTIONS preflight request, and that doesn't work either.
--Edit--
Here is the actual error message in the console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://other.domain.com:8880/. This can be fixed by moving the resource to the same domain or enabling CORS.
The server is getting the requests. Both the OPTIONS and then GET requests are hitting the server and being responded to. In fact, in the console log for the page making the AJAX request, I can click on the CORS error and see the response, and it is the correct data. But I can't seem to get the javascript to continue.
In regards to .done vs .then, they seem to work interchangeable. Or at least, in this example, the .then and .fail are working just fine.
You're correctly setting CORS headers in your OPTIONS preflight response, but you also need to set Access-Control-Allow-Origin (either to your origin or *) on your actual GET response. The GET response should respond with the same CORS headers, regardless of whether there was a preflight response or not. This means that it must send the appropriate CORS headers, but it does not need to send anything except for Access-Control-Allow-Origin. (If other non-simple components like non-simple verbs or headers are involved, they will be allowed or denied in the preflight; the actual GET response does not need to worry about them.)
The Enable CORS site has a CORS testing tool to help you see the headers involved in a request that you specify. I've used that tool to set up a test similar to your case (GET with non-simple Content-Type header). If we examine the results of that test (careful -- the steps are presented little bit out of order, but they're all there), we see a preflight response:
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
...
Access-Control-Allow-Origin: http://client.cors-api.appspot.com
Access-Control-Allow-Headers: X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept
And the final CORS response:
Content-Length: 0
Content-Type: application/json
Access-Control-Allow-Origin: http://client.cors-api.appspot.com
Cache-Control: no-cache
As you can see, the GET response also has a Access-Control-Allow-Origin header and no other CORS headers. If you have any further uncertainties, feel free to tweak the settings on that tool to run a wide range of other test cases.

jQuery $.ajax(), $.post sending "OPTIONS" as REQUEST_METHOD in Firefox

Having trouble with what I thought was a relatively simple jQuery plugin...
The plugin should fetch data from a php script via ajax to add options to a <select>. The ajax request is pretty generic:
$.ajax({
url: o.url,
type: 'post',
contentType: "application/x-www-form-urlencoded",
data: '{"method":"getStates", "program":"EXPLORE"}',
success: function (data, status) {
console.log("Success!!");
console.log(data);
console.log(status);
},
error: function (xhr, desc, err) {
console.log(xhr);
console.log("Desc: " + desc + "\nErr:" + err);
}
});
This seems to work fine in Safari. In Firefox 3.5, the REQUEST_TYPE on the server is always 'OPTIONS', and the $_POST data does not appear. Apache logs the request as type 'OPTIONS':
::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46
Why would this ajax call work in Safari, but not Firefox, and how do I fix it for Firefox?
Response Headers
Date: Wed, 08 Jul 2009 21:22:17 GMT
Server:Apache/2.0.59 (Unix) PHP/5.2.6 DAV/2
X-Powered-By: PHP/5.2.6
Content-Length 46
Keep-Alive timeout=15, max=100
Connection Keep-Alive
Content-Type text/html
Request Headers
Host orderform:8888
User-Agent Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
Connection keep-alive
Origin http://ux.inetu.act.org
Access-Control-Request-Method POST
Access-Control-Request-Headers x-requested-with
Here is a picture of the Firebug output:
The reason for the error is the same origin policy. It only allows you to do XMLHTTPRequests to your own domain. See if you can use a JSONP callback instead:
$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
I used the following code on Django side to interpret the OPTIONS request and to set the required Access-Control headers. After this my cross domain requests from Firefox started working. As said before, the browser first sends the OPTIONS request and then immediately after that the POST/GET
def send_data(request):
if request.method == "OPTIONS":
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response['Access-Control-Max-Age'] = 1000
# note that '*' is not valid for Access-Control-Allow-Headers
response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
return response
if request.method == "POST":
# ...
Edit: it seems to be that at least in some cases you also need to add the same Access-Control headers to the actual response. This can be a little bit confusing, since the request seems to succeed, but Firefox does not pass the contents of the response to the Javascript.
This mozilla developer center article describes various cross-domain request scenarios. The article seems to indicate that a POST request with content type of 'application/x-www-form-urlencoded' should be sent as a 'simple request' (with no 'preflight' OPTIONS request). I found , however, that Firefox sent the OPTIONS request, even though my POST was sent with that content type.
I was able to make this work by creating an options request handler on the server, that set the 'Access-Control-Allow-Origin' response header to '*'. You can be more restrictive by setting it to something specific, like 'http://someurl.com'. Also, I have read that, supposedly, you can specify a comma-separated list of multiple origins, but I couldn't get this to work.
Once Firefox receives the response to the OPTIONS request with an acceptable 'Access-Control-Allow-Origin' value, it sends the POST request.
I've fixed this issue using an entirely-Apache based solution. In my vhost / htaccess I put the following block:
# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]
You may not need the latter part, depending on what happens when Apache executes your target script. Credit goes to the friendly ServerFault folk for the latter part.
This PHP at the top of the responding script seems to work. (With Firefox 3.6.11. I have not yet done a lot of testing.)
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
header('Access-Control-Allow-Headers: '
. $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
header('Access-Control-Allow-Headers: *');
}
if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
exit(0);
}
I had same problem with sending requests to google maps, and solution is quite simple with jQuery 1.5 - for dataType use dataType: "jsonp"
Culprit is preflight request using OPTIONS method
For HTTP request methods that can cause side-effects on user data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method.
Web specification refer to: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
I resolved the problem by adding following lines in Nginx conf.
location / {
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
location ~ ^/(xxxx)$ {
if ($request_method = OPTIONS) {
rewrite ^(.*)$ / last;
}
}
I was looking through source 1.3.2, when using JSONP, the request is made by building a SCRIPT element dynamically, which gets past the browsers Same-domain policy. Naturally, you can't make a POST request using a SCRIPT element, the browser would fetch the result using GET.
As you are requesting a JSONP call, the SCRIPT element is not generated, because it only does this when the Type of AJAX call is set to GET.
http://dev.jquery.com/ticket/4690
We had a problem like this with ASP.Net. Our IIS was returning an Internal Server Error when trying to execute a jQuery $.post to get some html content due to PageHandlerFactory was restricted to respond only GET,HEAD,POST,DEBUG Verbs. So you can change that restriction adding the verb "OPTIONS" to the list or selecting "All Verbs"
You can modify that in your IIS Manager, selecting your website, then selecting Handler Mappings, double click in your PageHandlerFactory for *.apx files as you need (We use Integrated application pool with framework 4.0). Click on Request Restrictions, then go to Verbs Tabn and apply your modification.
Now our $.post request is working as expected :)
Check if your form's action URL includes the www part of the domain, while the original page you have opened is viewed without www.
Typically done for Canonical Urls..
I struggled for hours before stumbling upon this article and found the hint of Cross Domain.
I seems that if o.url = 'index.php' and this file exists is ok and returning a success message in the console. It returns an error if I use url:http://www.google.com
If doing a post request why not using directly the $.post method:
$.post("test.php", { func: "getNameAndTime" },
function(data){
alert(data.name); // John
console.log(data.time); // 2pm
}, "json");
It is so much simpler.
I have posted a clear example of how to solve this if control the server code of the domain you are POSTing to. This answer is touched on in this thread, but this more clearly explains it IMO.
How do I send a cross-domain POST request via JavaScript?
Solution to this is:
use dataType: json
add &callback=? to your url
this worked on calling Facebook API and with Firefox. Firebug is using GET instead of OPTIONS with the above conditions (both of them).
Another possibility to circumvent the problem is to use a proxy script. That method is described for example here
Can you try this without
contentType:application/x-www-form-urlencoded
Try adding the option:
dataType: "json"
function test_success(page,name,id,divname,str)
{
var dropdownIndex = document.getElementById(name).selectedIndex;
var dropdownValue = document.getElementById(name)[dropdownIndex].value;
var params='&'+id+'='+dropdownValue+'&'+str;
//makerequest_sp(url, params, divid1);
$.ajax({
url: page,
type: "post",
data: params,
// callback handler that will be called on success
success: function(response, textStatus, jqXHR){
// log a message to the console
document.getElementById(divname).innerHTML = response;
var retname = 'n_district';
var dropdownIndex = document.getElementById(retname).selectedIndex;
var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
if(dropdownValue >0)
{
//alert(dropdownValue);
document.getElementById('inputname').value = dropdownValue;
}
else
{
document.getElementById('inputname').value = "00";
}
return;
url2=page2;
var params2 = parrams2+'&';
makerequest_sp(url2, params2, divid2);
}
});
}
I had a similar problem with trying to use the Facebook API.
The only contentType which didn't send the Preflighted request seemed to be just text/plain... not the rest of the parameters mentioned at mozilla here
Why is this the only browser which does this?
Why doesn't Facebook know and accept the preflight request?
FYI: The aforementioned Moz doc suggests X-Lori headers should trigger a Preflighted request ... it doesn't.
You need to do some work on server side. I see you are using PHP on server side, but solution for .NET web application is here:
Cannot set content-type to 'application/json' in jQuery.ajax
Do the same in PHP script and it will work. Simply: At first request browser is asking server if is allowed to send such data with such type and second request is the proper/allowed.
Try to add the following:
dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),
I used a proxy url to solve a similar problem when I want to post data to my apache solr hosted in another server. (This may not be the perfect answer but it solves my problem.)
Follow this URL: Using Mode-Rewrite for proxying, I add this line to my httpd.conf:
RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]
Therefore, I can just post data to /solr instead of posting data to http://ip:8983/solr/*. Then it will be posting data in the same origin.
I already have this code handling well my cors situation in php:
header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );
And it was working fine locally and remotely, but not for uploads when remote.
Something happen with apache/php OR my code, I didn't bother to search it, when you request OPTIONS it returns my header with cors rules but with 302 result. Therefore my browser doesn't recognise as an acceptable situation.
What I did, based on #Mark McDonald answer, is just put this code after my header:
if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
header("HTTP/1.1 202 Accepted");
exit;
}
Now, when requesting OPTIONS it will just send the header and 202 result.
Please be advised:
JSONP supports only the GET request method.
*Send request by firefox:*
$.ajax({
type: 'POST',//<<===
contentType: 'application/json',
url: url,
dataType: "json"//<<=============
...
});
Above request send by OPTIONS(while ==>type: 'POST')!!!!
$.ajax({
type: 'POST',//<<===
contentType: 'application/json',
url: url,
dataType: "jsonp"//<<==============
...
});
But above request send by GET(while ==>type: 'POST')!!!!
When you are in "cross-domain communication" , pay attention and be careful.

Resources