web-dev-qa-db-fra.com

Activer la demande de publication CORS de AngularJS à Jersey

J'essaie de publier un document JSON d'une application AngularJS sur un service Jersey REST. La demande échoue, m'informant que:

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 Fonction post

J'ai activé (ce que je crois être) les en-têtes appropriés: Access-Control-Allow-Origin et Access-Control-Allow-Methods sur la réponse, comme indiqué dans la méthode ci-dessous:

@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();
}

Contrôleur JS Angulaire

J'ai déclaré l'application et configuré $ httpProvider avec tous les paramètres suggérés dans des questions similaires de Stack Overflow:

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

J'ai également créé ce contrôleur pour ouvrir un modal et gérer le formulaire:

    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);

En vain, j'ai essayé: 

  • supprimer .allow("OPTIONS") des en-têtes de réponse.
  • supprimer la configuration $ httpProvider de l'application
  • changé la configuration de $ httpProvider pour appeler myApp.config (fonction ($ httpProvider) {...}), en passant la fonction elle-même plutôt que le tableau.

Les requêtes Get fonctionnent avec la même 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();       
}

avec ce contrôleur qui fonctionne bien:

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

Mise à jour n ° 1:

J'ai essayé la même requête sur la même base d'origine, desservant essentiellement l'application Angular ainsi que le service REST de locahost: 8080. Cette configuration fonctionnait bien, mais nécessitait une légère modification et un nettoyage général de mon code, que j'ai précédemment modifié.

La poste échoue toujours en tant que demande CORS, mais je recherche toujours la pièce manquante dans cette configuration.

Mise à jour # 2:

J'ai examiné les en-têtes de la demande de travail lorsqu'ils ont été livrés au navigateur et les ai comparés à la demande qui ne fonctionnait pas.

La requête get de travail renvoie les en-têtes suivants avec leur réponse: Working GET Request Response

La demande de publication qui ne fonctionne pas renvoie les en-têtes avec sa réponse, mais l'en-tête de contrôle d'accès-absent est manquant:

Non-working POST Request Response

Je pense que le problème est maintenant que les en-têtes sont supprimés de la réponse avant de la renvoyer au client, ce qui entraînerait alors l'échec de la requête par le navigateur.

Mise à jour # 3:

Envoi d'un test POST demande à la même URL de Chrome REST extension de la console renvoie les en-têtes de réponse appropriés, comme indiqué dans la capture d'écran ci-dessous ..__REST Console Screencap

À ce stade, je ne peux pas déterminer ce qui supprime les en-têtes entre Jersey et mon Angular - client, mais je suis assez convaincu que c'est le coupable.

11
Paul Cauchon

Le problème s'est avéré être un traitement inadéquat de la demande OPTIONS envoyée avant le vol avant la demande POST avec les en-têtes d'origine croisée appropriés. 

J'ai pu résoudre le problème en téléchargeant et en mettant en œuvre le filtre CORS disponible sur cette page: http://software.dzhuvinov.com/cors-filter-installation.html

Si vous rencontrez un problème similaire, suivez les instructions et testez pour vérifier que votre demande OPTIONS n'échoue plus et qu'elle est immédiatement suivie de votre demande aboutie.

8
Paul Cauchon

Le meilleur moyen consiste à ajouter un filtre Jersey Response qui ajoutera les en-têtes CORS pour toutes les méthodes. Vous n'êtes pas obligé de modifier la mise en œuvre de vos services Web.

Je vais expliquer pour Jersey 2.x 

1) Ajoutez d’abord un ResponseFilter comme indiqué ci-dessous 

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) puis dans le fichier web.xml, dans la déclaration de servlet du maillot, ajoutez ce qui suit 

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

J'avais rencontré une erreur CORS similaire lors de l'appel de mon service Restful (implémenté dans Java - Jersey) depuis angularjs. Pour résoudre ce problème, j'ai ajouté Access-Control-Allow-Origin: * dans l'en-tête de la réponse. J'ai ajouté ci-dessous:

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

Pour plus d'informations, vous pouvez consulter - http://enable-cors.org/server.html

Une erreur CORS se produit généralement lorsque votre code angularjs (projet Web) et votre code webserivce (projet côté serveur) se trouvent sur une adresse IP et un numéro de port différents. 

L'implémentation de votre service Web semble correcte. Donc, juste pour vérifier, essayez de les exécuter sur localhost sur le même port (par exemple 8080). Cela devrait fonctionner si tout le code est correct. 

Afin de les exécuter séparément, essayez d'ajouter Access-Control-Allow-Origin: * dans la mise en œuvre du service Web, comme indiqué ci-dessus. 

J'espère que cela t'aides.

0
Sandeep Kulkarni

En fait, vous avez une autre solution qui n’a pas besoin de filtre. Ajouter les en-têtes Access-Control-Allow-* à la demande GET ne suffit pas, vous devez créer un noeud final OPTIONS pour permettre aux navigateurs de faire la demande de pré-vol, à savoir:

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

    return buildResponse(rb, requestH);
}

voir https://kdecherf.com/blog/2011/06/19/Java-jersey-a-cors-compliant-rest-api/ pour référence.

0
y.luis