React remote request with authorization - javascript

I want to do a remote request using React JS. I try to do it as follows:
let username = 'some-username';
let password = 'some-password';
let url = 'some-url';
fetch(url', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+btoa(username + ":" + password),
},
}).then(response => {
debugger;
return response.json();
}).then(json => {
debugger;
});
I get an error:
If I do the same request with the same credentials with postman it works:
Any idea?
UPDATE
let user = 'some-user';
let password = 'some-password';
let url = 'some-url';
let req = new XMLHttpRequest();
let body = '';
if ('withCredentials' in req) {
req.open('GET', url, true);
req.setRequestHeader('Content-Type', 'application/json');
req.setRequestHeader('Authorization', 'Basic '+ btoa(user + ":" + password));
req.setRequestHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
req.onreadystatechange = () => {
debugger;
if (req.readyState === 4) {
///////////////// it comes here but req.status is 0
if (req.status >= 200 && req.status < 400) {
debugger;
// JSON.parse(req.responseText) etc.
} else {
// Handle error case
}
}
};
req.send(body);
}
This is what I see in network tab:

You are having CORS problems. This is why it's working with Postman, it skips that check (OPTIONS call to the same request instead the GET one) and your React App (or any Javascript Ajax call ) fails, because your browser is checking it before launch the request..
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
This post shows how to deal with CORS
https://www.eriwen.com/javascript/how-to-cors/

Related

How do I make this Request API work with CORS?

