web-dev-qa-db-fra.com

$ http response Set-Cookie non accessible

J'écris actuellement une interface angularjs sur mon backend, et je rencontre un problème un peu commun:

Le serveur envoie un cookie dans une réponse, mais est complètement ignoré par angular.js (impossible d'imprimer la valeur 'Set-Cookie').

J'ai essayé de lire

Set-Cookie dans l'en-tête HTTP est ignoré avec AngularJS

Angularjs $ http ne semble pas comprendre "Set-Cookie" dans la réponse

mais malheureusement, j'ai essayé toutes les solutions et ça ne semble pas fonctionner.

Demande envoyée

(as captured by Chrome Developer Tools)

Réponse reçue

(as captured by Chrome Developer Tools)

J'utilise angular.js (v1.2.10), et c'est ce que j'ai utilisé pour faire la demande

$http.post('/services/api/emailLogin',
           sanitizeCredentials(credentials), 
           {
              withCredentials: true
           }).success(function(data, status, header) {
                console.log(header('Server'));
                console.log(header('Set-Cookie'));
                console.log(header('Access-Control-Allow-Headers'));
                console.log(header('Access-Control-Allow-Methods'));
                console.log(header);
            }).then(
                function(response) {
                    console.log(response);
                    return response.data;
                });

withCredentials=true est défini côté client avant de faire la demande. 

Access-Control-Allow-Credentials=true est défini côté serveur avant de renvoyer la réponse.

Vous pouvez clairement constater que Set-Cookie se trouve dans les en-têtes de réponse des outils de développement Chrome, mais l'impression est simplement

(code printout)

Seul Set-Cookie dans l'en-tête de la réponse n'est pas en cours d'impression. Je me demande pourquoi cela se produit-il? Est-ce qu'il y a un moyen pour moi de m'assurer que withCredentials=true est bien défini (je ne l'ai pas vu dans l'en-tête de la requête)?

Toute aide est appréciée!

24
Luke

J'ai regardé à l'intérieur du code source $httpBackend:

xhr.onreadystatechange = function() {

  // some code

  if (xhr && xhr.readyState == 4) {
    var responseHeaders = null,
        response = null;

    if(status !== ABORTED) {
      responseHeaders = xhr.getAllResponseHeaders();

      // some code

Il utilise XMLHttpRequest#getAllResponseHeaders pour obtenir les en-têtes de réponse.

Après quelques recherches, j'ai trouvé cette question: xmlHttp.getResponseHeader + Ne fonctionne pas pour CORS

Ce qui m'a fait comprendre que XHR par sa spécification, ne supporte pas SET-COOKIE, donc cela n'a rien à voir avec angular.js en particulier.

http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders%28%29-method

4.7.4 La méthode getAllResponseHeaders ()

Renvoie tous les en-têtes de la réponse, à l'exception de ceux dont le nom de champ est Set-Cookie ou Set-Cookie2.


Au lieu de cela, utilisez simplement $cookies :

C'est un module différent, vous devez donc l'ajouter à vos scripts

 <script src="angular.js"></script>
 <script src="angular-cookies.js"></script>

Et ajoutez-le aux dépendances du module: 

 var app = angular.module('app',['ngCookies']);

Ensuite, utilisez-le comme ceci:

app.controller('ctrl',  function($scope, $http , $cookies, $timeout){

  $scope.request = function(){

    $http.get('/api').then(function(response){
      $timeout(function(){
        console.log($cookies.session)
      });         
    })
  }
})

J'utilise $timeout car $cookies ne se synchronise avec les cookies du navigateur qu'après un résumé.

29
Ilan Frumer

Devez-vous réellement lire le cookie dans Angular, ou est-ce suffisant si il est configuré et renvoyé lors de demandes ultérieures par le navigateur? Bien que je ne sois pas parvenu à faire fonctionner le premier, j’ai réussi à le faire fonctionner assez rapidement avec une configuration très similaire. (En particulier, je n'ai pas pu utiliser $cookies comme l'a recommandé @Ilan. Il n'a rien retourné.)

Tout d’abord, du côté Angular, configurez le $httpProvider pour envoyer withCredentials par défaut:

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.withCredentials = true;
}]);

C'est tout ce que vous devez faire dans Angular. Notez qu'à ce stade, si vous essayez de lire $cookies, il ne sera toujours pas en mesure de voir le cookie, mais il est enregistré et sera transmis lors de futurs appels AJAX.

Ensuite, sur le serveur, vous devez fournir un domaine spécifique (ou un ensemble de domaines) autorisé par CORS, ainsi que l'en-tête Access-Control-Allow-Credentials. Mon backend est Flask en Python avec Flask-Restful et il ressemble à ceci:

import Flask
from flask_restful import Api
app = Flask(__name__)
api = Api(app)
api.decorators = [cors.crossdomain(Origin='http://localhost:8100', credentials=True)]

C'est tout ce qu'il a fallu. Maintenant, Angular (ou plutôt le navigateur) définit le cookie et le renvoie avec les futures demandes de manière totalement transparente!

(La même remarque est faite ici: https://stackoverflow.com/a/19384207/2397068 .)

4
Lane Rettig

Vous devez modifier votre fichier .htaccess pour permettre la lecture des en-têtes.

Header set Access-Control-Allow-Headers Content-Type
0
DDT

Dans Angular 1.3.14, cela ne fonctionne plus.

J'ai eu le même problème. J'utilise $ resource et déclare withCredentials: true.

 var fooRes = $resource('/address/exemple',
    {},
    {
        bar: {
            method: 'POST',
            withCredentials: true,
        }
    }
 );

 fooRes.bar({
     "stuff" : "lorem"
 }).$promise.then(function(){
     //callback
 })

ou vous pouvez définir l'en-tête "Access-Control-Allow-Credentials", "true".

Maintenant le cookie sera sauvegardé et vous pourrez utiliser $ cookie.

J'espère que ça va aider.

0
hugodutra