启用AngularJS到Jersey的CORS发布请求

时间:2014-09-16 04:09:22

标签: java angularjs rest jersey cors

我试图将AngularJS应用中的JSON文档发布到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 Post Function

我在响应中启用了(我认为相应的)相应的标题:Access-Control-Allow-OriginAccess-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<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();       
}

使用此控制器工作正常:

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:

我已经在同一个来源的基础上尝试了相同的请求,主要是从locahost的REST服务旁边提供Angular应用程序:8080。这个配置有效,但我的代码需要稍作修改和一些常规清理,我已在上面编辑过。

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

更新#2:

我已经调查了工作请求的标题,因为它们已经发送到浏览器并将它们与非工作请求进行比较。

工作get请求返回以下标头及其响应: Working GET Request Response

非工作发布请求返回带有响应的标头,但缺少Access-Control-Allow-Origin标头:

Non-working POST Request Response

我认为现在这已成为在将响应返回给客户端之前从响应中删除标题的问题,这会导致浏览器使请求失败。

更新#3:

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

此时,我无法确定删除Jersey和我的Angular客户端之间的标题,但我确信这是罪魁祸首。

4 个答案:

答案 0 :(得分:8)

问题结果是在POST请求之前使用正确的交叉原始标头在飞行前发送的OPTIONS请求的处理不充分。

我可以通过下载和实现此页面上的CORS过滤器来解决此问题:http://software.dzhuvinov.com/cors-filter-installation.html

如果您遇到类似问题,请按照说明进行测试,看看您的OPTIONS请求是否已失败,并且紧接着您的成功请求。

答案 1 :(得分:2)

最好的方法是添加Jersey Response过滤器,它将为所有方法添加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声明中添加以下

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>

答案 2 :(得分:0)

我从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错误。

您的网络服务实施看起来是正确的。所以只需要检查,尝试在同一端口上的localhost上运行它们(例如8080)。如果所有代码都正确,它应该在那里工作。

要单独运行它们,请尝试在webservice实现中添加 Access-Control-Allow-Origin:* ,如上所示。

希望这有帮助。

答案 3 :(得分:0)

实际上,您还有其他不需要过滤器的解决方案。将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/以供参考。