Unable to add custom header to response - ajax

I am unable to add a custom header to a response that is returned from render():
response = render(request, 'my_template.html', {'ctx1': 1, 'ctx2': 2})
response['My-Custom-Header'] = 'abc12345'
return response
This is a response to an Ajax request initiated using jQuery's $.get(). On Chrome, the response has the template rendered properly, but it does not have the custom header. If I print the response object before returning, I see that it has my custom header.
I thought the issue was because of this answer, so I added Django middleware to add a header to all responses:
class CustomHeaderMiddleware():
def process_response(self, request, response):
response['Access-Control-Expose-Headers'] = 'My-Custom-Header'
return response
All my responses now have header Access-Control-Expose-Headers: My-Custom-Header, but I still see this issue.
My request is local; I'm using the Django development web server.
If I send a non-Ajax GET request, whatever custom header I add in Django is visible in the response on Chrome. So this issue seems to be limited to Ajax requests.
I am using Django 1.11.4 and Python2.
[edit]
If I capture the response in WireShark, I see that it does not have My-Custom-Header. Furthermore, if I add a custom header to the response dictionary in the above middleware, the header shows up in Wireshark and is visible in Chrome. So this seems to be a Django issue with responses to Ajax requests.

This issue is unrelated to Ajax. I was calling my view using this template tag. The custom response headers are lost because of line 32 in that code.

Related

Postman GET returning HTML and status 200

I am trying to call a Postman GET endpoint, and it is returning HTML and status 200, instead of a valid JSON response and status 200. It looks like it is not hitting the backend at all. I have a separate POST endpoint, which works fine and returns me a valid JSON response.
Could this be due to authorization headers? I am using the same authorization for both.
I managed to resolve the issue. I had to add a cookie header, which I got from the browser since I was able to call the service from the browser and I had authenticated on browser level.

Laravel 5.3 Returns 302 with Request Validation - Ajax Request

I am having trouble with an ajax request to a Laravel application, specifically making a POST request to an authentication controller. I'm sending a post request with SuperAgent to a controller that uses a Request class to validate the input. The request carries a password and a username. When I inspect the console I'm getting back a GET 200 OK and a POST 302 Not Found. I tried debugging the application routes but nothing seemed to work.
Turns out it was something very simple. Having used jquery for a long time to make ajax request, I overlooked a very important header. The 'Accept', 'application/json' Header. Debugging the Request validation, I noticed that Laravel's expectsJson method was returning false, so all I had to do was add said header to the SuperAgent request object.

Heroku CSRF and POST httpRequest

I have a web crawler on Heroku and I'm trying to call the script from a POST request on Parse Cloud Code httpRequest but I receive a 403 forbidden response basically telling me the Referer Header didn't pass. How can I get past this?
Django's CSRF protection tests the Referer header: see https://docs.djangoproject.com/es/1.9/ref/csrf/#how-it-works. Browsers typically send that header to indicate the page that originated a request, but programmatic user agents don't (cURL, Python requests, and presumably Parse.Cloud.httpRequest) without being told to do so.
To add custom headers to a Parse request, see: Parse.Cloud.httpRequest call with HTTP request header (note the headers object).
That said, you also need to make sure you have a way to get the CSRF token to begin with, and include it either in a XCSRF-Token header or a form field (unclear from your question whether you are doing that).

How to make angular2 properly trigger the correct callback after successful preflight requests

In my angular2 app, I tried to make a cross domain ajax call. Although the call went through successfully, on angular2 side, the error callback was always triggered instead of the success callback. Here is the code:
this.http.post('http://localhost:3000/tasks/add', '["' + task + '"]', {headers: headers}).subscribe(
data => null,
err => alert("err"),
() => alert("succ")
);
In the browser console I saw this:
XMLHttpRequest cannot load http://localhost:3000/tasks/add. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
The server side was implemented in nodejs and it included the following to handle preflight
router.options('/add', (req, res, next) => {
console.log('!OPTIONS');
var headers = {};
// IE8 does not allow domains to be specified, just the *
// headers["Access-Control-Allow-Origin"] = req.headers.origin;
headers["Access-Control-Allow-Origin"] = "*";
headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS";
headers["Access-Control-Allow-Credentials"] = false;
headers["Access-Control-Max-Age"] = '86400'; // 24 hours
headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept";
res.writeHead(200, headers);
res.end();
});
The above server code was successfully executed, and client side did send the post request after the preflight options request.
My question is, what can I do in angular2 to make it trigger the success callback, which it should?
Update:
I am attaching a screenshot here and it was took in Chrome. The bottom part of it shows CORS error message, but top portion shows that the request went okay with 200. As a matter of fact it did go through, as I received both the options and post requests on the server side.
Update 2:
Tried express-cors on nodejs (server) side, still the same. (My initial code already handles preflight options request - the code can be found above in the original portion of this post. Unless there was a mistake in my original nodejs code, express-cors was not expected to change anything.)
Below is the code I tried with express-cors, basically the same as what's on its npm page:
var express = require('express');
var cors = require('express-cors')
var router = express.Router();
router.use(cors({
allowedOrigins: [
'*'
]
}))
Update 3:
Mystery solved! In my original code I only set the CORS headers in the options response, but not the response to the post request followed the options request. That appeared to still allow the whole process to complete at the application level - since the server side did get the post request and processed the data, but angular saw an error since the response did not include the proper headers. I modified my code to not only send back CORS headers in the options response, but also the post response, and now angular triggers the success callback.
In fact, there is a dedicated module for CORS with Express called cors. You can install it like this: npm install cors.
That said, there is nothing to do in the browser since everything is done under the hood for cross domain requests:
The browser sends the Origin header
Use simple or preflighted requests
Check the CORS response headers to see if your request can be executed
This link could help you to understand how CORS works within browsers: http://restlet.com/blog/2015/12/15/understanding-and-using-cors/
So in short, it's a server issue not something in the Angular application. You can check the response content (headers) in the Chrome dev tools, tab Network to see that CORS headers are there in the response (both simple and preflighted).
Your error message doesn't look like the preflight request was successful. The preflight request is made by the browsers automatically before the actual request, if the preflight request results in an error, like in your case, the actual request isn't made at all.
Try this on the server instead:
headers["Access-Control-Allow-Origin"] = "http://localhost";

