React JS + React Router works with $.get but not $.ajax PUT - javascript

I've got a react.js setup and I've got some simple routes using react-router. I have a dispatcher:
import dispatcher from "../dispatcher";
export function reloadList(){
dispatcher.dispatch({type: "FETCH_LIST"});
let serverRequest = $.get("../../data/list.json",
function(data){
console.log(data); //Logs correct data from list.json
}
);
}
export function saveList(list){
dispatcher.dispatch({type: "SAVE_LIST"});
let serverRequest =
$.ajax(
url: "../../data/list.json",
dataType: 'json',
method: 'PUT',
data: list,
success: function(data) {
console.log("success!"); //does not output
},
error: function(xhr, status, err) {
console.log(status, err.toString()); //outputs a 404 error
}
});
)
}
The reloadList() function runs fine and outputs the JSON data. The saveList() function throws a 404 error. I'm assuming this is because of the way the app handles routes, but I'm confused as to why it works with get and not put (or post for that matter).
I've been struggling with saving data to a file in react, this seemed like the right way at first but now I'm not so sure. Is there something I'm doing wrong here?

I'm going to assume based on your phrasing, that you're using some sort of simple HTTP server (or no server at all) for developing locally, and list.json is a file on your machine. If this is the case, your issue is that your HTTP server doesn't have the routing to know how to handle PUTs (or POSTs, or probably any method other than GET for that matter). If you want to write files to your system you will need to add an actual backend that can handle a PUT and more intelligently interact with the filesystem.
Unfortunately there's no simple way to write files to disk in the browser alone (for security reasons), but if you want to avoid adding to your backend, you could look into creating files for download using a Blob

You can't change the contents of a file on the server from the browser using only an ajax request. Think about how terribly insecure the internet would be if I could change files on your server just by sending an ajax request.
You're going to need some server side code that can handle such a request. For instance, you can setups a simple Node / Express server that has a route which can handle reading and writing to disk, using the built in fs module.
Maybe later on you'll want to use a database instead.
http://expressjs.com/en/starter/installing.html

Make sure your server routes the PUT request, not just GET.
For example express.js routes like this:
`app.get("/getRequestHere",function(){});
app.put("/putRequestHere",function(){});`
Some browsers (Chrome) restrict the ability to make PUT/DELETE requests. It sents out an OPTION request that returns 404. What you want to do is put this in your .htaccess file:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token, accessToken"
# Added a rewrite to respond with a 200 SUCCESS on every OPTIONS request.
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
first parts accepts access for PUT methods,
second part forwards (rewrite) OPTION method requests into an 200 response. This code I copied somewhere else on stackoverflow and it totally works.
(stop down voting me guys. This answer totally works)

Related

CORB from Vanilla JS getJSONP to Google Apps Script [duplicate]

