Can’t access cross-origin response header from frontend JavaScript - javascript

I am building a simple web app using ReactJS and create-react-app.
I have a backend API set up on Heroku where I can make POST requests. Everything works fine, except:
When I make a POST request using fetch API, the response is 100% correct but it only gives me 2 standard headers. I want to get my custom header. I have added expose header in my response and here's the plot twist: When I view the headers from Chrome Inspection Tool or Postman (API tool), it shows all the headers, including my custom one. Here is the fetch code I'm using -
fetch(theUrl, {
method: 'POST',
body: JSON.stringify({
"placeholder": "placeholder"
})
})
.then(function(res) {
console.log(res.headers.get('CUSTOM_HEADER_NAME'));
})
If it makes any difference, this fetch method is called from a function outside the main body of the ReactJS component.
The name of the custom header is Image-Identification-Path, and the header in my response header is Access-Control-Expose-Headers for Image-Identification-Path.
Summary: How do I get my custom header using fetch?

You must configure the server to which the request is sent, such that its response has an Access-Control-Expose-Headers header that has the name of your custom response header.
Otherwise, if your browser doesn’t see the name of your custom header in that Access-Control-Expose-Headers header, it won’t let you access the value of your custom header.
In such a case it’s expected that you’d still be able to see the custom header if you look at the response in Postman or even in your browser devtools.
But just because the browser gets the custom header in the response doesn’t mean the browser will expose it to your frontend JavaScript code.
For cross-origin requests, browsers will only expose that custom response header to your frontend code if that header name is in the Access-Control-Expose-Headers value.

I know this question is old but I ran into this problem yesterday, and the given answer didn't work for me.
The solution I found was given in this article. Basically:
You can’t directly access the headers on the response to a fetch call - you have to iterate through after using the entries() method on the headers.
So, in your particular case you should be able to achieve the goal by using this code:
fetch(theUrl, {
method: 'POST',
body: JSON.stringify({
"placeholder": "placeholder"
})
})
.then(response => {
for (var pair of response.headers.entries()) { // accessing the entries
if (pair[0] === 'CUSTOM_HEADER_NAME') { // key you're looking for, in your case Image-Identification-Path
let imagePath = pair[1]; //// saving that value
}
}
.... })

Related

how i can implement a get method to a api what needs "Temporary Headers"?

I made an api using Net.Core 'https://localhost:44351/api/usuarios' with Authorize attribute for use a validation token for access to this api. This token is generated by another api and works well.
When i use Postman for acess to api 'https://localhost:44351/api/usuarios' i need put in the Authorization tab the token previously generated (see https://i.ibb.co/Lg7rD4N/2.png) and this way i get access for the api (see https://i.ibb.co/0BqnPhR/3.png)
But the huge problem is when i try from a JAVASCRIPT CLIENT use method GET using FETCH. I know need to do a object like this for make correct request
let params= { method: 'GET',
headers: {"X-Auth-Token": "5f5fe128570248a9bd198add1a5b25e4"}
};
So my question is how i can implement the attributte 'Temporary-Headers' in the object 'params' like Postman does ( https://i.ibb.co/0BqnPhR/3.png)?
"Temporary Headers" are just like any other Header, those are just values auto-generated by the web client. I believe your problem relies on how you send your Header if you are using the fetch API modify your parameters like the following.
{
method: 'GET',
headers: {
"Authorization": "Bearer <replace with your token>"
}
}
This is how postman is sending the request as seen in the screenshots you provide.

How do I do a POST request using the Google Chrome Puppeteer library?

Good Day All,
I'm trying to do a POST request using the puppeteer headless chrome library. I can't seem to get the below code to work.
// Get csrf token
let token = await page.evaluate(() => document.querySelector('[name="CSRFToken"]').value);
let postResponse = await page.evaluate(async(token, cookies) => {
let response = fetch("/loyalty/points", {
method : 'POST',
cookie : cookies,
postData : 'CSRFToken=' + token,
}).then(response => response.text()).catch(error => console.log(error));
return response;
});
console.log('Final response');
console.log(postResponse);
I keep on getting an error that the CSRF token has not been set or that the cookie is invalid.
My question is, am I using the correct method in puppeteer to do a POST? If so, is there any way for me to do some debugging that I can see the actual POST request that was sent?
I appreciate any advice or help. Thanks
You are not creating a request body: hence the error. The postData attribute you set on the request object is not any known attribute, so it won't be set on the request either, meaning that the server will never see your CSRF token. You should look into the MDN docs on fetch().
I believe you should be all good by simply replacing postData with body, but it's hard to know without access to your endpoint. For all we know it might require special headers.
Given that you only post normal form data (which is implied by your key=value code), I would also start using the FormData objects provided by your browser to avoid manual coding of implementation details.
const formData = new FormData();
formData.append("CSRFToken", token);
const response = fetch("/loyalty/points", {
method : 'POST',
cookie : cookies,
body : formData,
headers : {
'cookie' : cookies,
/* other headers you need, possibly content-type (see below) */
},
}).then(response => response.text()).catch(error => console.log(error));
return response;
});
Caveat: using the FormData API will always set the content-type of the data to multipart/form-data. If your server for some reason doesn't support that encoding, and you need to use application/x-www-form-urlencoded (see here for difference),
you can't blindly change the Content-Type: you also need to url encode the content.
For debugging I would simply use a normal Chrome instance to see this. You should be able to run the code there and see the network requests in DevTools (where it would be immediately noticeable that you POST an empty request).

