angular / angular

Deliver web apps with confidence 🚀

Home Page:https://angular.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

common/http: HttpParams encoding of form data

achimha opened this issue · comments

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

When using HttpClient to send form data using HttpParams, a plus sign (+) in a parameter is not correctly encoded.

Expected behavior

Encoding should be correct.

Minimal reproduction of the problem with instructions

https://plnkr.co/edit/x4T7Dw4QZgHRHfyFaNWz?p=preview

The plunker performs a bogus HTTP POST request with form data that contains an email address containing a plus sign.

Environment


Angular version: 4.3.1


Browser:
- [x] Chrome (desktop) version 59

The relevant code:

export class App implements OnInit {
  constructor(private http: HttpClient) {
  }
  
  ngOnInit() {
    const body = new HttpParams().set('email', 'my+name@email.com');
    this.http.post('/foobar', body).subscribe(() => {});
  }
}

Relates to #11058

It seems like the params are being encoded with this function.

function standardEncoding(v: string): string {
  return encodeURIComponent(v)
      .replace(/%40/gi, '@')
      .replace(/%3A/gi, ':')
      .replace(/%24/gi, '$')
      .replace(/%2C/gi, ',')
      .replace(/%3B/gi, ';')
      .replace(/%2B/gi, '+')
      .replace(/%3D/gi, '=')
      .replace(/%3F/gi, '?')
      .replace(/%2F/gi, '/');
}

is there a reason to replace '%2b' with '+'? This makes it so that there is not way to encode a '+' with HttpParams.

This bug currently makes it impossible for me to use HttpClient to encode a latitude/longitude param in ISO-6709 format (which uses the +/- separator between the values).

This bug also makes it impossible to put some e-mail addresses in the query string (for example, a search). As demonstrated by Google, a + is a valid symbol in an e-mail address. For example testuser+github@gmail.com.

Angular removes the encoding for the + symbol, apparently because IETF RFC 3986 lists it as one of the characters that is valid in a query string parameter. That's true, it is valid if it's intended as a space. What happens with the e-mail above, is that when it reaches the server, the server interprets it as testuser github@gmail.com and predictably can't find a corresponding e-mail address.

I'm hitting this bug when sending out ISO datetimes including a timezone offset

The same problem.

For anyone who can't wait for the eventual patch, I was able to workaround this by manually encoding the '+' and appending the param to the HttpClent uri via '?'. As in:

    const myEncodedQueryParamValue = myQueryParamValue.replace("+", "%2B");
    const uri = myUri + "?myQueryParam=" + myEncodedQueryParamValue;

    return this.httpClient.get<MyResponseType>(
      uri,
      {
        params: myParams
      }
    );

HttpClient will do the right thing here and append any passed-in HttpParams to the existing query string.

What I've done is implement HttpParameterCodec into a simple encoder that uses the browser standard encodeURIComponent MDN.

class CustomEncoder implements HttpParameterCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key);
  }

  encodeValue(value: string): string {
    return encodeURIComponent(value);
  }

  decodeKey(key: string): string {
    return decodeURIComponent(key);
  }

  decodeValue(value: string): string {
    return decodeURIComponent(value);
  }
}

And then use it when I need HttpParams instances.

// These params get encoded correctly
const params = new HttpParams({encoder: new CustomEncoder()});

I created a PR to solve this here: #19710 .


I also created a package to solve it (at least while it is reviewed / merged) for the case of HttpClient. It's a simple drop-in replacement that modifies the minimum to fix the problem. It works with just changing one import.

Check it here: https://github.com/tiangolo/ngx-http-client

We have the inverse problem, when we put the URL on the browser like this https://example.com?someQueryParam=My+Query+Param+With+Plus+Sign Angular transform all the + to %2B, we build a pretty friendly URL in other script (or in our fronted server) but when came to Angular the problem begins!

Any helps?

Many thanks!

This issue breaks the password flow of angular-oauth2-oidc for emails (and presumably passwords) that have a plus in them.

This issue is back on Angular 6.0.3, passing parameters through http.get with a query value containing "+" sign is being converted to %20, arriving wrong in the server side.

What I've done is implement HttpParameterCodec into a simple encoder that uses the browser standard encodeURIComponent MDN.

class CustomEncoder implements HttpParameterCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key);
  }

  encodeValue(value: string): string {
    return encodeURIComponent(value);
  }

  decodeKey(key: string): string {
    return decodeURIComponent(key);
  }

  decodeValue(value: string): string {
    return decodeURIComponent(value);
  }
}

