I'm new to Javascript, and it's a mind bending world to me.
I have a [web app] that talks to a [proxy] that talks to a [web service].
In the signature, onProxyRes(proxyRes, req, res), where do each of the parameters originate? It seems like all documentation assumes you already know how it works.
So far, it seems obvious to me req was the original request, does this mean res is the original response? If I am trying to inject headers, should I be updating proxyRes?
I'm having a CORS error, I believe I just need to inject the header...
'Access-Control-Allow-Origin', '*'
https://www.npmjs.com/package/http-proxy-middleware
indicates how to use this feature.
function onProxyRes(proxyRes, req, res) {
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
}
should work.
However, I haven't gotten my example to work yet, so I am hoping another reply will come in, or you have it figured out now.
Related
I'm trying to subscribe to a webhook (post notifications) on YouTube and I keep getting the error Invalid value for hub.mode even though I ensured it was "subscribe."
Here is my post body:
{
"hub.callback": "http://x.x.x.x:xxxx/xxxxxxx",
"hub.topic": "https://www.youtube.com/xml/feeds/videos.xml?channel_id=x",
"hub.verify": "async",
"hub.mode": "subscribe",
"hub.verify_token": verifyToken,
"hub.secret": secret,
"hub.lease_seconds": 864000
}
I've tried it capitalized too, didn't work. Any ideas? The post is short and I don't give a lot of information, but there is no other information I can give that would help you, but if you know why this happens could you let me know?
Thanks to Intoxicated Penguin for the suggestion to put the post data in with the url as a query itself, that seemed to have worked.
So my path changed from /subscribe to /subscribe?hub.callback=xxx...&hub.mode=subscribe and it worked since then!
I'm working with angularjs, and from my service I have to make a call to the server using a path parameter (id), query params (var1 and var2) and a body request ({"codes": ["1000"]}) - which has to be sent as an string array, within a get method (I know, sending a body request should be done within a POST).
So far, in my service I have this:
function getSub(id, var1, var2) {
var payload = {
first: var1,
second: var2
};
var url = 'sub/' + id + '/mylink'
return api.get(url, payload, {"codes": ["1000"]}).then(function (response) {
console.log("READ RESPONSE ", response);
return response;
});
};
So far, all I am getting is a bad request error linked to the response body not provided.
It may be a noob question, and not a best practice one, but I meed to find a solution for this. So far, by searching the net far and wide, I could only understand that this is an unorthodox way of using body request.
Thanks in advance!
Yes, you can send a request body with GET but it make no sense. you can parse it on the server and modify your response based on its contents, you are ignore this recommendation in the HTTP / 1.1 specification, section 4.3:
As far as I know, the standard has no statement about the body of a get type request. Thus this is a classical it depends on the implementation. Anyway, implementations tend not to support such a combination. And XMLHttpRequest is one of them:
send() accepts an optional parameter which lets you specify the request's body; this is primarily used for requests such as PUT. If the request method is GET or HEAD, the body parameter is ignored and the request body is set to null.
Background
I'm using express-http-proxy to proxy a handful of requests between my SPA (single page application) and a CouchDB instance. I'm doing this proxy on a per call basis, NOT creating a proxy server (this will be important in a moment).
example of current use
app.use(`some/url`, proxy(dburl, {
forwardPath: req => {return 'some/url'+require('url').parse(req.url).path;}
}) );
Which means I am NOT using httpProxy.createServer. I want to send some snippet of text data along with my responses as a header. After looking through the documentation I've come to the conclusion that what I want will be using intercept. Unfortunately I've not quite managed to grasp how to use it, and the only related questions I've found so far appear to be based on httpProxy.createServer which appears (from my limited understanding) to work differently.
We are using individual request proxying because we wish to proxy different requests to different micro-services, and found this to be the most concise way (that we knew of & at the time) of doing that.
The Question
Given the code
const text = 'asdf';
app.use(`some/url`, proxy(dburl, {
forwardPath: req => {return 'some/url'+require('url').parse(req.url).path;},
intercept: function(rsp, data, req, res, callback) {
//SUSPECT LOCATION
}
}) );
Is there some code at SUSPECT LOCATION which would allow me to place text on the header for the final response without further affects to the (currently otherwise working) proxy?
Additional Notes
Headers and network requests in general are not very familiar to me, my apologies if the answer seems self evident.
Bonus points for a link to a resource that helps explain either the finer points of using this library for proxying, a similar library for proxying, or the underlying technologies which would make it clear how to use this library for proxying. AKA I'd rather spend some of my own time looking further into this and not come back for further questions.
I am not entirely confident that the place for my code will be SUSPECT LOCATION and I will happily listen if it needs to go somewhere else, or if we need to approach this problem in a different way.
The accepted answer is now outdated.
Intercept does not exist anymore.
Instead, use your own middleware before the proxy function
router.route('/my-route').get((req, res, next) => {
res.set('My-Header', 'my-header-value');
next();
}, proxyFunction);
It follows express.js methods on req, res objects.
Within the intercept function body, set the response headers using the following express format.
res.set('hola', 'amigos!!!');
Refer below link:
http://expressjs.com/en/4x/api.html#res.set
The best way to understand a library when there is no documentation is to follow its test suite. If there is no test suite don't use that library.
This is the test suite for the express-http-proxy intercept function
https://github.com/villadora/express-http-proxy/blob/master/test/intercept.js
This is the test case
it('can modify the response headers', function(done) {
var app = express();
app.use(proxy('httpbin.org', {
intercept: function(rsp, data, req, res, cb) {
res.set('x-wombat-alliance', 'mammels');
res.set('content-type', 'wiki/wiki');
cb(null, data);
}
}));
request(app)
.get('/ip')
.end(function(err, res) {
if (err) { return done(err); }
assert(res.headers['content-type'] === 'wiki/wiki');
assert(res.headers['x-wombat-alliance'] === 'mammels');
done();
});
});
If you want to undetstand in and out of proxying, the best resource is haproxy
http://cbonte.github.io/haproxy-dconv/1.7/intro.html
But before that you need to understand http more (a constructive comment)
I have an error reporting beacon I created using Google Apps script and it is published to run as myself and to be accessible to "anyone, even anonymous," which should mean that X-domain requests to GAS are allowed.
However, my browsers are now indicating there is no Access-Control-Allow-Origin header on the response after the code posts to the beacon.
Am I missing something here? This used to work as recently as two months ago. So long as the GAS was published for public access, then it was setting the Access-Control-Allow-Origin header.
In Google Apps Script:
Code.gs
function doPost(data){
if(data){
//Do Something
}
return ContentService.createTextOutput("{status:'okay'}", ContentService.MimeType.JSON);
}
Client Side:
script.js
$.post(beacon_url, data, null, "json");
When making calls to a contentservice script I always have sent a callback for JSONP. Since GAS does not support CORS this is the only reliable way to ensure your app doesn't break when x-domain issues arrive.
Making a call in jQuery just add "&callback=?". It will figure everything else out.
var url = "https://script.google.com/macros/s/{YourProjectId}/exec?offset="+offset+"&baseDate="+baseDate+"&callback=?";
$.getJSON( url,function( returnValue ){...});
On the server side
function doGet(e){
var callback = e.parameter.callback;
//do stuff ...
return ContentService.createTextOutput(callback+'('+ JSON.stringify(returnValue)+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I've lost a couple of hours with the same issue. The solution was trivial.
When you deploy the script as webapp, you get two URLs: the /dev one and the /exec one. You should use /exec one to make cross domain POST requests. The /dev one is always private: it requires to be authorized and doesn't set *Allow-Origin header.
PS.: The /exec one seems to be frozen — it doesn't reflect any changes of code until you manually deploy it with a new version string (dropdown list in deploy dialog). To debug the most recent version of the script with the /dev URL just install an alternative browser and disable it's web-security features (--disable-web-security in GoogleChrome).
Just to make it simpler for those who are only interested in a POST request like me:
function doPost(e){
//do stuff ...
var MyResponse = "It Works!";
return ContentService.createTextOutput(MyResponse).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I stumbled upon the same issue:
calling /exec-urls from the browser went fine when running a webpage on localhost
throws crossorigin-error when called from a https-domain
I was trying to avoid refactoring my POST JSON-clientcode into JSONP (I was skeptical, since things always worked before).
Possible Fix #1
Luckily, after I did one non-CORS request (fetch() in the browser from a https-domain, using mode: no-cors), the usual CORS-requests worked fine again.
last thoughts
A last explanation might be: every new appscript-deployment needs a bit of time/usage before its configuration actually settled down at server-level.
Following solution works for me
In Google Apps Script
function doPost(e) {
return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);
}
In JavaScript
fetch(URL, {
redirect: "follow",
method: "POST",
body: JSON.stringify(DATA),
headers: {
"Content-Type": "text/plain;charset=utf-8",
},
})
Notice the attribute redirect: "follow" which is very very important. Without that, it doesn't work for me.
I faced a similar issue of CORS policy error when I tried to integrate the app script application with another Vue application.
Please be careful with the following configurations:
Project version should be NEW for every deployment.
Execute the app as me in case you want to give access to all.
Who has access to the app to anyone, anonymous.
Hope this works for you.
in your calling application, just set the content-type to text/plain, and you will be able to parse the returned JSON from GAS as a valid json object.
Here is my JSON object in my google script doPost function
var result = {
status: 200,
error: 'None',
rowID: rowID
};
ws.appendRow(rowContents);
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
and here I am calling my app script API from node js
const requestOptions = {
method: 'POST',
headers: {'Content-Type': 'text/plain'},
body: JSON.stringify({param1: value, param2:value})
};
const response = await fetch(server_URL, requestOptions);
const data = await response.json();
console.log(data);
console.log(data.status);
My case is different, I'm facing the CORS error in a very weird way.
My code works normally and no CORS errors, only until I added a constant:
const MY_CONST = "...";
It seems that Google Apps Script (GAS) won't allow 'const' keyword, GAS is based on ES3 or before ES5 or that kind of thing. The error on 'const' redirect to an error page URL with no CORS.
Reference:
https://stackoverflow.com/a/54413892/5581893
In case this helps all any of those people like me:
I have a .js file which contains all my utility functions, including ones which call a GAS. I keep forgetting to clear my cache when I go to test updates, so I'll often get this kind of error because the cached code is using the /dev link instead of the /exec one.
I've got some code that does an ajax request using jQuery, and handles success and error conditions. On an error, I want to find out what the URL I called was, so I can log it. This information appears to be contained in the XMLHttpRequest.channel, but firefox is complaining about accessing this -
Permission denied for <http://localhost:8081> to get property XMLHttpRequest.channel
Any ideas how I can determine the URL associated with an XMLHttpRequest? What's the security issue getting hold of this information? Cheers,
Colin
Ok - sorry about this - an answer is here
http://api.jquery.com/ajaxError/
specifically this code from above link -
$('.log').ajaxError(function(e, xhr, settings, exception) {
if (settings.url == 'ajax/missing.html') {
$(this).text('Triggered ajaxError handler.');
}
});
shows how to access the request url in the event of an ajax error. Doesn't explain why the XMLHttpRequest.channel object is a no go though. Anyway, hopefully that will help others with a similar problem.
Well, you just add it ;]
XMLHttpRequest.prototype.baseOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async) { this._url = url; return XMLHttpRequest.prototype.baseOpen.apply(this, arguments); };
then you can later ask for xhr._url in your error handler.
PS: Sorry, just discovered this thread is old.
The security issue is cross domain XHR requests.
In FF2 you used to be able to override this in about:config, also see this blog and especially this preference:
user_pref("capability.policy.default.XMLHttpRequest.channel", "allAccess");
But that's all not possible anymore in FF3. And with a good reason.
Note that XMLHttpRequest.channel is Gecko-specific, so this wouldn't have worked in non-Gecko browsers.
Firebug presents this error with a trace that shows the URI used.