Thanks for reading.
I'm making a Spoiler Blocker Chrome Extension, and as you can imagine that's been quite a journey...
Right now I have everything sorted out, except the last part. Because of CORS Policy, I have to use a middleman (Cloudflare worker) to access the Razor API from the extension (returns data about entities).
I found an open source code from managing requests with Cors activated for the Cloudflare worker, but it's been days, and I can't get it to send requests as I want it to. I tried predetermining the url to analyze and other stuff, but it just doesn't work.
´´´
The API: https://www.textrazor.com/docs/rest
fetch('https://secret.workers.dev/?https://api.textrazor.com', {
method: 'POST',
body: JSON.stringify({
'extractors': 'entities,sentences',
'text': txt
}),
headers: {
'x-cors-headers': JSON.stringify({
'Content-Type': 'application/x-www-form-urlencoded',
'X-TextRazor-Key': 'secret',
'Accept-Encoding': 'gzip'
})
}
Cloudflare worker code:
addEventListener("fetch", async event=>{
event.respondWith((async function() {
isOPTIONS = (event.request.method == "OPTIONS");
var origin_url = new URL(event.request.url); ```
function fix(myHeaders) {
// myHeaders.set("Access-Control-Allow-Origin", "*");
myHeaders.set("Access-Control-Allow-Origin", event.request.headers.get("Origin"));
if (isOPTIONS) {
myHeaders.set("Access-Control-Allow-Methods", event.request.headers.get("access-control-request-method"));
acrh = event.request.headers.get("access-control-request-headers");
//myHeaders.set("Access-Control-Allow-Credentials", "true");
if (acrh) {
myHeaders.set("Access-Control-Allow-Headers", acrh);
}
myHeaders.delete("X-Content-Type-Options");
}
return myHeaders;
}
var fetch_url = decodeURIComponent(decodeURIComponent(origin_url.search.substr(1)));
var orig = event.request.headers.get("Origin");
var body = event.request;
var remIp = event.request.headers.get("CF-Connecting-IP");
if ((!isListed(fetch_url, blacklist)) && (isListed(orig, whitelist))) {
xheaders = event.request.headers.get("x-cors-headers");
if (xheaders != null) {
try {
xheaders = JSON.parse(xheaders);
} catch (e) {}
}
if (origin_url.search.startsWith("?")) {
recv_headers = {};
for (var pair of event.request.headers.entries()) {
if ((pair[0].match("^origin") == null) && (pair[0].match("eferer") == null) && (pair[0].match("^cf-") == null) && (pair[0].match("^x-forw") == null) && (pair[0].match("^x-cors-headers") == null)) {
recv_headers[pair[0]] = pair[1];
}
}
if (xheaders != null) {
Object.entries(xheaders).forEach((c)=>recv_headers[c[0]] = c[1]);
}
var myHeaders = new Headers();
myHeaders.append('x-textrazor-key', 'secret');
myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');
var myBody = {
extractors: 'entities,sentences',
url: 'https://en.wikipedia.org/wiki/Marty_McFly'
}
newreq = new Request("https://api.textrazor.com", {
method: 'POST',
headers: myHeaders,
body: myBody
})
var response = await fetch(fetch_url,newreq);
var myHeaders = new Headers(response.headers);
cors_headers = [];
allh = {};
for (var pair of response.headers.entries()) {
cors_headers.push(pair[0]);
allh[pair[0]] = pair[1];
}
cors_headers.push("cors-received-headers");
myHeaders = fix(myHeaders);
myHeaders.set("Access-Control-Expose-Headers", cors_headers.join(","));
myHeaders.set("cors-received-headers", JSON.stringify(allh));
if (isOPTIONS) {
var body = null;
} else {
var body = await response.arrayBuffer();
}
var init = {
headers: myHeaders,
status: (isOPTIONS ? 200 : response.status),
statusText: (isOPTIONS ? "OK" : response.statusText)
};
return new Response(body,init);
} else {
var myHeaders = new Headers();
myHeaders = fix(myHeaders);
if (typeof event.request.cf != "undefined") {
if (typeof event.request.cf.country != "undefined") {
country = event.request.cf.country;
} else
country = false;
if (typeof event.request.cf.colo != "undefined") {
colo = event.request.cf.colo;
} else
colo = false;
} else {
country = false;
colo = false;
}
return new Response(
"CLOUDFLARE-CORS-ANYWHERE\n\n" +
"Source:\nhttps://github.com/Zibri/cloudflare-cors-anywhere\n\n" +
"Usage:\n" + origin_url.origin + "/?uri\n\n" +
"Donate:\nhttps://paypal.me/Zibri/5\n\n" +
"Limits: 100,000 requests/day\n" +
" 1,000 requests/10 minutes\n\n" +
(orig != null ? "Origin: " + orig + "\n" : "") +
"Ip: " + remIp + "\n" +
(country ? "Country: " + country + "\n" : "") +
(colo ? "Datacenter: " + colo + "\n" : "") + "\n" +
((xheaders != null) ? "\nx-cors-headers: " + JSON.stringify(xheaders) : ""),
{status: 200, headers: myHeaders}
);
}
} else {
return new Response(
"Create your own cors proxy</br>\n" +
"<a href='https://github.com/Zibri/cloudflare-cors-anywhere'>https://github.com/Zibri/cloudflare-cors-anywhere</a></br>\n" +
"\nDonate</br>\n" +
"<a href='https://paypal.me/Zibri/5'>https://paypal.me/Zibri/5</a>\n",
{
status: 403,
statusText: 'Forbidden',
headers: {
"Content-Type": "text/html"
}
});
}
}
)());
});
Error: "Please specify a TextRazor request."
Thanks in advance!
EDIT:
So, I tried doing the background thingy. But... it doesn't work
background.js after receiving the message from content.js
chrome.runtime.onMessage.addListener(function (msg, sender, value, request, sendResponse) {
if (msg.from == "content") { //get content scripts tab id
contentTabId = sender.tab.id;
var myHeaders = new Headers();
myHeaders.append("x-textrazor-key", "secret");
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("text", request.txt);
urlencoded.append("extractors", "entities,entailments");
fetch("https://api.textrazor.com/", {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
}).then((response) => sendResponse(response.json()));
return true; // Will respond asynchronously.
}
content.js after receiving the first message from background (that orders it to look up the text from the page the user is on)
chrome.runtime.sendMessage({
contentScriptQuery: "querySpoiler", txt: txt, messsage: { from: "content" }, responseCallback: function (response) {
request = response.json();
When looking onto it in DevTools, runtime.sendMessage arguments and caller return an error:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.r (:1:83)
at chrome-extension://gbbiidobmakfdibhklcfnadmobpekifb/content.js:61:15
Thanks again!
To make cross-domain requests in an extension, list all hosts you need to access in the extension manifest.
In manifest v3 the key is "host_permissions" ​(in manifest v2 it is "permissions")
The value will be an array of 1 or more match patterns
The example in original post shows the target host is https://api.textrazor.com. The extension manifest should include the following permission. After adding the permission, and within the extension context, it will be allowed to make requests to the specified host without needing a proxy server.
{
"host_permissions": [
"https://api.textrazor.com/*"
]
}
The * represents a wildcard and means: "match any path that starts with this pattern". You can change the match pattern based on your requirements to include different schemes, subdomains, paths, etc. and add more entries to the value to include more hosts in general. See: https://developer.chrome.com/docs/extensions/mv3/match_patterns for more details and examples of how to define match patterns.
After that, you can perform the API requests, example:
var myHeaders = new Headers();
myHeaders.append("x-textrazor-key", "API_KEY_HERE");
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("text", "Spain's stricken Bankia expects to sell off its vast portfolio of industrial holdings that includes a stake in the parent company of British Airways and Iberia.");
urlencoded.append("extractors", "entities,entailments");
fetch("https://api.textrazor.com/", {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
})
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));

Postman post request works but ajax post does not. Have checked client side js over and over

first question ever on stackoverflow and boy do I need an answer. My problem is that I have an endpoint to create an item, and it works when I send a POST request with Postman. I'm using node and express:
router.post("/", jwtAuth, (req, res) => {
console.log(req.body);
const requiredFields = ["date", "time", "task", "notes"];
requiredFields.forEach(field => {
if (!(field in req.body)) {
const message = `Missing \`${field}\` in request body`;
console.error(message);
return res.status(400).send(message);
}
});
Task.create({
userId: req.user.id,
date: req.body.date,
time: req.body.time,
task: req.body.task,
notes: req.body.notes
})
.then(task => res.status(201).json(task.serialize()))
.catch(err => {
console.error(err);
res.status(500).json({ message: "Internal server error" });
});
});
That endpoint works when I send with Postman and the req body logged with the right values.
But when I send my ajax request, my server code logs the req.body as an empty object ('{}'). Because Postman works I believe the problem is with my client side javascript but I just cannot find the problem. I and others have looked over it a million times but just can't find the problem. Here is my client side javascript:
//User submits a new task after timer has run
function handleTaskSubmit() {
$(".submit-task").click(event => {
console.log("test");
const date = $(".new-task-date").text();
const taskTime = $(".new-task-time").text();
const task = $(".popup-title").text();
const notes = $("#task-notes").val();
$(".task-notes-form").submit(event => {
event.preventDefault();
postNewTask(date, taskTime, task, notes);
});
});
}
function postNewTask(date, taskTime, task, notes) {
const data = JSON.stringify({
date: date,
time: taskTime,
task: task,
notes: notes
});
//Here I log all the data. The data object and all its key are defined
console.log(data);
console.log(date);
console.log(taskTime);
console.log(task);
console.log(notes);
const token = localStorage.getItem("token");
const settings = {
url: "http://localhost:8080/tasks",
type: "POST",
dataType: "json",
data: data,
contentType: "application/json, charset=utf-8",
headers: {
Authorization: `Bearer ${token}`
},
success: function() {
console.log("Now we are cooking with gas");
},
error: function(err) {
console.log(err);
}
};
$.ajax(settings);
}
handleTaskSubmit();
What I would do:
Change header 'application/json' to 'application/x-www-form-urlencoded' since official docs have no info on former one.
Stop using $.ajax and get comfortable with XHR requests, since jquery from CDN is sometimes a mess when CDN get's laggy and XHR is a native implement and available immediately. Yes it's a code mess, but you always know that it is not the inner library logic thing, but your own problem. You blindly use library, that conceals XHR inside and you do not know how to ask the right question "XHR post method docs" because you are not yet comfortable with basic technology underneath.
Save this and import the variable
var httpClient = {
get: function( url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString = '';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
}
if (queryString.length !== 0) {
url += (url.indexOf('?') === -1 ? '?' : '&') + queryString;
}
xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.send(null);
},
post: function(url, data, callback ) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var readyState = xhr.readyState;
if (readyState == 4) {
callback(xhr);
}
};
var queryString='';
if (typeof data === 'object') {
for (var propertyName in data) {
queryString += (queryString.length === 0 ? '' : '&') + propertyName + '=' + encodeURIComponent(data[propertyName]);
}
} else {
queryString=data
}
xhr.open('POST', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(queryString);
}
};
usage is as simple as jquery: httpClient.post(Url, data, (xhr) => {})
Check if you have body parser set-up in app.js
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({ extended: true })); // get information from html forms
if body parser is set-up try changing header to 'multipart/form-data' or
'text/plain'.
For just the sake check req.query
Cheers! :)