Axios API Twitter request not returning back user tweets

I am trying to call Twitters API and get a my tweets back so I can post them on a website I am creating.
When I run the following code I get an error.
XMLHttpRequest cannot load https://api.twitter.com/1.1/search/tweets.json?q=%SamSchaeferSays. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3333' is therefore not allowed access. The response had HTTP status code 400." And "bundle.js:30041 Uncaught (in promise) Error: Network Error.
I am new to API calls not using PHP - not sure what I am doing wrong here.
const tweet = 'https://api.twitter.com/1.1/search/tweets.json?q=%SamSchaeferSays';
function getUserTweet() {
return axios.get(`${tweet}`).then(function(response){
console.log(response.data)
console.log(response.status)
});
}
sample OAuth string
const DST = `OAuth oauth_consumer_key="${consumerKey}",
oauth_nonce="${}",
oauth_signature="${}",
oauth_signature_method="${}",
oauth_timestamp="${}",
oauth_token="${}",
oauth_version="1.0"
`;
A 400 Bad Request error means that the server doesn't understand your request. In your case there's a typo that prevents the request from going through (extra %). Try this:
const tweet = 'https://api.twitter.com/1.1/search/tweets.json?q=SamSchaeferSays';
function getUserTweet() {
return axios.get(`${tweet}`, { headers: { 'Authorization': 'YOUR_OAUTH_HEADER' } }).then(function(response){
console.log(response.data)
console.log(response.status)
});
}
This will fix the 400 Bad Request error, but you won't get any data back yet. The Twitter API requires you to authorize your request. Find out more in their documentation.
To allow applications to provide this information, Twitter’s API relies on the OAuth 1.0a protocol. At a very simplified level, Twitter’s implementation requires that requests needing authorization contain an additional HTTP Authorization header with enough information to answer the questions listed above. A version of the HTTP request shown above, modified to include this header, looks like this (normally the Authorization header would need to be on one line, but has been wrapped for legibility here):

xhr caching values from getResponseHeader?

