Mocking a server response - angularjs

I'm new to e2e testing. I've encountered problem during my protractor tests. My web application calls API despite my efforts:
Started GET "/api/programs" for 127.0.0.1 at 2015-06-22 10:43:19 +0200
Processing by Api::V1::ProgramsController#index as JSON
======= NO Authorization token =======
I want to provide my web application with a correct HTTP POST response. Yet my code is not working:
Here is my code:
describe('e2e tests', function() {
it('FO tests', function() {
browser.addMockModule('WebClientApp', function() {
console.log('test');
angular.module('WebClientApp', ['ngMockE2E'])
.run(function($httpBackend) {
console.log('Test2!');
alert('Test3!');
$httpBackend.when('POST','api/auth/current_resource')
.respond(200, [{
"resource":
{"id":"e11e5e4a-034c-4545-967a-dae395d5c950","email":"admin#aa.com","name":"Xaa","surname":"Xaaaa","is_active":true,"personal_number":null,"resource_name":"User","roles":"admin"}
"token": "AdPnyXvZZDtcPkMVE9rIDFM09WmHubAnEd4wGXLPMiPWrFu0gDH1uIg7lqXXl1k2UgmJ1ektHf3Pduq2iF0nsR3A4yJ1dw8cB2FHgw3rWMf3q4357Atg9FtC7WnHisGa"
}]);
$httpBackend.whenGET(/.*/).passThrough();
});
});
browser.getRegisteredMockModules();
browser.get('http://0.0.0.0:9000/#/back-office/dashboard');
browser.pause();
});
});
And my Request:
Remote Address:0.0.0.0:3000
Request URL:http://0.0.0.0:3000/api/auth/sign_in
Request Method:OPTIONS
Status Code:200 OK
Response Headers
view source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:accept, content-type
Access-Control-Allow-Methods:GET, PUT, DELETE, POST, OPTIONS
Access-Control-Allow-Origin:http://localhost:9000
Access-Control-Max-Age:1728000
Content-Type:text/plain
Transfer-Encoding:chunked
X-Request-Id:1c75d93d-4539-4654-9963-a04bf45defe0
X-Runtime:0.029612
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:0.0.0.0:3000
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36