I have called third party API using Jquery AJAX. I am getting following error in console:
Cross-Origin Read Blocking (CORB) blocked cross-origin response MY URL with MIME type application/json. See https://www.chromestatus.com/feature/5629709824032768 for more details.
I have used following code for Ajax call :
$.ajax({
type: 'GET',
url: My Url,
contentType: 'application/json',
dataType:'jsonp',
responseType:'application/json',
xhrFields: {
withCredentials: false
},
headers: {
'Access-Control-Allow-Credentials' : true,
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'GET',
'Access-Control-Allow-Headers':'application/json',
},
success: function(data) {
console.log(data);
},
error: function(error) {
console.log("FAIL....=================");
}
});
When I checked in Fiddler, I have got the data in response but not in Ajax success method.
Please help me out.
dataType:'jsonp',
You are making a JSONP request, but the server is responding with JSON.
The browser is refusing to try to treat the JSON as JSONP because it would be a security risk. (If the browser did try to treat the JSON as JSONP then it would, at best, fail).
See this question for more details on what JSONP is. Note that is a nasty hack to work around the Same Origin Policy that was used before CORS was available. CORS is a much cleaner, safer, and more powerful solution to the problem.
It looks like you are trying to make a cross-origin request and are throwing everything you can think of at it in one massive pile of conflicting instructions.
You need to understand how the Same Origin policy works.
See this question for an in-depth guide.
Now a few notes about your code:
contentType: 'application/json',
This is ignored when you use JSONP
You are making a GET request. There is no request body to describe the type of.
This will make a cross-origin request non-simple, meaning that as well as basic CORS permissions, you also need to deal with a pre-flight.
Remove that.
dataType:'jsonp',
The server is not responding with JSONP.
Remove this. (You could make the server respond with JSONP instead, but CORS is better).
responseType:'application/json',
This is not an option supported by jQuery.ajax. Remove this.
xhrFields: {
withCredentials: false },
This is the default. Unless you are setting it to true with ajaxSetup, remove this.
headers: {
'Access-Control-Allow-Credentials' : true,
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Methods':'GET',
'Access-Control-Allow-Headers':'application/json',
},
These are response headers. They belong on the response, not the request.
This will make a cross-origin request non-simple, meaning that as well as basic CORS permissions, you also need to deal with a pre-flight.
In most cases, the blocked response should not affect the web page's behavior and the CORB error message can be safely ignored. For example, the warning may occur in cases when the body of the blocked response was empty already, or when the response was going to be delivered to a context that can't handle it (e.g., a HTML document such as a 404 error page being delivered to an tag).
https://www.chromium.org/Home/chromium-security/corb-for-developers
I had to clean my browser's cache, I was reading in this link, that, if the request get a empty response, we get this warning error. I was getting some CORS on my request, and so the response of this request got empty, All I had to do was clear the browser's cache, and the CORS got away. I was receiving CORS because the chrome had saved the PORT number on the cache, The server would just accept localhost:3010 and I was doing localhost:3002, because of the cache.
Return response with header 'Access-Control-Allow-Origin:*'
Check below code for the Php server response.
<?php header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
echo json_encode($phparray);
You have to add CORS on the server side:
If you are using nodeJS then:
First you need to install cors by using below command :
npm install cors --save
Now add the following code to your app starting file like ( app.js or server.js)
var express = require('express');
var app = express();
var cors = require('cors');
var bodyParser = require('body-parser');
//enables cors
app.use(cors({
'allowedHeaders': ['sessionId', 'Content-Type'],
'exposedHeaders': ['sessionId'],
'origin': '*',
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
'preflightContinue': false
}));
require('./router/index')(app);
It's not clear from the question, but assuming this is something happening on a development or test client, and given that you are already using Fiddler you can have Fiddler respond with an allow response:
Select the problem request in Fiddler
Open the AutoResponder tab
Click Add Rule and edit the rule to:
Method:OPTIONS server url here, e.g. Method:OPTIONS http://localhost
*CORSPreflightAllow
Check Unmatched requests passthrough
Check Enable Rules
A couple notes:
Obviously this is only a solution for development/testing where it isn't possible/practical to modify the API service
Check that any agreements you have with the third-party API provider allow you to do this
As others have noted, this is part of how CORS works, and eventually the header will need to be set on the API server. If you control that server, you can set the headers yourself. In this case since it is a third party service, I can only assume they have some mechanism via which you are able to provide them with the URL of the originating site and they will update their service accordingly to respond with the correct headers.
If you are working on localhost, try this, this one the only extension and method that worked for me (Angular, only javascript, no php)
https://chrome.google.com/webstore/detail/moesif-orign-cors-changer/digfbfaphojjndkpccljibejjbppifbc/related?hl=en
In a Chrome extension, you can use
chrome.webRequest.onHeadersReceived.addListener
to rewrite the server response headers. You can either replace an existing header or add an additional header. This is the header you want:
Access-Control-Allow-Origin: *
https://developers.chrome.com/extensions/webRequest#event-onHeadersReceived
I was stuck on CORB issues, and this fixed it for me.
have you tried changing the dataType in your ajax request from jsonp to json? that fixed it in my case.
There is an edge case worth mentioning in this context: Chrome (some versions, at least) checks CORS preflights using the algorithm set up for CORB. IMO, this is a bit silly because preflights don't seem to affect the CORB threat model, and CORB seems designed to be orthogonal to CORS. Also, the body of a CORS preflight is not accessible, so there is no negative consequence just an irritating warning.
Anyway, check that your CORS preflight responses (OPTIONS method responses) don't have a body (204). An empty 200 with content type application/octet-stream and length zero worked well here too.
You can confirm if this is the case you are hitting by counting CORB warnings vs. OPTIONS responses with a message body.
It seems that this warning occured when sending an empty response with a 200.
This configuration in my .htaccess display the warning on Chrome:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST,GET,HEAD,OPTIONS,PUT,DELETE"
Header always set Access-Control-Allow-Headers "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]
But changing the last line to
RewriteRule .* / [R=204,L]
resolve the issue!
I have a similar problem. My case is because the contentType of server response is application/json, rather than text/javascript.
So, I solve it from my server (spring mvc):
// http://127.0.0.1:8080/jsonp/test?callback=json_123456
#GetMapping(value = "/test")
public void testJsonp(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
#RequestParam(value = "callback", required = false) String callback) throws IOException {
JSONObject json = new JSONObject();
json.put("a", 1);
json.put("b", "test");
String dataString = json.toJSONString();
if (StringUtils.isBlank(callback)) {
httpServletResponse.setContentType("application/json; charset=UTF-8");
httpServletResponse.getWriter().print(dataString);
} else {
// important: contentType must be text/javascript
httpServletResponse.setContentType("text/javascript; charset=UTF-8");
dataString = callback + "(" + dataString + ")";
httpServletResponse.getWriter().print(dataString);
}
}
Response headers are generally set on the server. Set 'Access-Control-Allow-Headers' to 'Content-Type' on server side
I had the same problem with my Chrome extension. When I tried to add to my manifest "content_scripts" option this part:
//{
// "matches": [ "<all_urls>" ],
// "css": [ "myStyles.css" ],
// "js": [ "test.js" ]
//}
And I remove the other part from my manifest "permissons":
"https://*/"
Only when I delete it CORB on one of my XHR reqest disappare.
Worst of all that there are few XHR reqest in my code and only one of them start to get CORB error (why CORB do not appare on other XHR I do not know; why manifest changes coused this error I do not know). That's why I inspected the entire code again and again by few hours and lost a lot of time.
I encountered this problem because the format of the jsonp response from the server is wrong. The incorrect response is as follows.
callback(["apple", "peach"])
The problem is, the object inside callback should be a correct json object, instead of a json array. So I modified some server code and changed its format:
callback({"fruit": ["apple", "peach"]})
The browser happily accepted the response after the modification.
Try to install "Moesif CORS" extension if you are facing issue in google chrome. As it is cross origin request, so chrome is not accepting a response even when the response status code is 200