I'm running up against a very frustrating bug. I'm not exactly sure what is happening, but I think xhr is doing some kind of cache on the response headers.
My app is using devise_token_auth for the backend authentication service. We're using it with rotating access-tokens, and so I have written a function that runs after every request.
function storeAndGetResponseHeaders(xhr) {
const headersObj = {};
headerKeys.filter((key) => xhr.getResponseHeader(key))
.forEach((key) => {
headersObj[key] = xhr.getResponseHeader(key);
window.sessionStorage.setItem(key, xhr.getResponseHeader(key));
});
return headersObj;
}
where headerKeys is ['access-token', 'client', 'expiry', 'uid', 'token-type']. So any response that has these headers it should save them into sessionStorage and then return them in an object which gets stored within my AJAX service that I wrote and added to every request. We're using rxjs, and this service is just a thin wrapper around it. This is what RxAjax.ajax looks like.
ajax(urlOrRequest) {
const request = typeof urlOrRequest === 'string' ? { url: urlOrRequest } : urlOrRequest;
request.headers = Object.assign({}, this.headers, urlOrRequest.headers);
request.url = `${this.baseUrl}${request.url}`;
return Observable.ajax(request).map(this.afterRequest, this);
}
where this.headers is the stored headers from last request (or the loaded headers from sessionStorage). this.afterRequest is what sets the headers from the response xhr.
My problem is that I'm getting bad values into my headers object (specifically old access tokens). What I've noticed is that when I add a logging statement of headersObj after assignment, sometimes it will have old response headers from a past request. However when I look at the request itself in the dev console Network tab, it doesn't show any of the auth headers in the response headers ('access-token', 'client', etc...). This gets fixed for a little while if I do a hard refresh on the browser, but comes back seemingly inexplicably.
Note we're using rxjs to make our requests, which might be relevant (but I don't think it is the cause of this problem, as I'm trying to read the headers from the original xmlhttprequest object). Thanks!
As Barmar suggested in the comments, it was a caching issue. There may be a bug in the chrome console, where it wasn't showing the cached headers that were on the cached request. Hence even though it looked like there were no auth headers there really were.
It looks like if you're using jQuery you can add the option cache: false to the request in order to prevent caching. Because I'm not, the first thing I did was try adding ?cache=${new Date().toJSON} to each request, which successfully busted the cache and fixed my problem (that is what cache: false in jQuery does).
Our backend is in rails, and so I ended up adding
before_action :set_cache_headers
...
private
def set_cache_headers
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
end
to my application controller. Now no requests are cached by the browser. Not sure if this will be our long term solution or not

How to get data from window.fetch() response?

I'm trying to use window.fetch() to get json from the server, but can't get the data from the response.
I have this code:
let url =
'https://api.flightstats.com/flex/schedules/rest/v1/json/from/SYD/to/MEL/departing/2016/3/28?appId=f4b1b6c9&appKey=59bd8f0274f2ae88aebd2c1db7794f7f';
let request = new Request (url, {
method: 'GET',
mode: 'no-cors'
});
fetch(request)
.then(function(response){
console.log(response)
});
It seems that this request is successfull, I see status 200
and response body with json in network tab - status and response. But in console.log I dont see json object - console log image
I cant understand why I dont see json in console.log
The host site you are requesting from does not appear to support CORS. As such, you can't use fetch() to make a cross origin request and get the data back. If, you change your fetch() request to mode: 'cors', the debug console will show that the host site does not offer CORS headers to allow the browser to show you the result of the request.
When you are using mode: 'no-cors', the browser is hiding the result from you (because you don't have permission to see it) and you can see the response is tagged as opaque.
In a little poking around on the api.flightstats.com site, I did see that it supports JSONP which will allow you to work around the lack of CORS support issue and successfully complete a cross origin request.
For simplicity of showing that it can work, I used jQuery to just prove that a JSONP request can be made. Here's that code in a working snippet. Note I changed the URL from /json/ to /jsonp/ and specific dataType: "jsonp" in the jQuery request. This causes jQuery to add the callback=xxxxx query parameter and to fetch the response via that corresponding script (the JSONP method).
var url =
'https://api.flightstats.com/flex/schedules/rest/v1/jsonp/from/SYD/to/MEL/departing/2016/3/28?appId=f4b1b6c9&appKey=59bd8f0274f2ae88aebd2c1db7794f7f';
$.ajax(url, {dataType: "jsonp"}).then(function(response) {
log(response);
}, function(err) {
log("$.ajax() error:")
log(err);
})
<script src="http://files.the-friend-family.com/log.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
If you take a look at the documentation of the Fetch API; you'll notice that the API offers various methods to extract the data:
arrayBuffer()
blob()
json()
text()
formData()
Assuming the response is valid JSON (which I've noticed it doesn't seem to appear), you can use the response.json() function to retrieve the response data. This also uses a Promise mechanism, as for everything with the Fetch API.
response.json().then(function(data) {
console.log(data);
});

Categories

Resources