如何使用 x-www-form-urlencode 强制 Angular2进行 POST

我有一个项目,需要使用 Angular2(final)发布到一个旧的,遗留的 Tomcat 7服务器,提供一个有点 REST-ish API 使用。Jsp 页面。

当项目只是一个执行 AJAX 请求的简单 JQuery 应用程序时,这种方法工作得很好。然而,该项目的范围已经扩大到需要使用更现代的框架重写。Angular2看起来非常适合这项工作,但有一个例外: 它拒绝使用任何选项执行 POST 请求,只使用 API 不提取的表单数据。API 期望所有内容都进行 urlencode 编码,依靠 Java 的 request.getParameter("param")语法提取单个字段。

这是从我的 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));
}
}
);
}
}

无论我将头内容类型设置为什么,它最终总是以未编码的表单数据的形式到达。它不尊重我设置的头部。

还有其他人遇到过这种情况吗?如何强制 Angular2以一种旧的 JavaAPI 可以使用 request.getParameter("param")读取的格式 POST 数据?

106427 次浏览

UPDATE June 2020: This answer is 4 years old and no longer valid due to API changes in Angular. Please refer to more recent answers for the current version approach.


You can do this using URLSearchParams as the body of the request and angular will automatically set the content type to application/x-www-form-urlencoded and encode the body properly.

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


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

The reason it's not currently working for you is you're not encoding the body data in the correct format and you're not setting the header options correctly.

You need to encode the body like this:

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

You need to set the header options like this:

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

For Angular > 4.3 (New HTTPClient) use the following:

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 => {
//...
});

Note 3 things to make it work as expected:

  1. Use URLSearchParams for your body
  2. Convert body to string
  3. Set the header's content-type

Attention: Older browsers do need a polyfill! I used: npm i url-search-params-polyfill --save and then added to polyfills.ts: import 'url-search-params-polyfill';

For those still looking for an answer this is how I solved it with Angular 5 and 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);

Do NOT set Content-Type header, angular will fix this for you!

This is what worked for me with Angular 7:

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


this.http.post(url, payload);

No need to explicitly set the header with this approach.

Note that the HttpParams object is immutable. So doing something like the following won't work, it will give you an empty body:

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


this.http.post(url, payload);

I found out this solution after working several hours on this issue

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

}

Guys I've been working on this since a while and thanks to this post from Josh Morony https://www.joshmorony.com/integrating-an-ionic-application-with-a-nodejs-backend/ I figured out what the problem was. Basically, when I started testing my api I was using POSTMAN and it was working perfectly but when it came to implementing it with Ionic Angular it became a problem. The solution in this post is only about importing body-parser and use it as app middleware like this app.use(bodyParser.json()) on your server-side root file(index).

Hopefully, this will help, Thanks!

When using angular forms most parameters will be sent as objects, hence your login function will most likely have this object form.value = {username: 'someone', password:'1234', grant_type: 'password'} as the parameter

to send this object as x-www-form-urlencoded your code will be

export class AuthService {
private headers = new HttpHeaders(
{
'Content-Type':  'application/x-www-form-urlencoded',
Accept: '*/*',
}
);
constructor(private http: HttpClient) { }


login(data): Observable<any> {
const body = new HttpParams({fromObject: data});
const options = { headers: this.headers};
return this.http.post(`${environment.baseUrl}/token`, body.toString(), options);
}

Angular 8

const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
const params = new HttpParams();
params.set('username', 'username');
params.set('password', 'password');


this.http.post(
'https://localhost:5000/api',
params.toString(),
{ headers }
);
export class MaintenanceService {


constructor(private http: HttpClient) { }


//header de requete http
private headers = new HttpHeaders(
{  'Content-Type':  'application/x-www-form-urlencoded' }
);










// requete http pour recuperer le type des maintenances
createMaintenance(data: createMaintenance){
const options = { headers: this.headers};
return this.http.post('api/v2/admin/maintenances', data, options ).subscribe(status=> console.log(JSON.stringify(status)));
}

Angular 9

This is a code that works.

Take other options that fit to you to return not success answer.

 let params = new HttpParams({
fromObject: { email: usuario.email, password: usuario.password, role: usuario.role },
});


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

return this.http.post(`${this.url}/usuario/signup`, params.toString(), httpOptions).pipe(
map(
(resp) => {
        

console.log('return http', resp);
return resp;
},
(error) => {
console.log('return http error', error);
return error;
}
)
);

remember from string you use fromString and not fromObject.

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


body.set('userId', userId);
body.set('discussionId', discussionId);

For Angular 12, this is what worked for me.


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


params = new HttpParams()
.set("client_id", "client_id")
.set("client_secret", "client_secret")
.set("grant_type", "grant_type")
.set("scope", "scope")


getToken(){
return this._http.post(`${URL}`, this.params, this.options)
}


Also, remember to import the following at the top import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';

Also notice that, unlike the others, we do not use toString() as it's redundant.