Unable to call web-service from angularjs app

Unable to call post webservice from my application. following is the code.
var postLogin = "http://0.0.0.0:000/ddd/v1/login";
var loginvalue = {"email":"some#mail.com","password":"cbsjc6dw3bgjyfdgdKHGGDF="};
var config = {
headers: {
'Content-Type': 'application/json'
}
}
$http.post(postLogin ,loginvalue,config ).success( function(response) {
alert("response "+ response)
$scope.defer.resolve(response);
}).error(function(error){
alert("dddddd" + JSON.stringify(error));
})
If i write this code then it is returning as 400 error but if i use the postman application of google then i am getting the response without any error. So i am in confusion that whatever the code i have written is right or wrong. Hence i need to solve this issue.
Please go through the above image.
This usually happens when Client and Server are on different domains. The POST requests done by the client are first verified with a OPTIONS pre-flight check, to see if a POST would be possible. Sometimes, servers are configured to not allow OPTIONS request method. This will be the outcome of a pre-flight OPTIONS check, in such a case.
There is more information here - Why is an OPTIONS request sent and can I disable it?
Other resources for understanding the concept and helping us to configure the Response headers from the Server-side application are here:
https://medium.com/#praveen.beatle/avoiding-pre-flight-options-calls-on-cors-requests-baba9692c21a
https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
At the end of the day, if the Server is NOT configured to handle Cross-site requests, nothing can be done from the client-side.
Also, there are cases where the server does allow cross-site request, processes and send the response back to client, without the Access-Control-Allow-Origin header or with the Access-Control-Allow-Origin header, but not the same as the request origin or a wildcard "*". In such cases, browser stops processing the response, even when the call turns out to be in HTTP 200 OK Status.
Below is one such example, that I recently encountered while integrating with an external application.

