启用从 AngularJS 到 Jersey 的 CORS 发布请求

2022-01-15 00:00:00 cors rest angularjs java jersey

我正在尝试将 JSON 文档从 AngularJS 应用程序发布到 Jersey REST 服务.请求失败,通知我:

I'm attempting to post a JSON document from an AngularJS app to a Jersey REST service. The request fails, informing me that:

XMLHttpRequest 无法加载 http://localhost:8080/my.rest.service/api/order/addOrder.请求的资源上不存在Access-Control-Allow-Origin"标头.因此不允许访问源http://localhost".

我已启用(我认为是)相应的标头:响应中的 Access-Control-Allow-OriginAccess-Control-Allow-Methods,如下方法所示:

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 控制器

我已经声明了应用程序并使用类似 Stack Overflow 问题中建议的所有设置配置了 $httpProvider:

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:
");
            $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 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;
    });
});

更新 #1:

我已经在同一个来源的基础上尝试了相同的请求,本质上是为 Angular 应用程序提供服务以及来自 locahost:8080 的 REST 服务.此配置有效,但需要在我上面编辑的代码中进行轻微更改和一些常规清理.

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.

作为 CORS 请求,Post 仍然失败,但是我仍在寻找此配置中缺少的部分.

The Post still fails as a CORS request, however so I'm still looking for the missing piece in this configuration.

我调查了工作请求的标头,因为它们被传递到浏览器,并将它们与非工作请求进行了比较.

I've investigated the headers of the working request as they're delivered to the browser and compared them with the non-working request.

有效的 get 请求会返回以下标头及其响应:

The working get request returns the following headers with its response:

不工作的 post 请求返回标头及其响应,但缺少 Access-Control-Allow-Origin 标头:

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.

从 Chrome 的 REST 控制台扩展向同一 URL 提交测试 POST 请求会返回相应的响应标头,如下面的屏幕截图所示.

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.

此时,我无法确定是什么删除了 Jersey 和我的 Angular 客户端之间的标头,但我相当有信心这是罪魁祸首.

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.

推荐答案

问题原来是在使用正确的跨域标头的 POST 请求之前在飞行前发送的 OPTIONS 请求处理不充分.

The problem turned out to be inadequate handling of the OPTIONS request sent in pre-flight prior to the POST request with the proper cross origin headers.

我能够通过下载并实施此页面上的 CORS 过滤器来解决此问题:http://software.dzhuvinov.com/cors-filter-installation.html.

I was able to resolve the issue by downloading and implementing the CORS filter found at this page: http://software.dzhuvinov.com/cors-filter-installation.html.

如果您遇到类似问题,请按照说明进行测试,以查看您的 OPTIONS 请求不再失败,并且紧随其后的是您的成功请求.

If you're experiencing a similar problem, follow the instructions and test to see that your OPTIONS request is no longer failing, and is immediately followed by your successful request.

相关文章