Testing an AJAX function with xhr-mock fails

I'm trying to test the following function from my network.js:
export function post (data) {
return new Promise(function (resolve, reject) {
// need to log to the root
var url = window.location.protocol + '//' + window.location.hostname
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 204) {
resolve(null)
} else {
reject(new Error('an error ocurred whilst sending the request'))
}
}
}
xhr.open('POST', url, true)
xhr.setRequestHeader('Content-type', 'application/json')
xhr.send(JSON.stringify(data))
})
}
My test case looks like this:
import xhrMock from 'xhr-mock'
import * as network from '../src/network'
describe('Payload networking test suite', function () {
beforeEach(() => xhrMock.setup())
afterEach(() => xhrMock.teardown())
test('POSTs JSON string', async () => {
expect.assertions(1)
xhrMock.post(window.location.protocol + '//' + window.location.hostname, (req, res) => {
expect(req.header('Content-Type')).toEqual('application/json')
return res.status(204)
})
await network.post({})
})
})
When running my test suite I'm getting:
xhr-mock: No handler returned a response for the request.
POST http://localhost/ HTTP/1.1
content-type: application/json
{}
This is mostly based on the documentation and I don't understand why its failing
Solution
add a trailing / to the url you are giving xhrMock.post()
Error Details
The url is http://localhost.
That turns into a req.url() of
{
protocol: 'http',
host: 'localhost',
path: '/',
query: {}
}
Calling toString() on that object returns 'http://localhost/'
xhr-mock compares the URLs by doing req.url().toString() === url
'http://localhost/' === 'http://localhost' returns false so xhr-mock is returning an error that no handler returned a response.
I found I had some problems as well and using the following module was a better alternative for me:
https://github.com/berniegp/mock-xmlhttprequest
Usage is pretty straight forward:
const MockXMLHttpRequest = require('mock-xmlhttprequest');
const MockXhr = MockXMLHttpRequest.newMockXhr();
// Mock JSON response
MockXhr.onSend = (xhr) => {
const responseHeaders = { 'Content-Type': 'application/json' };
const response = '{ "message": "Success!" }';
xhr.respond(200, responseHeaders, response);
};
// Install in the global context so "new XMLHttpRequest()" uses the XMLHttpRequest mock
global.XMLHttpRequest = MockXhr;