REST request from app works, but not from javascript

I'm building an app that has to get and set data at a remote web service through requests. When I use the jQuery GET request it works fine, I can request data from the service without any problems, but when I use PUT I get some erros:
OPTIONS http://myurl.com 501 (Unsupported method
('OPTIONS'))
OPTIONS http://myurl.com Origin null is not allowed by Access-Control-Allow-Origin.
I've tried almost anything to get this to work, but it won't work. I've download a chrome app called REST Console, which can make custom REST requests. The strange thing is that I can interact with my server over that app but not through my javascript!
This is the javascript:
$.ajax({
url: 'http://myurl.com',
type: 'PUT',
data: '<time>16:00</time>',
success: function(data) { alert(data); }
});
Could anybody tell me what is going on here?
First ensure you're serving the page that runs the script from a web server, not directly from the file system.
I'm guessing your service at http://myurl.com is at a different host name to the host name your page is being served from? For it to work in this case you need to implement HTTP headers to support Cross Origin Resource Sharing.
Your service at http://myurl.com needs to handle an HTTP OPTIONS request, the response to which should be a set of HTTP headers (with no content) as follows:
Access-Control-Allow-Origin: http://url-of-page-with-javascript/
Optionally you can also specify Access-Control-Allow-Credentials, Access-Control-Allow-Headers and Access-Control-Allow-Methods. See the full specification here.
You'll also need to add the same headers with the same values when your server responds to the PUT request - obviously the content will also be included with this response.

jQuery, CORS, JSON (without padding) and authentication issues

