启用AngularJS到Jersey的CORS发布请求
我正在尝试将一个JSON文档从AngularJS应用程序发布到Jersey REST服务。 请求失败,通知我:
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 Postfunction
我启用了(我相信的)适当的标题:响应的Access-Control-Allow-Origin
和Access-Control-Allow-Methods
,如下面的方法所示:
@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控制器
我已经声明了应用程序,并使用类似Stack Overflow问题中建议的所有设置配置了$ httpProvider:
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"; }]);
我还创建了这个控制器来打开一个模态并处理表单:
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);
无济于事,我试过了:
- 从响应标头中删除
.allow("OPTIONS")
。 - 从应用程序中删除$ httpProvider配置
- 更改了$ httpProvider配置以调用myApp.config(function($ httpProvider){…}),传递函数本身而不是数组。
获取请求使用相同的配置:
@GET @Path("/listall/") @Produces(MediaType.APPLICATION_JSON) public Response listAll(){ DB db = mongo.getDB("staffing"); DBCollection col = db.getCollection("orders"); List 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(); }
使用此控制器工作正常:
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 200) { data[i].shortDesc = data[i].description.substring(0,196) + "..."; } else { data[i].shortDesc = data[i].description; } }; $scope.orders = data; }); });
更新#1:
我在同一个来源的基础上尝试过相同的请求,基本上是从locahost的服务中提供Angular应用程序:8080。 这个配置有效,但我的代码需要稍微更改和一些常规清理,我在上面编辑过。
邮政仍然作为CORS请求失败,但是我仍然在寻找这个配置中缺失的部分。
更新#2:
我已经调查了工作请求的标题,因为它们被传递到浏览器并将它们与非工作请求进行比较。
工作get请求返回以下标头及其响应:
非工作发布请求返回带有响应的标头,但缺少Access-Control-Allow-Origin标头:
我相信现在这已成为在将响应返回到客户端之前从响应中删除标题的问题,这会导致浏览器使请求失败。
更新#3:
从Chrome的REST控制台扩展中向同一URL提交测试POST请求会返回相应的响应标头,如下面的屏幕截图所示。
在这一点上,我无法确定删除Jersey和我的Angular客户端之间的标题是什么,但我相信这是罪魁祸首。
问题结果是在POST请求之前使用正确的交叉原始标头在飞行前发送的OPTIONS请求的处理不充分。
我可以通过下载和实现此页面上的CORSfilter来解决此问题: http : //software.dzhuvinov.com/cors-filter-installation.html 。
如果您遇到类似问题,请按照说明进行测试,以确定您的OPTIONS请求不再失败,并且紧接着您的成功请求。
最好的方法是添加Jersey Responsefilter,它将为所有方法添加CORS头。 您不必更改Web服务实现。
我将为Jersey 2.x解释
1)首先添加一个ResponseFilter,如下所示
import java.io.IOException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; public class CorsResponseFilter implements ContainerResponseFilter { @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { responseContext.getHeaders().add("Access-Control-Allow-Origin","*"); responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); } }
2)然后在web.xml中,在jersey servlet声明中添加如下
jersey.config.server.provider.classnames YOUR PACKAGE.CorsResponseFilter
从angularjs调用我的Restful服务(在java – Jersey中实现)时,我遇到了类似的CORS错误。 为了解决这个问题,我在响应头中添加了Access-Control-Allow-Origin:* 。 我在下面添加:
response.addHeader("Access-Control-Allow-Origin", "*");
有关更多信息,请查看 – http://enable-cors.org/server.html
当您的angularjs代码(Web项目)和webserivce代码(服务器端项目)位于不同的IP和端口号时,通常会发生CORS错误。
您的Web服务实现看起来是正确的。 所以只需要检查,尝试在同一端口上的localhost上运行它们(例如8080)。 如果所有代码都正确,它应该在那里工作。
为了单独运行它们,请尝试在webservice实现中添加Access-Control-Allow-Origin:* ,如上所示。
希望这可以帮助。
实际上,您有其他不需要filter的解决方案。 将GET
Access-Control-Allow-*
标头添加到GET
请求是不够的,您必须创建一个OPTIONS
端点以允许浏览器执行飞行前请求,即:
@OPTIONS public Response corsMyResource(@HeaderParam("Access-Control-Request-Headers") String requestH) { ResponseBuilder rb = Response.ok(); return buildResponse(rb, requestH); }
请参阅https://kdecherf.com/blog/2011/06/19/java-jersey-a-cors-compliant-rest-api/以供参考。