79 votes

Comment forcer Angular2 à POST en utilisant x-www-form-urlencoded ?

J'ai un projet qui nécessite l'utilisation d'Angular2 (final) pour poster sur un vieux serveur Tomcat 7 fournissant une API de type REST à l'aide de pages .jsp.

Cela fonctionnait bien lorsque le projet n'était qu'une simple application JQuery effectuant des requêtes AJAX. Toutefois, l'ampleur du projet a pris une telle ampleur qu'il doit être réécrit à l'aide d'un framework plus moderne. Angular2 semble fantastique pour ce travail, à une exception près : Il refuse d'exécuter des requêtes POST en utilisant une autre option que des données de formulaire, que l'API n'extrait pas. L'API s'attend à ce que tout soit urlencodé, en s'appuyant sur la fonction Java request.getParameter("param") pour extraire des champs individuels.

Voici un extrait de mon fichier user.service.ts :

import { Injectable }    from '@angular/core';
import { Headers, Response, Http, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class UserService {
    private loggedIn = false;
    private loginUrl = 'http://localhost:8080/mpadmin/api/login.jsp';
    private headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded'});

    constructor(private http: Http) {}

    login(username, password) {
        return this.http.post(this.loginUrl, {'username': username, 'password':  password}, this.headers)
            .map((response: Response) => {
                let user = response.json();
                if (user) {
                    localStorage.setItem('currentUser', JSON.stringify(user));
                }
            }
        );
    }
}

Quel que soit le type de contenu de l'en-tête, il finit toujours par arriver sous forme de données de formulaire non codées. Il n'honore pas l'en-tête que j'ai défini.

Quelqu'un d'autre a-t-il rencontré ce problème ? Comment faire pour forcer Angular2 à POST des données dans un format qui peut être lu par une ancienne API Java en utilisant request.getParameter("param") ?

Editar : Pour tous ceux qui trouveront ce problème à l'avenir, la solution est en fait très simple. Définissez le corps du message comme ceci :

let body = `username=${username}&password=${password}`;`

Voir l'exemple de Brad ci-dessous.

1 votes

Je pense que c'est plutôt nul qu'angular rende si difficile le fait de poster comme www-form-urlencoded . Il devrait y avoir un soutien de première classe pour faire cela facilement.

1 votes

Cela ne semble plus fonctionner avec Angular4+ et HttpClient.

0 votes

Vous avez raison, @stt106. Cette solution est ancienne, et spécifique aux versions antérieures à la 4. Je réécrirai la solution ci-dessus pour HttpClient lorsque j'aurai le temps.

106voto

Mick Points 1717

Pour Angular > 4.3 (nouveau HTTPClient), utilisez ce qui suit :

let body = new URLSearchParams();
body.set('user', username);
body.set('password', password);

let options = {
    headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
};

this.http
    .post('//yourUrl.com/login', body.toString(), options)
    .subscribe(response => {
        //...
    });

Notez 3 choses pour que cela fonctionne comme prévu :

  1. Utilisez URLSearchParams pour votre corps
  2. Convertir le corps en chaîne de caractères
  3. Définir le type de contenu de l'en-tête

Attention : Les anciens navigateurs ont besoin d'un polyfill ! J'ai utilisé : npm i url-search-params-polyfill --save et ensuite ajouté à polyfills.ts : import 'url-search-params-polyfill';

1 votes

Les gars, faites attention, vous avez besoin d'un polyfill ! Je l'ai ajouté à la réponse.

1 votes

Ugh, angulaire. C'est fou qu'on doive écrire un code aussi verbeux pour faire une chose aussi simple.

1 votes

Honnêtement, je pense que c'est assez simple. Et si vous voulez ajouter d'autres options d'en-tête ? De plus, la génération de x-www-form-urlencoded est automatiquement effectuée par URLSearchParams. Je ne vois pas du tout de code passe-partout ici. Si vous le faites, avec le nouveau et magnifique HttpClient d'Angular vous pouvez créer un Intercepteur et ajoutez-y vos options d'en-tête. Pas de code dupliqué alors. Ugh, Angular (est en fait génial !)

104voto

Brad Points 2876

MISE À JOUR juin 2020 : Cette réponse a 4 ans et n'est plus valable en raison des changements d'API dans Angular. Veuillez vous référer aux réponses plus récentes pour l'approche de la version actuelle.


Vous pouvez le faire en utilisant URLSearchParams en tant que corps de la requête et angular définira automatiquement le type de contenu à application/x-www-form-urlencoded et coder le corps correctement.

let body = new URLSearchParams();
body.set('username', username);
body.set('password', password);

this.http.post(this.loginUrl, body).map(...);

La raison pour laquelle cela ne fonctionne pas actuellement est que vous n'encodez pas les données du corps au bon format et que vous ne définissez pas correctement les options d'en-tête.

Vous devez coder le corps comme ceci :

let body = `username=${username}&password=${password}`;

Vous devez définir les options d'en-tête comme suit :

this.http.post(this.loginUrl, body, { headers: headers }).map(...);

4 votes

J'ai essayé l'option URLSearchParams(), qui n'a pas fonctionné, mais vous aviez tout à fait raison au sujet du format du corps. Cela a fait l'affaire. Marqué comme étant la réponse, je vais mettre à jour le message pour que d'autres personnes puissent le trouver.

0 votes

J'utilise URLSearchParams comme corps pour les données du formulaire et je n'ai aucun problème. Je pense que cela a été introduit dans la RC4. Quelle version d'Angular utilisez-vous ?

0 votes

Final, mais en y repensant, je suis presque sûr que j'ai juste fait une faute de frappe, et comme la deuxième option a fonctionné, je n'ai pas pris la peine de réessayer. Merci encore pour l'aide, d'ailleurs.

38voto

Johan Kvint Points 569

Pour ceux qui cherchent encore une réponse, voici comment j'ai résolu le problème avec Angular 5 et HttpClient :

const formData = new FormData();

// append your data
formData.append('myKey1', 'some value 1');
formData.append('myKey1', 'some value 2');
formData.append('myKey3', true);

this.httpClient.post('apiPath', formData);

Faites PAS Définir l'en-tête Content-Type, angular va le faire pour vous !

0 votes

Joli ! Merci pour une solution HttpClient pour Angular 5. Je suis en train de migrer mon projet vers Angular 5 et je cherchais à simplifier le processus d'affichage.

0 votes

Je dois inclure Authorization dans l'en-tête, dois-je le définir sur la page d'accueil du site web de l'entreprise ? formData également ou le définir dans le RequestOptions ?

0 votes

Comment ajouter un objet dans un objet ? ex : option{ details : {nom : xx, âge : yy } }

30voto

Robert Hegner Points 1060

C'est ce qui a fonctionné pour moi avec Angular 7 :

const payload = new HttpParams()
  .set('username', username)
  .set('password', password);

this.http.post(url, payload);

Il n'est pas nécessaire de définir explicitement l'en-tête avec cette approche.

Notez que l'objet HttpParams est immuable. Donc, si vous faites quelque chose comme ce qui suit, cela ne fonctionnera pas, vous obtiendrez un corps vide :

const payload = new HttpParams();
payload.set('username', username);
payload.set('password', password);

this.http.post(url, payload);

0 votes

La ligne n'est-elle pas this.http.post(url, content); censé être this.http.post(url, payload) ?

0 votes

@NelsonKing vous avez raison ! Merci de l'avoir signalé. J'ai mis à jour ma réponse.

2 votes

Est-ce que je rate quelque chose ici, mais cela ne code pas les paramètres ?

4voto

khaleel Points 11

J'ai trouvé cette solution après avoir travaillé plusieurs heures sur ce problème.

login(userName: string, password: string) {
const headers = new Headers();
headers.append('Accept', 'application/json');
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append( 'No-Auth', 'True');

const body = new URLSearchParams();
body.set('username', userName);
body.set('password', password);
body.set('grant_type', 'password');

return this.http.post(
    this.baseUrl + '/token'
   , body.toString()
   , { headers: headers }
  )
  .pipe(map(res => res.json()))
  .pipe(map(res => {
    localStorage.setItem('auth_token', res.auth_token);
    return true;
  }))
  .pipe(catchError((error: any) => {
    return Observable.throw(error);
  }));

}

0 votes

M'a aidé. Merci beaucoup !

0 votes

La classe URLSearchParams n'a pas de méthode toString.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X