I have two domains. I'm trying to access a JSON object from one domain through a page on another. I've read everything I could find regarding this issue, and still can't figure this out.
The domain serving the JSON has the following settings:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"
Header set Access-Control-Allow-Headers "origin, authorization, accept"
From my other domain, I'm calling the following:
$.ajax({
type:'get',
beforeSend: function(xhr) {
var auth = // authentication;
xhr.setRequestHeader("Authorization", "Basic " + auth);
}
url:myUrl,
dataType:'json',
error: function(xhr, textStatus, errorThrown) { console.log(textStatus, errorThrown); }
})
I know that 'auth' is initialized properly (logged and checked). However, this does not work. In Firefox's Console, I get
Request URL: ...
Request Method:
OPTIONS
Status Code:
HTTP/1.1 401 Authorization Required
If I get rid of the beforeSend:... part, I see the following
Request Method:
GET
Status Code:
HTTP/1.1 401 Authorization Required
However, the domain serving JSON also can serve JSONP. I don't want to use this, mainly because the application will be running constantly on a dedicated browser, and I'm worried about this issue. More importantly, I would really like to know what is actually wrong with what I am doing. I know that for practical purposes there are various ways to overcome the JSONP memory leak (such as not using jQuery).
At any rate, when I did use JSONP, my code looked like this:
$.ajax({
url:newUrl,
dataType:'jsonp',
jsonp:'jsonp'
}).done(function(d){console.log(d)})
This gets the following
Request Method:
GET
Status Code:
HTTP/1.1 200 OK
after it prompts me with an alert box for a username and password.
Is there a fundamental difference in the way jQuery handles JSONP requests as opposed to JSON requests? And if so, how can I fix this?
Thanks.
Edit: Here's what I did find.
Basically, because I need authentication, the GET request is sending an Authorization header. However, this is not a "simple" header, and so the browser is sending a pre-flight request (the OPTIONS). This preflight request doesn't have any authentication, though, and so the server was rejecting it. The "solution" was to set the server to let OPTIONS request not require authentication, and report an HTTP status of 200 to it.
Reference: http://www.kinvey.com/blog/item/61-kinvey-adds-cross-origin-resource-sharing-cors
mail-archive[.com]/c-user#axis.apache.org/msg00790.html (not allowed to post more links)
Unfortunately, the "solution" is only working on Firefox and not Chrome. Chrome simply shows the request in red, but doesn't give me any more info on why it failed.
Edit 2: Fixed on Chrome: The server I was trying to get data from had a security certificate which was not trusted. The preflight request on Chrome failed because of this. Solution
superuser[.com]/questions/27268/how-do-i-disable-the-warning-chrome-gives-if-a-security-certificate-is-not-trust (not allowed to post more links)
Welp, now that I have enough rep a while later, I might as well answer this question and accept it.
When you attempt to send a GET json request to a server with headers, the browser first sends an OPTION request to make sure that you can access it. Unfortunately, this OPTION request cannot carry with it any authentication. This means that if you want to send a GET with auth, the server must allow an OPTION without auth. Once I did this, things started working.
Some examples available here may illustrate further how access control can be combined with CORS. Specifically the credentialed GET example. Access control requires that the request set the withCredentials flag to true on the XMLHttpRequest, and for the server handling the OPTIONS method to do two things:
Set Access-Control-Allow-Credentials: true
Not use a wildcard * in the Access-Control-Allow-Origin header. This has to be set to the origin exactly according to the MDN docs on HTTP access control (CORS).
Essentially, the thing processing the OPTIONS request needs to send back appropriate response headers so you can make that credentialed request.
In your question you stated that the service you are interacting with is returning Access-Control-Allow-Origin: *, which is not compatible with a credentialed cross-domain request. This needs to return the origin specifically.
The aforementioned MDN Http Access Control (CORS) documentation also links to the Server-Side Access Control documentation outlining how a server would potentially respond to various cross domain requests - including handling a cross domain credentialed POST request that requires you to send back the correct headers in response to the OPTIONS method. You can find that example here.
Why don't you try typing the URL you are fetching the JSON from into your browser and seeing what happens. It sounds like you literally just need to authenticate into this other website to access it.
If your site needs to work in other browsers like IE, you WILL need JSONP, by the way. The security won't allow the cross site request to work. The headers won't change that. I believe you will also need to add a security policy in your headers.

Headers and POST data disappear when using AJAX and mod_rewrite

I have an instance of Apache 2.2.21 on Windows 7 running at http://localhost that uses mod_rewrite to redirect requests beginning with /backend to http://localhost:8080. The server at http://localhost:8080 is in turn a CherryPy 3.2.0 server that is designed to accept POSTed JSON messages and respond with JSON messages.
My problem is that when I try to POST to http://localhost/backend using AJAX the CherryPy server ends up seeing a message with no request headers and no attached JSON data. It thus returns a 400 BAD REQUEST error. But I also have applications built in Excel and .NET that send similar messages to http://localhost/backend with no problems. Also, I've tried setting up a page at http://localhost/route (that uses Django) which passes the AJAX POST along to http://localhost:8080; this works perfectly.
My question then is why the AJAX POST to `http://localhost/backend' is not working. Perhaps this is related to AJAX security?
Example JavaScript using jQuery would be something like
$.post(
"/backend",
JSON.stringify({"type": "getdata", "id": "1"}),
function(data) {
alert("Done");
}
);
And the relevant section of the Apache configuration file is
RewriteEngine on
RewriteRule ^/backend http://localhost:8080 [proxy]
CherryPy is running with no special startup options:
cherrypy.config.update({'server.socket_port': 8080})
cherrypy.quickstart(MyServer())
Have you considered using mod_proxy and ProxyPass? It's sort of more suited to what it looks like you're trying to do. You'd simply need a directive like this:
ProxyPass /backend http://localhost:8080/
You can add connection timeouts, retry attempts, rewrite cookie domains, even rewrite response header hostnames with ProxyPassReverse.
This actually turned out to just be a case of failing to manually include a header in the AJAX request that specified the Content-Type as application/json. For example,
$.ajax({
...,
headers: {"Content-Type": "applicaton/json"},
...
});

Categories

Resources