I'm having trouble understanding Apollo Client library as it does not work as intended. Instead of sending the GET HTTP method, it sends the OPTIONS HTTP method even though I've put to use GET only when retrieving data from GraphQL server.
const client = new ApolloClient({
link: ApolloLink.from([
new MeteorAccountsLink(),
new HttpLink({
uri: 'https://selo-comments.herokuapp.com/graphql',
useGETForQueries: true
})
]),
cache: new InMemoryCache()
});
Console log from the browser:
OPTIONS https://selo-comments.herokuapp.com/graphql?query=%7B%0A%20%20comments(id%3A%20%22TFpQmhrDxQqHk2ryy%22)%20%7B%0A%20%20%20%20articleID%0A%20%20%20%20content%0A%20%20%20%20userId%0A%20%20%20%20createdAt%0A%20%20%20%20commentID%0A%20%20%20%20votes%0A%20%20%20%20blockedUsers%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D%0A&variables=%7B%7D 405 (Method Not Allowed)
Which obviously means that the HTTP method is incorrect even if it has the query parameter in the url. If you query that url using Postman or simply navigating to the url using browser's address bar, you will get GraphQL data. I have to use https://cors-anywhere.herokuapp.com/ in order to execute the query successfully.
What am I doing wrong?
The options request is probably a preflight request for CORS.
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood.
It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.
You probably need to configure your server to allow cross origin calls.
Maybe you can find some inspiration here to get u started. Allow CORS REST request to a Express/Node.js application on Heroku
Related
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.
I am calling an API from different domain, it doesn't have any authorization check for now but in future we are planning to do so. When I hit this API without any headers I get the response but when I set the headers the browser throws CORS error. the headers are appID, version and empID. "Access-Control-Allow-Origin" header is set on server side. I am using another API from another domain which works perfectly fine with and without headers. I think the issue is with content-type or data-type but I am clueless.I get the response in JSON format.
When you pass non-standard headers to an AJAX request, the client will send a pre-flight OPTIONS HTTP Request before attempting the real request.
Your server needs to be able to handle that request, and also return the required ACAO headers in response to it.
I can't execute the 'GET' request with the getTasks() function.
$(document).ready(function(){
getTasks();
});
const apiKey = 'xxxxxxx';
function getTasks(){
$.ajax({
type: 'GET',
url: 'https://api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey='+apiKey,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
success: function(data){
console.log(data);
},
error: function(){
console.log('FAIL')
}
})
}
The error that I get is:
api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey=xxxxxxx
Failed to load resource: the server responded with a status of 400
(Bad Request)
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access. The response
had HTTP status code 400.
I understand that Google-Chrome on Windows is CORS enabled, and will not (by default) allow communication with a different domain. I'm not sure what a preflight request is. Regardless, I tried to implement what I saw from Using CORS - HTML5 Rocks (from the CORS from jQuery section), but to no avail.
At a guess, the remote API simply does not respond to pre-flight requests for GET calls (because it shouldn't have to).
Your code is triggering a pre-flight request because it is non-simple. This is due to your adding a Content-type: application/json header. A request Content-type header is used to indicate the request payload format. As it is a GET, there is no payload.
Try this instead...
$.getJSON('https://api.mlab.com/api/1/databases/taskmanager/collections/tasks', {
apiKey: apiKey
}).done(function(data) {
console.log(data)
}).fail(function() {
console.log('FAIL')
})
CORS is there to protect you. If you want some more info on it, wikipedia has a good entry on it.
It appears the issue here is that you're trying to access your mongodb hosted by mlab directly from your web app. As you can see in your code, you're providing credentials/api keys to make that request.
My guess is that mlab's intent of not allowing CORS is to prevent you from doing this. You should never put your private API keys in html to be hosted on a web page, as it's easily accessible by reading source code. Then someone would have direct access to your mongodb.
Instead, you should create a server-side application (node, or... ** Whatever **) that exposes an api you control on the same domain (or a domain you give permission to via CORS).
As far as the "preflight" request, if you look in your chrome debugging tools, you should see an additional request go out with the "OPTIONS" method. This is the request that chrome (and most other http clients) send out first to a server hosted on a different domain. it's looking for the Access-Control-Allow-Origin header to find out whether it's allowed to make the request. Pretty interesting stuff if you ever have some time to dig into it.
I have a proxy set up for a third party service that at the moment looks like this:
app.use('/service', (req, res) => {
let url = `http://www.service.com/endpoint/${config.POSTCODER_KEY}${req.url}`
req.headers['Referer'] = 'my.domain.com'
console.log(req.headers['Referer'])
req.pipe(request(url)).pipe(res)
})
As you can see I am trying to add Referer header to the request and it seems to be working as console.log prints out 'my.domain.com' however request fails and the error I get back from the service is 403 unauthorised referring to Referer header. When I inspect network in inspector tools my referer is displayed as localhost.
I am testing this in Postman api client (https://www.getpostman.com) by setting Referer to my white listed domain and it works. I'm not sure why it uses localhost with express.
Piping streams together only transfers the data in those streams. Headers are not a part of that. When you req.pipe(request(url)) you're only writing the request body to the proxied request. If you want to set the headers used for the proxied request, you have to pass them to request, like:
req.pipe(request({ url: url, headers: req.headers })).pipe(res);
However, as noted in my answer to your previous question, you will also need to properly set the headers on res when the proxied response arrives.
I am trying to get data from API that has oAuth authentication.
What I am doing is sending the auth request from server1 (where my Angular app is) to server2 and I get the access token. Then I put the token in the JS variable and I am trying to access an API endpoint.
My js code looks like this:
let headers = new Headers({ 'Authorization': "Bearer " + oAuthAccessToken });
let options = new RequestOptions({ headers: headers });
let url = "http://example.com/api/getSomething"
this.http.post(url, body, options)
.subscribe( res => {
console.log(res.json())
});
The problem is that I am always getting "401 Unathorized". When I inspect the request in the Network tab of Chrome Dev Tools I see two strange things - first the request method is OPTIONS not POST and the header Authorization is missing.
Any idea what I might be doing wrong ? Why is the header not set ?
Edit:
The problem was that Angular sends OPTIONS request before the POST and my app firewall was expecting Authorization header to be always present. This header is not present in the OPTIONS request so I was getting Unauthorized. I changed my server app to send proper headers on OPTIONS request and now everything is fine.
Thanks for the help.
I think the browser try to discover which http methods are allowed, so the first request is an request with the OPTIONS method. Usually the backend service answers with Access-Control-Allow-Methods inside the header. Afterwards the browser sends the real request.
I think that you need to allow CORS, then it should work as expected
As you are dealing with cross-domain requests, Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. so the option request is just to check is the server support CORS.
From : https://stackoverflow.com/a/21783145/3279156
Content-Type should be like below:
let header= new Headers({'Content-type':'application/x-www-form-urlencode'});
header.append('Authorization',"Bearer " + token);
let opt= new RequestOptions({headers:header});