Java 启用从 AngularJS 到 Jersey 的 CORS Post 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25860490/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:20:46  来源:igfitidea点击:

Enable CORS Post Request from AngularJS to Jersey

javaangularjsrestjerseycors

提问by Paul Cauchon

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

我正在尝试将 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.

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

Jersey REST 发布功能

I have enabled (what I believe to be) the appropriate headers: Access-Control-Allow-Originand Access-Control-Allow-Methodson the response, as seen in the method below:

我已启用(我认为是)适当的标头: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 Controller

Angular JS 控制器

I've declared the app and configured the $httpProvider with all of the settings suggested in similar Stack Overflow questions:

我已经声明了该应用程序并使用类似 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";
 }]);

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.
  • .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;
    });
});

Update #1:

更新 #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.

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

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

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

Update #2:

更新#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: Working GET Request Response

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

The non-working post request returns headers with its response, but is missing the Access-Control-Allow-Origin header:

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

Non-working POST Request Response

非工作 POST 请求响应

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:

更新 #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. REST Console Screencap

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

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.

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

采纳答案by Paul Cauchon

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.

事实证明,问题是在 POST 请求之前发送的 OPTIONS 请求处理不当,使用正确的跨源头。

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.

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

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.

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

回答by Sandeep Kulkarni

I had faced similar CORS error while calling my Restful service (implemented in java - Jersey) from angularjs. To fix it I added Access-Control-Allow-Origin: *in response header. I added below :

我在从 angularjs 调用我的 Restful 服务(在 java - Jersey 中实现)时遇到了类似的 CORS 错误。为了修复它,我在响应头中添加了Access-Control-Allow-Origin: *。我在下面添加:

response.addHeader("Access-Control-Allow-Origin", "*"); 

For more information you can check - http://enable-cors.org/server.html

有关更多信息,您可以查看 - http://enable-cors.org/server.html

CORS error occurs typically when your angularjs code (web project) and webserivce code (server side project) are on different IP and port no.

CORS 错误通常发生在您的 angularjs 代码(网络项目)和网络服务代码(服务器端项目)位于不同的 IP 和端口号上时。

Your webservice implementation looks correct. So just to check, try running them on localhost on same port (eg. 8080). It should work there if all code is correct.

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

In order to run them separately try adding Access-Control-Allow-Origin: *in webservice implementation as shown above.

为了分别运行它们,请尝试在 webservice 实现中添加Access-Control-Allow-Origin: *如上所示。

Hope this helps.

希望这可以帮助。

回答by nondescript

Best way is to add Jersey Response filter which will add the CORS headers for all the methods. You don't have to change your webservices implementation.

最好的方法是添加 Jersey Response 过滤器,它将为所有方法添加 CORS 标头。您不必更改您的网络服务实现。

I will explain for Jersey 2.x

我会解释 Jersey 2.x

1) First add a ResponseFilter as shown below

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) then in the web.xml , in the jersey servlet declaration add the below

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>

回答by y.luis

Actually, you have other solution that does not need a filter. Adding the Access-Control-Allow-*headers to the GETrequest, is not enough, you have to create an OPTIONSendpoint to allow browsers do the pre-flight request, i.e.:

实际上,您还有其他不需要过滤器的解决方案。将Access-Control-Allow-*标头添加到GET请求中是不够的,您必须创建一个OPTIONS端点以允许浏览器执行飞行前请求,即:

@OPTIONS
public Response corsMyResource(@HeaderParam("Access-Control-Request-Headers") String requestH) {
    ResponseBuilder rb = Response.ok();

    return buildResponse(rb, requestH);
}

see https://kdecherf.com/blog/2011/06/19/java-jersey-a-cors-compliant-rest-api/for reference.

请参阅https://kdecherf.com/blog/2011/06/19/java-jersey-a-cors-compatible-rest-api/以供参考。