HTTP headers not getting sent with fetch()

I'm trying to configure the backend API on my app and here's the code to send a request:
static async xhr(endpoint, args, method) {
const url = `${API_SERVER}${endpoint}`;
let formBody = [];
for (let property in args) {
let encodedKey = encodeURIComponent(property);
let encodedValue = encodeURIComponent(args[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
let options = Object.assign({ method: method }, args ? { body: formBody } : null );
try {
let headers = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
};
accessToken = 'bdi8HD8js91jdoach7234h';
if(accessToken != null)
headers['Authorization'] = accessToken;
options.headers = headers;
return fetch(url, options).then( resp => {
console.log(resp);
let json = resp.json();
if(resp.status >= 200 && resp.status < 300) {
if (resp.ok) {
return json
}
} else {
return {};
}
return json.then(err => {throw err});
});
} catch (error) {
return null;
}
}
Note: I debugged and found that the headers are correctly getting added to the options variable, but for some reason, the server isn't receiving the Authorization header.
I used Postman to send the exact same request with the exact same headers and I'm getting the correct response via it. I have no idea what's wrong, except it would only be so if the headers aren't getting sent in the first place.
Can someone please tell me what am I doing wrong? Thanks!
The headers option has to be an instance of Headers. You can transform your current headers object to a Headers instance by passing it to its constructor like this:
const headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
});
Note that I also replaced let with const since that variable is not going to be reassigned.
To change a header or add a new header to that Headers instance, you can use the set method. Instead of headers['Authorization'] = accessToken you'd do ...
headers.set('Authorization', accessToken)

How to get all Files Save in Dropbox to my HTML Page

i have save Images in Dropbox using Javascript like that
document.forms.newsletter.addEventListener('submit', function
cb(evt) {
//evt.preventDefault()
// API key from here: https://dropbox.github.io/dropbox-api-v2-
explorer/#files_upload
// need to consider how this gets secured
var TOKEN = ''
var dir = 'blackground/'
var file = document.getElementById('file').files[0]
var promise = new Promise(function (resolve, reject) {
// Dropbox requires application/octet-stream
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
}
else {
reject(xhr.response || 'Unable to upload file');
}
};
xhr.open('POST',
'https://content.dropboxapi.com/2/files/upload');
xhr.setRequestHeader('Authorization', 'Bearer ' + TOKEN);
xhr.setRequestHeader('Content-Type', 'application/octet-
stream');
xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify({
path: '/' + dir + file.name,
mode: 'add',
autorename: true,
mute: false
}));
xhr.send(file);
})
promise
.then(function (result) {
// Save dropbox response to form
document.getElementById('dropbox').value =
JSON.stringify(result)
// Submit form on successful upload
evt.target.submit()
})
.catch(function (err) {
console.log('a');
})
return false
})
It works fine. But i want to retrieve each Image using Javascript and ajax to display it in my Web Page. How to make it?
I read this Documentation https://www.dropbox.com/developers-v1/core/docs#files-GET that we can make it with Get Verb.
I have make a Get for the API to get All Image like so
$.ajax({
method: "GET",
url: "https://content.dropboxapi.com/1/files/auto/blackground",
dataType: "json",
ContentType:"application/octet-stream",
Authorization:"Bearer token"
});
i get this Error
{error: "Authentication failed"}
error
:
"Authentication failed"
blackground is the folder where are all the Images
Something can help please
It works fine now. I make it like so. Token is my Token for Dropbox.
var token = '';
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var imageUrl = (window.URL || window.webkitURL).createObjectURL(xhr.response);
// display, assuming <img id="image"> somewhere
document.getElementById('image').src = imageUrl;
// download via the download attribute: limited browser support
var a = document.createElement('a');
//a.download = 'test.png';
//a.href = imageUrl;
//a.click();
}
};
xhr.open('POST', 'https://content.dropboxapi.com/2/files/download');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify({ path: '/blackground/blackground_logo.jpg' }));
xhr.send();

Categories

Resources