Angular resource how to keep ajax header and enable cors at the same time

In my ng-resource files, I enable the ajax header:
var app = angular.module('custom_resource', ['ngResource'])
app.config(['$httpProvider', function($httpProvider) {
//enable XMLHttpRequest, to indicate it's ajax request
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
}])
app.factory('Article', ['$resource', function($resource) {
return $resource('/article/api/:articleId', {articleId: '#_id'}, {
update: {method: 'PUT'},
query: {method: 'GET', isArray: true}
})
}])
So that I can separate ajax and non-ajax request and response accordingly (to send json data like res.json(data), or to send the entire html page like res.render('a.html')
for example, in my error handler, I need to decide to render error.html page or to just send a error message:
exports.finalHandler = function(err, req, res, next) {
res.status(err.status || 500)
var errorMessage = helper.isProduction() ? '' : (err.message || 'unknown error')
if (req.xhr) {
res.json({message: errorMessage})
}
else {
res.render(dir.error + '/error_page.ejs')
}
}
But now I need to do CORS request to other sites. Is it possible to do CORS request while keeping the ajax header? or other ways I can identify ajax and non-ajax request from server?
In case my question is not clear, heres a relevant article about angular and CORS
http://better-inter.net/enabling-cors-in-angular-js/
Basically, we need to delete xhr header to enable cors for other server, but I need the header for my own server
EDIT 2:
today I tried integrating google map and I got this error:
XMLHttpRequest cannot load http://maps.googleapis.com/maps/api/geocode/json?address=Singapore&sensor=false. Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers.
Setting custom headers on XHR requests triggers a preflight request.
So, it doesn't disable CORS but your server is most likely not handling the preflight request.
Inspired from this post: https://remysharp.com/2011/04/21/getting-cors-working
The solution should be to use the cors module and add the following to your node.js code before your routes:
var corsOptions = {
origin: true,
methods: ['GET', 'PUT', 'POST'],
allowedHeaders: ['X-Requested-With','Content-Type', 'Authorization']
};
app.options('*', cors(corsOptions)); //You may also be just fine with the default options
You can read more at: https://github.com/expressjs/cors
you may try to use cors package
First, to address you primary concern is it possible to do CORS request while keeping the ajax header?: the answer is YES, provided the sites you are accessing allow requests from you or any other external clients at all.
You wrote:
//Note: this disables CORS
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
But I don't understand what you mean by, it "disables CORS". The X-Requested-With header is not a standard header, and the known effect of adding a non-standard header to a request (made from a browser) is the triggering of a pre-flight request [3].
If the other sites you are interested in would set their servers to refuse processing of requests that do not originate from their own domain, then whether you set that header or not, your request should fail.
It seems everything is working fine for you, for requests sent to you own server. Otherwise you can solve the problem by appending the Access-Control-Allow-Origin header in your server responses as follows:
if you need to allow requests from specific domains
response.set("Access-Control-Allow-Origin", "one-host-domain, your-host-domain, some-other-host-domain"); // second argument is a comma-delimited list of allowed domains
(It may be better for you to actually check the request object for the origin, and if it's permitted based on presence in a pre-determined list, then send back the exact same origin).
If you need to permit all requests regardless of its origin
response.set("Access-Control-Allow-Origin", "*");
That should do, and I hope it clears your doubts for you.
More info on handling CORS when using AJAX: 0, 1 & 2.
EDIT
Following exchanges in the comment, I add the following points to support this answer further.
As it is today, the only side that needs disabling/enabling CORS in the client-server system is the server. All modern browsers allow cross origin requests by default and you don't need to do anything additional to support that capability. I understood that you're adding a custom header to distinguish AJAX requests from the rest?? AFAIK, that header changes nothing about how requests are made by browsers.
Here is how all cross-origin requests are handled by browsers today: for all request methods (but usually with the exception of GET), browsers send a pre-flight request with the OPTION method. If the destination server allows it, the actual request is then sent, otherwise the request fails. In the case where the servers, responds with a refusal there's nothing you nor whatever library you use can do about it. This is the fact from my own experience.
There are 3 solutions that come to my mind:
1. Ask site's admin to enable x-requested-with header in CORS.
2. Use proxy server.
3. Send request without x-requested-with header.
This article should make it clear how CORS works and how to make CORS requests.
Particularly "Simple requests" section and "Access-Control" section, especially access-control-allow-headers description is important in this case.
As it says: for simple requests access-control-allow-origin is enough. However if the request includes custom header (a header which is not included by default, such as x-requested-with header), the preflight request is triggered, and server's response to this request should enable this custom header in access-control-allow-headers by setting its value to either "*" or to the name of a custom header (x-requested-with).
Hope it makes it a little bit clearer.

Resources