And then use it when I need HttpParams instances.

// These params get encoded correctly
const params = new HttpParams({encoder: new CustomEncoder()});

Me too. I've created a new class that extends HttpUrlEncodingCodec, and overwritten two of its functions.

import { HttpUrlEncodingCodec } from '@angular/common/http';

export default class CustomEncoder extends HttpUrlEncodingCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key);
  }

  encodeValue(value: string): string {
    return encodeURIComponent(value);
  }
}

And Angular team hasn't fixed it for more than one year...
No comments.

@vitorfgsantos

...

Me too. I've created a new class that extends HttpUrlEncodingCodec, and overwritten two of its functions.

import { HttpUrlEncodingCodec } from '@angular/common/http';

export default class CustomEncoder extends HttpUrlEncodingCodec {
  encodeKey(key: string): string {
    return encodeURIComponent(key);
  }

  encodeValue(value: string): string {
    return encodeURIComponent(value);
  }
}

Don't forget to make the class Injectable and inject it in constructor. Otherwise testing gets more complicated.

@Injectable({
  providedIn: 'root'
})
export default class CustomEncoder extends HttpUrlEncodingCodec {
constructor(private customEncoder: CustomEncoder) { }

const params = new HttpParams({encoder: this.customEncoder});

Bug angular 7.0.2

Hitting this on angular 7.0.0 when trying to send a phone number to the backend:

const body = new HttpParams().set('phoneNumber', phoneNumber) // phoneNumber === +1234567890

return this.http.post(url, body, {
      headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
});

image

@orestes's custom encoder worked.

@alxhub What's the reasoning behind these exceptions to encodeURIComponent?
https://github.com/angular/angular/blame/f8096d499324cf0961f092944bbaedd05364eea1/packages/common/http/src/params.ts#L59

function standardEncoding(v: string): string {
  return encodeURIComponent(v)
      .replace(/%40/gi, '@')
      .replace(/%3A/gi, ':')
      .replace(/%24/gi, '$')
      .replace(/%2C/gi, ',')
      .replace(/%3B/gi, ';')
      .replace(/%2B/gi, '+')
      .replace(/%3D/gi, '=')
      .replace(/%3F/gi, '?')
      .replace(/%2F/gi, '/');
}

export class HttpUrlEncodingCodec implements HttpParameterCodec {
  encodeKey(key: string): string { return standardEncoding(key); }

  encodeValue(value: string): string { return standardEncoding(value); }

  decodeKey(key: string): string { return decodeURIComponent(key); }

  decodeValue(value: string) { return decodeURIComponent(value); }
}

I'm guessing this is for Router URLs?
HttpClient should not be using standardEncoding(), it should just use encodeURIComponent()

EDIT (Research Notes)
https://stackoverflow.com/a/49440389/197733 mentions RFC3986

In my case, this may be an issue with Spring Boot 1.5 default behavior
https://stackoverflow.com/questions/50270372/why-is-spring-de-coding-the-plus-character-on-application-json-get-requests

An amusing thing is that Chrome DevTools Network request details Query String Parameters section displays a request to:
path?foo=hello+world
decoded as

foo: hello world

So even DevTools isn't clear on the + vs space decoding issue!

I also tried posting phoneNumber. However, I have to use URLSearchParams instead of HttpParams. For me, none of the solutions worked.

Hello everyone!
I am also sending the string with + character (+375) and my server is receiving my string correctly, that is +375.

But in Chrome, I see the next data: https://i.gyazo.com/4e475a8446078fbd6641e2b85e970cc6.png

It seems like I have a space instead of the plus :)
But it works.

And If I click on the 'View URL encoded' link in Chrome, I will get the data with the plus character.
Look at https://i.gyazo.com/1581c20433272986727a5212630f6323.mp4

I don't know why it is. Why do I get the space?

How is this not fixed still?
Why was this behavior added in the first place?

Encountered this attempting to url encode ISO8601 timestamp, eg. '2019-05-22T04:20:33+0000' :(

Angular 8.2.14

What is the reasoning behind these exceptions to 'encodeURIComponent'?

Stackblitz: https://stackblitz.com/edit/angular-url-encode

Any updates with this issue now?

Experiencing the same issue

Guys, I proposed a solution on SO some time ago:
https://stackoverflow.com/a/52458069/4191683

Most of all I like tag freq1: low with the issue with 102 upvotes

I'm going to close this as a duplicate of #11058 - we'll track this problem there.

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.