Here is a possible work around if the problem is CORS. (I don't believe tat $httpBackend is prepare for that use).
But can you provide the code of the service which is calling: /api/programs ?
Maybe you are missing to mock other web services responses, which are been executed by some angular factory/service etc.
Update:
#Dan Kanze answer that for cross domain request you can use expectJSONP.
Here is his example code
httpBackend.expectJSONP('http://api.stackexchange.com/2.1/users/gigablox/timeline?callback=JSON_CALLBACK')
.respond(returnData);

Related

HTTP headers with Authorize sent as OPTIONS

I am making an authentication system based on tokens. When a user logs in a token sent back and this then submitted with each call to the server
Assigning a token
.factory('AuthenticationService', function($rootScope, $http, authService, $httpBackend) {
var service = {
login: function(user) {
$http.post('http://192.168.100.100/myApp/login', { user: user }, { ignoreAuthModule: true })
.success(function (data, status, headers, config) {
$http.defaults.headers.common.Authorization = data.authorizationToken;
console.log("token:" + data.authorizationToken);
authService.loginConfirmed(data, function(config) {
config.headers.Authorization = data.authorizationToken;
return config;
});
})
After this is executed calls are sent as OPTIONS rather than POST the problem being that I am sending to RESTful server and OPTIONS isn't ahhh ummm an option. i.e server expects POST, GET etc.
Chrome shows my headers as ..
General
**Remote Address:** 192.168.100.100:80
**Request URL:** http://192.168.100.100/myapp/login
**Request Method:** OPTIONS
**Status Code:** 404 Not Found
Response Headers
**Access-Control-Allow-Origin:** *
**Cache-Control:** no-cache, must-revalidate
**Connection:** Keep-Alive
**Content-Encoding:** gzip
**Content-Length:** 563
**Content-Type:** text/plain
**Date:** Tue, 04 Aug 2015 04:29:14 GMT
**Expires:** 0
**Keep-Alive:** timeout=5, max=100
**Server:** Apache/2.2.22 (Debian)
**Vary:** Accept-Encoding
**X-Powered-By:** PHP/5.4.41-0+deb7u1
Request Headers
OPTIONS /myapp/login HTTP/1.1
**Host:** 192.168.100.100
**Connection:** keep-alive
**Access-Control-Request-Method:** POST
**Origin:** null
**User-Agent:** Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
**Access-Control-Request-Headers:** authorization, content-type
**Accept:** */*
**Accept-Encoding:** gzip, deflate, sdch
**Accept-Language:** en-US,en;q=0.8
Will it always be OPTIONS and do I have to alter my RESTful server to accomodate this, should I not be able to see the token in the headers?
This is an preflight request to check if CORS are enaabled or not
During the preflight request, you should see the following two headers: Access-Control-Request-Method and Access-Control-Request-Headers. These request headers are asking the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
Shortly. You need to enable these headers at you server for actual request to work
Can you please check if CORS is enabled? if yes please try to handle OPTIONS request like this
if (req.method === 'OPTIONS') {
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();
}

$http.post Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers error

I'm working on a app using Ionic Framework.
On the backend i wrote a Flask Application for api which looks like below:
#API.route("/saverez",methods=["POST","OPTIONS"])
#crossdomain(origin='*', headers="*",methods="*")
#render_api
def saver():
.....
I got errors while posting json to api.
var headers = {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'POST, GET, OPTIONS',
'Accept': 'application/json'
};
$http({
method: "POST",
headers: headers,
url: url+ '/api/saverez',
data: $scope.form
}).success(function (result)
console.log(result);
}).error(function (data, status, headers, config) {
console.log(data);
console.log(status);
console.log(headers);
console.log(config);
});
So this gives me the error:
XMLHttpRequest cannot load http://myurl/api/saverez. Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers.
I googled it and then i found this snippet:
http://flask.pocoo.org/snippets/56/
I also added headers to my nginx conf like below:
location ~* \.(eot|ttf|woff)$ {
add_header Access-Control-Allow-Origin *;
}
Tried everything in that documentation and also evertyhing i found on google but sadly it didn't do any good.
How can i set the right headers for all origins ? I also use google pagespeed does it can cause this issue ?
Thanks in advance.
--- EDIT ---
Chrome network output
Remote Address:myip
Request URL:http://myurl/api/saverez
Request Method:OPTIONS
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:access-control-allow-origin, accept, access-control-allow-methods, content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host:myurl
Origin:http://192.168.1.46:8100
Pragma:no-cache
Referer:http://192.168.1.46:8100/
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
Response Headersview source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:*
Access-Control-Allow-Origin:*
Access-Control-Max-Age:21600
Allow:POST, OPTIONS
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Thu, 28 Aug 2014 13:26:11 GMT
Server:nginx/1.6.0
In my app module config section I have the following:
angular.module('starterapp', ['ionic'])
.config(function ($stateProvider, $httpProvider, $urlRouterProvider) {
// We need to setup some parameters for http requests
// These three lines are all you need for CORS support
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.withCredentials = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
That is all you need to have to make all the HTTP requests work with CORS. This of course assumes you have made your backend.
You adding those additional headers would not be allowed according the w3c specification for XMLHTTPRequest as they may only be added by the host browser.
PaulT's answer got me very close to solving this problem for myself, but I also had to explicitly add the Content-Type for my post operations.
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
Hope this helps.
I got it working by just adding JSONP as the method
$resource(
'http://maps.google.com/maps/api/geocode/json?address=:address&sensor=false',
{},
{
get: {
method: 'JSONP',
}
});

Sending an Angular post request with parameter to Rails API

I´m not able to completely integrate an Angular front-end with a Rails back-end API. They both run in differente servers, so I think I have problems with CORS.
My Angular app is running a controller that is calling a service that has resource with a query (GET) and save (POST) methods. The query (GET) is working fine, however the post is not working.
I´m able to send a POST request to the server when I don´t send any parameter. Like this:
Controller:
$scope.createBusiness = function() {
console.log("Business.name=" + $scope.business.name);
$scope.business = Business.save();
};
Service:
.factory('Business',
function($resource){
var businesses =
$resource('http://127.0.0.1\\:3000/:business', {business:'businesses'}, {
query: {method:'GET', isArray: true},
save: {method:'POST', isArray: false}
});
return businesses;
}
);
However, I want to post my model parameters, so when I try to send something then I don´t send a POST request anymore, but an OPTIONS request. And I get an error.
Please, see my request data when I send a request without parameters (POST request):
Request URL:http://127.0.0.1:3000/businesses
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:es-ES,es;q=0.8
Connection:keep-alive
Content-Length:0
Content-Type:text/plain;charset=UTF-8
Host:127.0.0.1:3000
Origin:http://localhost:1234
Referer:http://localhost:1234/app/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization
Access-Control-Allow-Methods:POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Access-Control-Request-Method:*
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:9
Content-Type:application/json; charset=utf-8
Date:Mon, 04 Nov 2013 16:50:33 GMT
Etag:"ccd3d779b6f97e2c24633184cbc8f98c"
Server:WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:e084295e-c7c6-4566-80d1-6e2a8ac2e712
X-Runtime:0.034000
X-Ua-Compatible:chrome=1
X-Xss-Protection:1; mode=block
I reach the server, execute the method and get the response! This is ok.
and, see my request data when I send a request WITH parameters (OPTIONS request):
Request URL:http://127.0.0.1:3000/businesses
Request Method:OPTIONS
Status Code:404 Not Found
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:es-ES,es;q=0.8
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:127.0.0.1:3000
Origin:http://localhost:1234
Referer:http://localhost:1234/app/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Connection:Keep-Alive
Content-Length:131852
Content-Type:text/html; charset=utf-8
Date:Mon, 04 Nov 2013 16:54:04 GMT
Server:WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
X-Request-Id:25705159-fbfb-4830-a0f1-6610fa09b70e
X-Runtime:0.371000
UPDATE
I forgot to add my controller when adding a model parameter:
$scope.createBusiness = function() {
console.log("Business.name=" + $scope.business.name);
$scope.business = Business.save($scope.business);
};
I have several views, with several forms, so, I don´t want only to post a form, but the business object model that I have in scope (and I filled with the data of all the forms).
UPDATE
This is my Rails Application_Controller (CORS configuration):
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
# OJOJOOJO: Rober: I have commented this line which is provided by default with rails and added all code below in order to
# add CSRF protection
#protect_from_forgery with: :exception
protect_from_forgery
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers, :set_csrf_cookie_for_ng
# For all responses in this controller, return the CORS access control headers.
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
headers['Access-Control-Max-Age'] = "1728000"
end
# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.
def cors_preflight_check
if request.method == :options
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
def set_csrf_cookie_for_ng
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
end
protected
def verified_request?
super || form_authenticity_token == request.headers['X_XSRF_TOKEN']
end
end
I finally made it work.
I will explain all my config in Angular and Rails 4 as I would have liked to read it.
Angular
app.js:
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers', 'myApp.i18n', 'demo']).
config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common["X-Requested-With"];
}]);
Note: Of course, you don´t need to include all my modules.
In my view:
<form novalidate="" name="createBusinessForm" ng-submit="setBusinessInformation()" class="css-form">
<label>* {{'BUSINESS_NAME' | translate}}</label>
<input type="text" name="name" ng-model="business.name" class="input-xxlarge input-height-large" placeholder="{{'BUSINESS_NAME_PLACEHOLDER' | translate}}" required maxlength="80">
<span ng-show="createBusinessForm.name.$dirty && createBusinessForm.name.$error.required" class="text-error">Mandatory field.</span>
<label>* {{'ID' | translate}}</label>
<input type="text" ng-model="business.cif_nif" class="input-xlarge input-height-large" placeholder="{{'BUSINESS_ID_PLACEHOLDER' | translate}}" required maxlength="60">
<label>* {{'ADDRESS' | translate}}</label>
Note: You can define as many fields as you need and with data bingind assign the values to an object.
in my controller:
$scope.createBusiness = function() {
$scope.business.type = $scope.type;
$scope.business.plan = $scope.plan;
$scope.business = Business.save($scope.business);
$location.path('/user-dashboard');
};
Note: All the attributes you need to send in the post request. Apart from the form, you can assign some new attributes to the object before sending to Rails API. We will use a service with a resource object to send the POST request.
In my service:
.factory('Business',
function($resource){
var businesses =
$resource('http://127.0.0.1\\:3000/:business', {business:'businesses'}, {
query: {method:'GET', isArray: true},
save: {method:'POST', isArray: false}
});
return businesses;
}
);
Note: I have a GET request to get the business from DB through Rails API and the POST one.
Rails 4
IMPORTANT
routes.rb
match "/businesses" => "application#index", via: :options
Note: New entry to match the OPTIONS request the Angular server will send to pre-negociate the start of sending the POST request.
application_controller.rb
class ApplicationController < ActionController::Base
before_filter :set_headers
def index
puts "Do nothing."
render nothing: true
end
def set_headers
puts 'ApplicationController.set_headers'
if request.headers["HTTP_ORIGIN"]
# better way check origin
# if request.headers["HTTP_ORIGIN"] && /^https?:\/\/(.*)\.some\.site\.com$/i.match(request.headers["HTTP_ORIGIN"])
headers['Access-Control-Allow-Origin'] = request.headers["HTTP_ORIGIN"]
headers['Access-Control-Expose-Headers'] = 'ETag'
headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD'
headers['Access-Control-Allow-Headers'] = '*,x-requested-with,Content-Type,If-Modified-Since,If-None-Match,Auth-User-Token'
headers['Access-Control-Max-Age'] = '86400'
headers['Access-Control-Allow-Credentials'] = 'true'
end
end
end
My Rails controller
def create
puts 'Businesses_controller.create!!!!!'
puts business_params.inspect
# business_type object is recovered from db
businessTypeName = params[:type]
businessType = BusinessType.where(:name => businessTypeName).first
...
end
Note: Here do whatever you need...
We had the same problem and took a similar but hopefully simpler/quicker/more flexible approach.
The quick rundown of what I did was use to the ruby library "rack-cors" (https://github.com/cyu/rack-cors) to manage all the CORS headers.
This means I didn't have to stick a bunch of hardcoded header name/values in my code.
The big benefit for me was that this takes care of both simple CORS requests (GET request and response) and preflighted requests that use OPTION requests (OPTIONS request and response and then POST request and response).
Here are the quick steps that I followed:
gem install rack-cors
add the following to Gemfile:
gem 'rack-cors', :require => 'rack/cors'
run "bundle install", which will update Gemfile.lock
edit config/application.rb to add the following block:
config.middleware.insert_before "ActionDispatch::Static", "Rack::Cors", :debug => true, :logger => Rails.logger do
allow do
origins '*'
resource '*',
:headers => :any,
:methods => [:get, :post, :delete, :put, :options],
:max_age => 0
end
end
Now in my case, I just wanted to open this up to any host, but you could be more restrictive. You can also limit headers and http methods too.
See more details on readme at the github page: https://github.com/cyu/rack-cors
(The rack-cors author has example rails apps under examples/rails3 and examples/rails4)

Access-Control-Allow-Origin with Laravel and Omnipay

I'm trying to make an Ajax post request from Backbone, calling the Laravel route "pay" but I get always this answer from the console log:
XMLHttpRequest cannot load https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=EC-5S932878HU8059629. Origin http://localhost is not allowed by Access-Control-Allow-Origin.
I tried to modify my MAMP httpd.conf to accept cross domain script, and as you can see I've added an header instruction to the routes.php.
Here is my JS code:
Backbone.ajax({
url:'index.php/pay',
type:'POST',
dataType:"json",
data: converteditems,
crossDomain: true,
success:function (data) {
if(data.error) { // If there is an error, show the error messages
$('.alert-error').text(data.error.text).show();
}
}
});
This is the routes.php in Laravel:
<?php
header('Access-Control-Allow-Origin: *');
Route::get('/', function()
{
return View::make('home');
});
Route::resource('products','ProductsController');
Route::resource('login', 'AuthenticationController');
Route::post('pay','PaypalController#doPay');
The doPay method use the Omnipay package in this way (for testing purpose ofc):
public function doPay()
{
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('blablablabla-facilitator_api1.gmail.com');
$gateway->setPassword('137787773');
$gateway->setSignature('AhFvPK5rU.kfQOKIwZcYO1yItmtHASGDFDFGDbY9.w');
$gateway->setTestMode('true');
$args['amount']='2.00';
$args['description']='Your purchase';
$args['returnUrl']='http://localhost/shoppingcart/index.php/return';
$args['cancelUrl']='http://localhost/shoppingcart/index.php/cancel';
try {
$response = $gateway->purchase($args)->send();
if ($response->isSuccessful()) {
$responsereturn=$response->getData();
} elseif ($response->isRedirect()) {
$response->redirect();
} else {
exit($response->getMessage());
}
} catch (\Exception $e) {
exit('internal error, log exception and display a generic message to the customer');
}
}
Some more info from the headers of my console:
Request URL:http://localhost/shoppingcart/public/index.php/pay
Request Headersview source
Accept:application/json, text/javascript, */*; q=0.01
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Origin:http://localhost
Referer:http://localhost/shoppingcart/public/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36
Request URL:https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&useraction=commit&token=EC-6J290181UP558705C
Request Headersview source
Origin:http://localhost
Referer:http://localhost/shoppingcart/public/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36
Query String Parametersview sourceview URL encoded
cmd:_express-checkout
useraction:commit
token:EC-6J290181UP558705C
If you want to make your request from Ajax, then you don't want omnipay to immediately redirect that request to paypal. You want to redirect the customer'a browser to paypal.
So you need to put some logic in your controller action, so if it is an XHR request, then just return the URL to forward the customer to.
E.g. Use $response->getRedirectUrl() on the omnipay response instead of $response->redirect(). Then put that in a JSON response or something and use JavaScript to send the customers browser to that URL.

Why does a cross-domain angularjs $http.post request fail when a XMLHttpRequest succeeds?

I am trying to exercise the Trello API with an application key and token from an angular (version 1.0.5) webapp. The server seems correctly configured to handle CORS. A test request with http://test-cors.org from enable cors works as expected.
When I do a post request in one of my angular controllers:
$http.post(url).success(function(data) {
$scope.server_resp = data;
});
I get a Request header field Content-Type is not allowed by Access-Control-Allow-Headers error. (Even though, as you see below, the Access-Control-Allow-Origin is set to '*'). Why is this header added and can it be removed?
XMLHttpRequest
When I make the same request using raw XMLHttpRequest, it succeeds. Here are the headers for the XMLHttpRequest:
Request Method:POST
Status Code:200 OK
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:0
Host:api.trello.com
Origin:http://192.168.0.125:9000
Referer:http://192.168.0.125:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
Response
Access-Control-Allow-Methods:GET, PUT, POST, DELETE
Access-Control-Allow-Origin:*
Cache-Control:max-age=0, must-revalidate, no-cache, no-store
Content-Length:563
Content-Type:application/json
Date:Mon, 18 Mar 2013 02:49:37 GMT
Expires:Thu, 01 Jan 1970 00:00:00
X-Powered-By:Express
X-Server-Time:1363574977568
Angular $http.post
Here are the headers for the angular initiated request. Note that the browser made a pre-flight OPTIONS request:
Request Method:OPTIONS
Status Code:200 OK
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, origin, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:api.trello.com
Origin:http://192.168.0.125:9000
Referer:http://192.168.0.125:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
Response
Access-Control-Allow-Methods:GET, PUT, POST, DELETE
Access-Control-Allow-Origin:*
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Mon, 18 Mar 2013 02:51:00 GMT
X-Powered-By:Express
Is there a way to configure angular's request headers to allow the above $http.post() code to work?
The 'content-type' header is not accepted by the server and is added by default for Angular $http POST request (see $http doc). You can try to remove it from your $http config. Inject $httpProvider in your controller, then this might work:
delete $httpProvider.defaults.headers.post['Content-type']
You might have to try with 'content-type' also, I'm not sure of the case to use.
Add the headers param to the $http and you'll be fine.
var config = {
method: 'POST',
url: 'your url',
headers: {
'Content-Type': undefined
},
data: {
"channel": "#fun-and-game",
"username": $scope.question.title,
"text": $scope.question.text,
"icon_emoji": ":ghost:"
},
};
$http(config).success(function(data) {
$scope.server_resp = data;
}).error(function(response) {
});
for more info, check angularjs $http docs
As per this angular pull request, CORS can be made to work by deleting X-Requested-With which causes a pre-flight OPTIONS request:
App.config(['$httpProvider', function($httpProvider) {
delete $httpProvider.defaults.headers.common["X-Requested-With"];
}
Note that I have not tried this personally, but a co-worker had to deltete the header to make his CORS request work.
I just ran into a similar issue and the problem was that I was getting the url wrong. I was posting to 1/cards/actions/createCard because I missread the docs. I got an access-control related error even though headers etc. look right. Posting to 1/cards created a card, which is what I wanted.
This worked for me
$http({
method : "POST",
url : url,
data : $.param({key: 'value', key2 : 'value'}),
headers : { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
})
To avoid this issue, create a function in the server side to catch the 'OPTIONS' and just return true. Some thing as follows.
/**
* #url OPTIONS /
*/
public function options()
{
return;
}

Resources