Как заставить Angular2 POST с помощью x-www-form-urlencoded
У меня есть проект, который должен использовать Angular2 (окончательный вариант) для публикации на старом унаследованном сервере Tomcat 7, предоставляющем API-интерфейс REST-ish с использованием страниц.jsp.
Это работало нормально, когда проект представлял собой простое приложение JQuery, выполняющее запросы AJAX. Тем не менее, масштабы проекта выросли так, что его необходимо будет переписать с использованием более современных рамок. Angular2 выглядит фантастически для этой работы, за одним исключением: он отказывается выполнять запросы POST, используя что-либо, кроме как данные формы, которые API не извлекает. API ожидает, что все будет urlencoded, полагаясь на 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 передавать данные POST в формате, который может быть прочитан старым Java API с помощью request.getParameter("param")
?
Изменить: Для тех, кто найдет это в будущем, решение на самом деле очень просто. Установите тело сообщения следующим образом:
let body = `username=${username}&password=${password}`;`
Смотрите пример Брэда ниже.
14 ответов
Вы можете сделать это используя URLSearchParams
поскольку тело запроса и angular автоматически установят тип контента в application/x-www-form-urlencoded
и кодировать тело правильно.
let body = new URLSearchParams();
body.set('username', username);
body.set('password', password);
this.http.post(this.loginUrl, body).map(...);
Причина, по которой он в данный момент не работает для вас, заключается в том, что вы не кодируете данные тела в правильном формате и неправильно настраиваете параметры заголовка.
Вам нужно закодировать тело следующим образом:
let body = `username=${username}&password=${password}`;
Вам нужно установить параметры заголовка следующим образом:
this.http.post(this.loginUrl, body, { headers: headers }).map(...);
Для Angular 4.3+/5+ (New HTTPClient) используйте следующее:
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 => {
//...
});
Обратите внимание на 3 вещи, чтобы заставить его работать как положено:
- Используйте URLSearchParams для вашего тела
- Преобразовать тело в строку
- Установите тип содержимого заголовка
Внимание: старым браузерам нужен полифилл! Я использовал: npm i url-search-params-polyfill --save
а затем добавлен в polyfills.ts: import 'url-search-params-polyfill';
Для тех, кто все еще ищет ответ, вот как я решил его с помощью Angular 5 и 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);
НЕ устанавливайте заголовок Content-Type, угловой исправит это для вас!
Вот что сработало для меня с Angular 7:
const payload = new HttpParams()
.set('username', username)
.set('password', password);
this.http.post(url, content);
Нет необходимости явно устанавливать заголовок при таком подходе.
Обратите внимание, что объект HttpParams является неизменным. Таким образом, выполнение чего-то вроде следующего не будет работать, оно даст вам пустое тело:
const payload = new HttpParams();
payload.set('username', username);
payload.set('password', password);
this.http.post(url, content);
Угловой 9
Это код, который работает.
Выберите другие варианты, которые подходят вам, чтобы вернуть ответ "неуспешно".
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;
}
)
);
помните из строки, которую вы используете
fromString
и нет
fromObject
.
Я нашел это решение после нескольких часов работы над этим вопросом.
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);
}));
}
При использовании угловых форм большинство параметров будут отправлены как объекты, поэтому ваша функция входа, скорее всего, будет иметь этот объектform.value = {username: 'someone', password:'1234', grant_type: 'password'}
как параметр
чтобы отправить этот объект как x-www-form-urlencoded, ваш код будет
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 12 это сработало для меня.
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)
}
Кроме того, не забудьте импортировать следующее вверху
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
Также обратите внимание, что, в отличие от других, мы не используем функцию toString(), так как она избыточна.
Добавляя из Brad, тело вам нужно будет вызвать body.toString() и поставить его как тело запроса.
Угловой 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)));
}
Ребята, я работал над этим некоторое время, и благодаря этому сообщению Джоша Морони https://www.joshmorony.com/integrating-an-ionic-application-with-a-nodejs-backend/ я понял, что проблема была. По сути, когда я начал тестировать свой api, я использовал POSTMAN, и он работал отлично, но когда дело дошло до его реализации с помощью Ionic Angular, это стало проблемой. Решение в этом посте касается только импортаbody-parser
и использовать его как промежуточное ПО для приложений, как это app.use(bodyParser.json())
в корневом файле (индексе) на стороне сервера.
Надеюсь, это поможет, спасибо!
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);
Мне удалось решить проблему, используя следующий блок кода:
const body = new URLSearchParams();
body.set('username', username);
body.set('password', password);
const headers = new HttpHeaders()
.append(
'Content-type',
'application/x-www-form-urlencoded; charset=UTF-8');
this.http.post(this.loginUrl, body, { headers }).map(...);