How to force Angular2 to POST using x-www-form-urlencoded

I have a project that needs to use Angular2 (final) to post to an old, legacy Tomcat 7 server providing a somewhat REST-ish API using .jsp pages.

This worked fine when the project was just a simple JQuery app performing AJAX requests. However, the scope of the project has grown such that it will need to be rewritten using a more modern framework. Angular2 looks fantastic for the job, with one exception: It refuses to perform POST requests using anything option but as form-data, which the API doesn't extract. The API expects everything to be urlencoded, relying on Java's request.getParameter("param") syntax to extract individual fields.

This is a snipped from my 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));
                }
            }
        );
    }
}

No matter what I set the header content type to be, it always ends up arriving as non-encoded form-data. It's not honoring the header I'm setting.

Has anyone else encountered this? How do you go about forcing Angular2 to POST data in a format that can be read by an old Java API using request.getParameter("param")?

Edit: For anyone else who finds this in the future, the solution is actually really simple. Set the body of the post like this:

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

See Brad's example below.

Answers:

Answer

For Angular 4.3+/5+ (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';

Answer

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

}

Answer

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!

Answer

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

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

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(...);
Answer

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!

Answer

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.