NOTE:
I've found a possibly related issue that warrants a new question here
This is a weird problem. I've been using angular over the course of 2 years and have never run into this problem.
I'm using angular v1.5.0. I'm making a post request like this:
$http({
method: "POST",
url: "/myurl",
data: {
file: myFile // This is just an object
}
});
Run-of-the-mill POST request right? Get this. I look in the console and the Network tab logs the request as a GET. Bizarre. So I've jiggered the code to work like this:
$http.post("/myurl", {file: myFile});
Same thing. After stepping through the $http service code I'm confident that the headers are being set properly. Has anyone else run into this problem?
Update
Taking germanio's advice, i've tried using the $resource service instead:
promise = $resource("/upload").save()
(this returns an error for another reason, it still executes the POST properly). I'm having the same problem: the request is logged as a GET in the console.
Here are the headers of the request when it gets to my server:
GET /myurl/ HTTP/1.1
Host: localhost:8001
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Pragma: no-cache
Referer: http://localhost:8001/myurl/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Update 2
As per georgeawg's suggestion I've used an interceptor to log the request at its various stages. Here is the interceptor code:
$httpProvider.interceptors.push(function() {
return {
request: function(config) {
console.log(config);
return config;
}
}
}
Having run this code I get this logged:
data:Object // contains file object
headers: Object // has Content-Type set to multipart
method:"POST" // ???
url :"/myurl
So this means the request is being sent as a POST from within Angular, but it is still logged as a GET both in the browser and on my server. I think there is something low level at work here about the HTTP protocol that I dont understand.
Is the request sent to the server before it is actually logged in the browser? If so, that might atleast point to my server as the culprit.
In the hopes of finding out whats going on, here is my server code:
type FormStruct struct {
Test string
}
func PHandler(w http.ResponseWriter, r *http.Request) {
var t FormStruct
req, _ := httputil.DumpRequest(r, true)
log.Println(string(req))
log.Println(r.Method) // GET
log.Println(r.Body)
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&t)
log.Println("Decoding complete")
if err != nil {
log.Println("Error")
panic(err.Error()+"\n\n")
}
log.Println(t.Test)
w.Write([]byte("Upload complete, no errors"))
}
func main() {
http.HandleFunc("/myurl/", PHandler)
fmt.Println("Go Server listening on port 8001")
http.ListenAndServe(":8001", nil)
}
My server throws an EOF error when it receives the request:
2016/03/30 10:51:37 http: panic serving [::1]:52039: EOF
Not sure what an EOF would even mean in this context.
Update 3
By the suggestion of another use, I tried using POSTMAN to hit my server with a fake POST request. The server receives the request properly. This means to me that there is something up with how angular is making the POST request. Please help.
Any ideas?
Full server logs:
Go Server listening on port 8001
2016/03/30 11:13:08 GET /myurl/ HTTP/1.1
Host: localhost:8001
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Postman-Token: 33d3de90-907e-4350-c703-6c57a4ce4ac0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
X-Xsrf-Token: null
2016/03/30 11:13:08 GET
2016/03/30 11:13:08 {}
2016/03/30 11:13:08 Decoding complete
2016/03/30 11:13:08 Error
2016/03/30 11:13:08 http: panic serving [::1]:52228: EOF
goroutine 5 [running]:
net/http.(*conn).serve.func1(0xc820016180)
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1389 +0xc1
panic(0x3168c0, 0xc82000b1a0)
/usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:426 +0x4e9
routes.FUPHandler(0x1055870, 0xc820061ee0, 0xc820104000)
/Users/projectpath/routes.go:42 +0x695
net/http.HandlerFunc.ServeHTTP(0x4e7e20, 0x1055870, 0xc820061ee0, 0xc820104000)
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1618 +0x3a
net/http.(*ServeMux).ServeHTTP(0xc820014b40, 0x1055870, 0xc820061ee0, 0xc820104000)
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1910 +0x17d
net/http.serverHandler.ServeHTTP(0xc820016100, 0x1055870, 0xc820061ee0, 0xc820104000)
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:2081 +0x19e
net/http.(*conn).serve(0xc820016180)
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1472 +0xf2e
created by net/http.(*Server).Serve
/usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:2137 +0x44e
Update 4
I stumbled onto something interesting:
Charles logs a POST request when I post to myurl, but the response status is 301. After the POST a GET is logged. This is the GET that is hitting my server.
My server, as you can see above, does not do any sort of redirection. How is the 301 happening?
This is due to a security consideration.
In your situation when a redirect is sent back from the server to the browser, the browser will not repeat the POST request (but rather just a "simple" GET request).
Generally speaking a browser will not send POST data to a redirect URL because the browser is not qualified to decide if you're willing to send the same data to the new URL what you intended to send to the original URL (think about passwords, credit card numbers and other sensitive data). But don't try to circumvent it, simply use registered path of your handler to POST to, or any of the other tips mentioned in the linked answer.
For context see question:
Go web server is automatically redirecting POST requests
You can read more on the subject here:
Why doesn't HTTP have POST redirect?
This code actually send GET to server
$http({
method: 'POST',
params: {
LoginForm_Login: userData.username,
LoginForm_Password: userData.password
},
url: YOURURL
}).then(
You need to use transformRequest, sample below actually send POST
$http({
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: {
LoginForm_Login: userData.username,
LoginForm_Password: userData.password
},
url: YOURURL
}).then(
Related
I am trying to call one application to another where I am getting and error as "Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response." and in my back-end I have used the following code as mentioned below
List<String> originList = Arrays.asList(origins.trim().split("( )*,( )*"));
String origin = request.getHeader("Origin");
if (originList.contains(origin)) {
originAllow = origin;
} else {
originAllow = originList.get(0);
}
response.setHeader("Access-Control-Allow-Origin", originAllow);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, accept, authorization");
response.setHeader("Access-Control-Expose-Headers", "Location");
In originAllow I am passing the url which I am trying to access but I am getting the below error,
XMLHttpRequest cannot load http://<<url>>. Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response.
Please find the browser request and response header,
Response Header
Access-Control-Allow-Headers:x-requested-with, content-type
Access-Control-Allow-Methods:GET OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600
Allow:GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Connection:close
Content-Length:0
Content-Type:text/plain; charset=UTF-8
Server:Apache-Coyote/1.1
X-Application-Context::8080
Request Header
Accept:/
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,ms;q=0.6
Access-Control-Request-Headers:authorization, x-requested-with
Access-Control-Request-Method:GET
Connection:keep-alive
Host:myrestapi-dev
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
OPTIONS <>?page=0&search_param=test&size=10,desc HTTP/1.1
Host: myrestapi-dev
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Access-Control-Request-Headers: authorization, x-requested-with
Accept: /
Referer: http://localhost:9000/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,ms;q=0.6
I am running application in localhost:port and the other application is using deployed url where the protocol,host are different.
Please let me know Is there anything I need to add to access the url from ui(angular) for authorization moreover it is working in other browser but not in chrome
this is because your server isn't allowing the authorization header.
To address it:
response.setHeader("Access-Control-Allow-Headers", "Content-Type, authorization");
should help you. as I am not sure about your backend I need to explain a bit more.
This is because it is up to the server to specify that it accepts cross-origin requests (and that it permits the Content-Type request header).Moreover, You need to make AJAX call from the same domain or make server-side changes, allowing requests from external domains.To address this you need to make changes in headers at http://yourdomain.com by allowing your external domain in headers:
Access-Control-Allow-Origin: *
1- ref1
2- ref2
Possible solution in AngularJs. : Using JSONP with Angular
The AngularJS $http service has a built-in method called "jsonp".
var url = "some REST API Url" + "?callback=JSON_CALLBACK";
$http.jsonp(url)
.success(function(data){
// whatever
});
Also, the url had to be appended with ?callback=JSON_CALLBACK.
I am going to expand this solution using JSONP
exampleurl = 'http://wikidata.org/w/api.php?whatever....&format=json'
//Number 1 :
$.ajax({
dataType: "jsonp",
url: exampleurl ,
}).done(function ( data ) {
// do whatever
});
//Number 2 :
$.ajax({
dataType: "json",
url: exampleurl + '&callback=?',
}).done(function ( data ) {
// do whatever
});
//Number 3 :
$.getJSON( exampleurl + '&callback=?', function(data) {
// do whatever
});
more information
1- getJson
2-$.Ajax
I'm using angular to POST to an authentication endpoint; on the server side, I can see the request succeed, and proper CORS headers are set. Angular's origin is http://localhost:9000
On the server side, preflight OPTIONS requests always get a 200 back, so that seems OK.
On the client side, the $http.post always fails with an error code of 0, which from other research suggests something is still wrong with CORS. I've read the spec and tried a number of other answers, yet something is still missing.
Angular POSTs like this:
$http({
method: 'POST',
url: 'http://localhost:3000/auth/login',
data: {
username: $scope.username,
password: $scope.password
}
})
.then(function (response) {
/* etc. etc. */
}, function (response) {
/* This always triggers, with response.status = 0 */
console.log("ERROR: " + response.data);
console.log("Status: " + response.status);
console.log("Status text: " + response.statusText);
console.log("Headers: " + response.headers);
$scope.error = 'Something went wrong...';
});
Using curl to debug what the server is sending back, this is it:
< HTTP/1.1 302 Found
< X-Powered-By: Express
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
< Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, Content-Length, X-Requested-With
< Access-Control-Allow-Credentials: true
< Set-Cookie: ua_session_token=(blahblah); Path=/
< Location: /
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 23
< Date: Mon, 28 Dec 2015 15:08:17 GMT
< Connection: keep-alive
This is why I'm at a loss, as per the specification, the server seems to be doing the right thing?
Here's what the server gets from the client in terms of request headers:
HEADER host localhost:3000
HEADER content-type application/json;charset=UTF-8
HEADER origin http://localhost:9000
HEADER content-length 38
HEADER connection keep-alive
HEADER accept application/json, text/plain, */*
HEADER user-agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9
HEADER referer http://localhost:9000/
HEADER accept-language en-us
HEADER accept-encoding gzip, deflate
UPDATE tried something else with no luck, based on this post. It would seem Access-Control-Allow-Headers is case-sensitive, and angular is sending on the request accept, origin, content-type. I tweaked the server to parrot back the same, with no luck.
Alright, after applying my head to my keyboard for several hours, I've fixed it.
The answer seems to be that angular really doesn't like getting redirects in response to POST. When I changed the server endpoint to return just a plain auth token as text (the same token it was setting as a cookie anyway) rather than returning a redirect, the angular POST started working like a charm and falling through to the success handler.
Not sure I got deep enough into this to know why angular was behaving in that way; by playing around with it I found that if the redirect the server sent was to a nonexistent (404) URL that this could be replicated, EVEN IF the original POST returned that valid redirect.
I've got some Angular code where I am attempting to send a POST to a dev server.
var url = 'http://someDevUrl.com',
data = { 'someKey': 'some value' };
$http.post(url, data);
It sends the OPTIONS preflight request. I can see it hit the server, and the server gives it a happy response.
OPTIONS Response Headers:
HTTP/1.1 200 OK
Server: Cowboy
Date: Mon, 08 Dec 2014 18:20:44 GMT
Connection: close
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 30
Access-Control-Allow-Headers: x-requested-with, content-type, accept, origin, authorization, x-csrftoken
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Via: 1.1 vegur
but then Angular never sends the POST after that...
Typically when $http.post() only sends an OPTIONS request, that means the OPTIONS request returned an error (usually a CORS issue). But in this case, all is fine with the OPTIONS request & response, but it still won't send the POST.
Has anyone seen this before or have an idea of what might be preventing it from sending the POST?
EDIT:
I've got around the issue by adding a Content-Type: text/plain header to the request:
var url = 'http://someDevUrl.com',
data = { 'someKey': 'some value' },
config = {
headers: {
'Content-Type': 'text/plain'
}
}
};
$http.post(url, data, config);
which causes it to skip the OPTIONS preflight, thus avoiding the issue. I'm still curious to know why it was not working in the first place, since there was/is no CORS issue and the OPTIONS request was not sending back an error response.
I'm using CakePHP as backend and AngularJS as frontend, whereas front- & backend are in different domains so this is basically a CORS-situation.
Basically I'm trying to send the contents of a form to a Cake-API (later this is meant to do authentication part - but I'm failing earlier) via $http.post. So here is the code:
aeapBackend.login = function(username, password) {
return $http.post(
API_URL + 'api_mobile_user/login', {
test: username,
test2: password
}
);
};
Whereas the corresponding API in CakePHP looks like this:
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('login'));
}
public function login() {
$this->response->header(array(
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Headers' => 'Content-Type'
)
);
$this->autoRender = false;
}
What happens next is that the preflight OPTIONS request ist done - which looks quite good to me:
Request Headers:
OPTIONS /api_mobile_user/login HTTP/1.1
Host: aeap.localhost
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://asf.localhost
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://asf.localhost/?username_input=hjk&password_input=hjgk&login_button=
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Response headers:
HTTP/1.1 200 OK
Date: Wed, 05 Nov 2014 15:29:00 GMT
Server: Apache/2.4.10 (Win32) OpenSSL/1.0.1i PHP/5.5.15
X-Powered-By: PHP/5.5.15
Set-Cookie: CAKEPHP=j6st0hnq8ear2cc6psg56d6eu3; expires=Wed, 05-Nov-2014 19:29:00 GMT; Max-Age=14400; path=/; HttpOnly
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
But when the actual POST-request is done I get an status code 403:
XMLHttpRequest cannot load http://aeap.localhost/api_mobile_user/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://asf.localhost' is therefore not allowed access. The response had HTTP status code 403.
How can I avoid this? In my opinion I already enabled CORS support for Cake ['Access-Control-Allow-Origin']. It seems to me that AngularJS posts some additional informations whioch are not checked during the preflight and then rejected by the backend.
Used versions: CakePHP 2.5.3, AngularJS: 1.3.0
Thanks to Marvin Smit I was able to determine the reason for the behavior which was not connected to CORS are the headers. I set 'Access-Control-Allow-Origin' => '*'on web-server level so I was able to get a response which pointed to the security component of CakePHP.
I basically tried to send a POST-Reuqest to an API which did not expect that data should be posted to it. Therefore the access was denied. So I had to add $this->Security->csrfCheck = false to the beforeFilter:
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('login'));
$this->Security->csrfCheck = false;
}
For what it's worth, the proper way to do this for Cakephp 3 is as follows
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('login'));
$this->eventManager()->off($this->Csrf);
}
Although, this is not recommended for AJAX requests. The following doc can help you more. CSRF And AJAX
DEMO: http://jsfiddle.net/rob_balfre/7QUUf/
How do you POST data (across domain) using $resource?
For example this curl writes to the API with no problem:
curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "Wobbly"}' http://192.168.91.20/api/food/
and the header response is:
HTTP/1.0 201 CREATED
Date: Thu, 25 Oct 2012 08:19:42 GMT
Server: WSGIServer/0.1 Python/2.6.1
Access-Control-Allow-Headers: Content-Type,*
Location: http://localhost/api/food/15/
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: text/html; charset=utf-8
I'm totally stuck on how to get angular to POST the same way. This is my $resource, it's worth noting that 'get' works just fine:
angular.module('tastypieModule', ['ngResource']).
factory('FoodOptions', function($resource, $timeout) {
var FoodOptions = $resource('http://testurl/api/:type',
{type: 'food'},
{
get: {method: 'JSONP', params: {format: 'jsonp', callback:'JSON_CALLBACK'}},
update: {method: 'POST', headers: {'Content-Type': 'application/json'}}
}
);
return FoodOptions;
})
When I call update it just fails and I see this is the console network tab:
METHOD: OPTIONS
STATUS: (canceled) Load cancelled
On the server you need to implement a Cross Origin Resource Sharing. http://www.html5rocks.com/en/tutorials/cors/ and http://omarrr.com/cors-html5-approach-to-crossdomain-policies/ both have good articles about the topic. The HEAD request is coming from the browser to your server to check for the headers that contain (or in your case, don't contain) the CORS permissions. If you implement CORS on the server then you'll see the browser first make a HEAD request once to the server then after confirming the correct permissions it will make the POST.