Включить запрос CORS от AngularJS до Джерси
Я пытаюсь опубликовать документ JSON из приложения AngularJS в службу 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.
Функция отправки REST в Джерси
Я включил (что я считаю) соответствующие заголовки: 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-контроллер
Я объявил приложение и настроил $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";
}]);
Я также создал этот контроллер, чтобы открыть modal и обработать форму:
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 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:
Я пробовал один и тот же запрос на основе того же происхождения, по существу обслуживая приложение Angular рядом с службой REST от locahost: 8080. Эта конфигурация работала, но требовала небольшого изменения и некоторой общей очистки в моем коде, которую я редактировал выше.
Сообщение по-прежнему не работает как запрос CORS, однако я все еще ищу недостающую часть в этой конфигурации.
Обновление # 2:
Я исследовал заголовки рабочего запроса по мере их доставки в браузер и сравнил их с нерабочим запросом.
Рабочий запрос получения возвращает следующие заголовки с его ответом:
![Working GET Request Response]()
Нерабочий почтовый запрос возвращает заголовки с ответом, но отсутствует заголовок Access-Control-Allow-Origin:
![Non-working POST Request Response]()
Я считаю, что теперь это стало проблемой, когда заголовки были отстранены от ответа до того, как они вернули его клиенту, что приведет к сбою запроса браузера.
Обновление № 3:
Отправка тестового запроса POST на тот же URL-адрес из консоли Chrome REST Console возвращает соответствующие заголовки ответов, как показано в скринкапе ниже.
![REST Console Screencap]()
На данный момент я не могу определить, как удалить заголовки между Джерси и моим клиентом Angular, но я уверен, что виновник.
Ответы
Ответ 1
Проблема оказалась неадекватной обработкой запроса OPTIONS, отправленного перед полетом до запроса POST с соответствующими заголовками перекрестного происхождения.
Я смог решить проблему, загрузив и внедряя фильтр CORS, найденный на этой странице: http://software.dzhuvinov.com/cors-filter-installation.html.
Если у вас возникла аналогичная проблема, следуйте инструкциям и протестируйте, чтобы убедиться, что ваш запрос OPTIONS больше не работает, и сразу же следует ваш успешный запрос.
Ответ 2
Лучший способ - добавить фильтр Джерси-ответ, который добавит заголовки CORS для всех методов. Вам не нужно менять реализацию веб-сервисов.
Я объясню для 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 в объявлении сервлета джерси добавьте ниже
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
</init-param>
Ответ 3
Я столкнулся с аналогичной ошибкой CORS при вызове моей Restful service (реализованной в java-Jersey) из angularjs. Чтобы исправить это, я добавил Access-Control-Allow-Origin: * в заголовке ответа. Я добавил ниже:
response.addHeader("Access-Control-Allow-Origin", "*");
Для получения дополнительной информации вы можете проверить - http://enable-cors.org/server.html
Ошибка CORS возникает, когда ваш код angularjs (веб-проект) и код webserivce (проект на стороне сервера) находятся на разных IP-адресах и портах.
Ваша реализация webservice выглядит правильно. Поэтому просто проверьте, попробуйте запустить их на localhost на одном и том же порту (например, 8080). Он должен работать там, если весь код верен.
Чтобы запустить их отдельно, попробуйте добавить Access-Control-Allow-Origin: * в реализацию webservice, как показано выше.
Надеюсь, что это поможет.
Ответ 4
На самом деле у вас есть другое решение, для которого не требуется фильтр. Добавление заголовков 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/ для справки.