I'm attempting to post a JSON document from an AngularJS app to a Jersey REST service. The request fails, informing me that:
XMLHttpRequest cannot load http://localhost:8080/my.rest.service/api/order/addOrder. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
Jersey REST Post Function
I have enabled (what I believe to be) the appropriate headers: Access-Control-Allow-Origin
and Access-Control-Allow-Methods
on the response, as seen in the method below:
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/addOrder")
public Response addOrder(DBObject dbobject) {
DB db = mongo.getDB("staffing");
DBCollection col = db.getCollection("orders");
col.insert(dbobject);
ObjectId id = (ObjectId)dbobject.get("_id");
return Response.ok()
.entity(id)
.header("Access-Control-Allow-Origin","*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
Angular JS Controller
I've declared the app and configured the $httpProvider with all of the settings suggested in similar Stack Overflow questions:
var staffingApp = angular.module('myApp', ['ngRoute', 'ui.bootstrap']);
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
}]);
I've also created this controller to open a modal and handle the form:
var modalCtrl = function($scope, $modal, $log, $http, $location) {
$scope.order = {
activityTitle : null,
anticipatedAwardDate : null,
component : null,
activityGroup : null,
activityCategory : null,
activityDescription : null
};
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'addOrder.html',
windowClass: 'modal',
controller: modalInstanceCtrl,
resolve: {
order : function () {
return $scope.order;
}
}
});
modalInstance.result.then(function (oid) {
$log.info("Form Submitted, headed to page...");
$location.path("/orders/" + oid);
}, function() {
$log.info("Form Cancelled")
});
};
};
var modalInstanceCtrl = function ($scope, $modalInstance, $log, $http, order) {
$scope.order = order,
$scope.ok = function () {
$log.log('Submitting user info');
$log.log(order);
$log.log('And now in JSON....');
$log.log(JSON.stringify(order));
$http.post('http://localhost:8080/my.rest.service/api/order/addOrder', JSON.stringify(order)).success(function(data){
$log.log("here's the data:\n");
$log.log(data);
$modalInstance.close(data._id.$oid)
});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
myApp.controller('modalCtrl', modalCtrl);
To no avail, I've tried:
- removing
.allow("OPTIONS")
from the response headers. - removing the $httpProvider configuration from the application
- changed the $httpProvider configuration to call myApp.config(function ($httpProvider) {...}), passing the function itself rather than the array.
Get requests work with the same configuration:
@GET
@Path("/listall/")
@Produces(MediaType.APPLICATION_JSON)
public Response listAll(){
DB db = mongo.getDB("staffing");
DBCollection col = db.getCollection("orders");
List<DBObject> res = col.find().limit(200).toArray();
return Response.ok()
.entity(res.toString())
.header("Access-Control-Allow-Origin","*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS")
.build();
}
with this controller that works fine:
myApp.controller('orderListCtrl', function ($scope, $http){
$http.get('http://localhost:8080/my.rest.service/api/order/listall').success(function(data) {
for (var i = 0; i < data.length; i++) {
if (data[i].description.length > 200) {
data[i].shortDesc = data[i].description.substring(0,196) + "...";
} else {
data[i].shortDesc = data[i].description;
}
};
$scope.orders = data;
});
});
Update #1:
I've tried the same request on a same origin basis, essentially serving the Angular application alongside the REST service from locahost:8080. This configuration worked, but required a slight change and some general clean up in my code, which I've edited above.
The Post still fails as a CORS request, however so I'm still looking for the missing piece in this configuration.
Update #2:
I've investigated the headers of the working request as they're delivered to the browser and compared them with the non-working request.
The working get request returns the following headers with its response:
The non-working post request returns headers with its response, but is missing the Access-Control-Allow-Origin header:
I believe this has now become an issue of the headers being stripped off of the response prior to returning it to the client, which would then cause the browser to fail the request.
Update #3:
Submitting a test POST request to the same URL from Chrome's REST Console extension returns the appropriate response headers, as seen in the screencap below.
At this point, I can't determine what's removing the headers between Jersey and my Angular client, but I'm fairly confident that's the culprit.