I have the following simple test:

const jsObj = {description: `a b`}; const expected = 'description=a%5Cnb'; const actual = Utils.buildQueryParams(jsObj); assert.deepEqual(actual, expected); 

Somewhere in the depths of Utils.buildQueryParams each parameter placed on the summary line goes through the following procedure:

 function encodeQueryItem(item) { let escaped = item; if (typeof escaped === 'string') { escaped = JSON.stringify(escaped); escaped = escaped.substring(1, escaped.length - 1); } return encodeURIComponent(escaped); } 

Everything works encodeQueryItem for me, but I don’t like what the encodeQueryItem looks encodeQueryItem . Is it possible to do this in a more elegant way?


Once the “why” questions have been sent, then I will explain: If I have a line like a\nb , where \n is a newline character, then the following line will go to the server: 'description=a%0Ab' , and in the same form (after URL decode) will be saved there. Then, when I ask him to return this line to me, he will happily put it in JSON, and the JSON parser will break because all whitespace characters in the string must be escaped.

Although it is not important, here is the code and buildQueryParams :

 function buildQueryParams(jsObject) { return Object.keys(jsObject) .map((name) => `${name}=${encodeQueryItem(jsObject[name])}`) .join('&'); } 
  • The format itself is a crutch. It is not necessary at the same time two different shielding, when the second is enough. - Qwertiy
  • Lacks. The format should be as it is. Not the answer to the question. - ixSci
  • but I don't understand something, why we string stringify again and apparently we cut the quotes - Grundy
  • @Grundy, escapes whitespace characters (\ n, \ t, etc.) - ixSci
  • @ixSci, but they can also be escaped through encodeURIComponent. - Qwertiy

1 answer 1

You don't really like what encodeQueryItem looks like. Because encodeURIComponent accepts only strings as input - which means that the conditional operator is definitely superfluous here.

If the task is to encode a string so that the server cannot decode it - the most beautiful way is to use the encodeURIComponent twice:

 function encodeQueryItem(item) { return encodeURIComponent(encodeURIComponent(item)); } 

The result of encodeURIComponent is "JSON-safe", so there will be no problems with it. Just need to remember to decode the string back to the client.

The advantage of this method - it will not break when the server is still repaired.


If it is required that the server provides correctly shielded JSON with an incorrect implementation, then you have chosen the correct option.

Only the type check is superfluous, and it’s better to use slice as indicated by lexxl

 function encodeQueryItem(item) { return encodeURIComponent(JSON.stringify(item).slice(1, -1)); } 

It must be remembered that this option will break as soon as the server is repaired and will require further rather strange decoding procedure, which was initially avoided. And sooner or later it will be repaired, because it is a vulnerability.


If the function can take on different types, you need to remember that in any case the server will turn everything into a string, which means that reverse decoding on the client cannot be avoided. So why then try? Just turn everything into a string with the type preserved via stringify - and then encode the string as done above.

The absence of separate processing for strings will noticeably simplify the logic of the decoder, which will still be.

 function encodeQueryItemObject(item) { return encodeURIComponent(encodeURIComponent(JSON.stringify(item))); } 

PS That's why I like to work with full-stack that can be repaired where it is broken, and not where it is allowed :)