LoginSignup
11
4

More than 5 years have passed since last update.

パーセントエンコーディングの処理においてスペースの置き換え先はプラス記号か %20 か?

Last updated at Posted at 2017-09-08

概要

いくつかの言語でパーセントエンコーディングの処理においてスペースの置き換え先がプラス記号か %20 であるかどうかを調べました。

URL のクエリ文字列や application/x-www-form-urlencoded のボディを求めるためとき、HTML5 や URL Standard の仕様ではスペースはプラス記号 (+) に変換されることが求められます。

一方でパーセントエンコーディングの明確な仕様がなかったことから、HTTP サーバーは +%20 の両方をスペースとして扱われます。サーバーサイドの言語では application/x-www-form-urlencoded のためのエンコーディングメソッドもしくはオプションが提供されず、RFC 3986 の仕様にもとづいたエンコーディングメソッドで代用することがあります。

多くの場合、スペースの置き換え先が (+) かパーセントエンコーディング (%20) か意識する必要はありませんが、OAuth の場合、問題になります。

OAuth 1.0 の場合、スペースに対してパーセントエンコーディング (RFC 3986) が要求されるために、プラス記号にエンコーディングする方法を採用したライブラリではエラーになります。Twitter はパーセントエンコーディングについての解説記事を公開しています。

JavaScript

URLSearchParams は URL Standard にもとづきます。スペースはプラス記号に置き換わります。

const params = new URLSearchParams();
params.append("msg", "hello world");
console.log("msg=hello+world" === params.toString());

URLSearchParams%20 もスペースとして扱います。解析した結果は次のとおりです。

const params2 = new URLSearchParams("msg=hello+world");
console.log("hello world" === params2.get("msg"));

const params3 = new URLSearchParams("msg=hello%20world");
console.log("hello world" === params3.get("msg"));

encodeURIComponent の場合、スペースを %20 に置き換えます。

console.log("hello%20world" === encodeURIComponent("hello world"));

スペースをプラス記号に置き換えたいのであれば、replace の呼び出しを追加します。

const ret = encodeURIComponent("hello world").replace(/%20/g, '+')
console.log("hello+world" === ret);

decodeURIComponent%20 をスペースに置き換えますが、プラス記号は置き換えません。

console.log("hello world" === decodeURIComponent("hello%20world"));
console.log("hello+world" === decodeURIComponent("hello+world"));

Node.js

Node.js v7.0 からサポートされるようになった URLSearchParams を優先的に使うほうがよいでしょう。v7.0 以前の以前の方法を示します。

標準の querystring.stringify モジュールはスペースを %20 に置き換えます。

const querystring = require('querystring');

const ret = querystring.stringify({ msg: "hello world" });
console.log("msg=hello%20world" === ret);

querystring.parse+%20 の両方をスペースに置き換えます。

const querystring = require("querystring");

console.log("hello world" === querystring.parse("msg=hello+world")["msg"]);
console.log("hello world" === querystring.parse("msg=hello%20world")["msg"]);

RFC 3986 の仕様にもとづいたエンコーディングが必要であれば、ljharb/qs を導入します。

const qs = require("qs");

console.log("msg=hello%20world" === qs.stringify({ msg: "hello world" }));
console.log("hello world" === qs.parse("msg=hello+world")["msg"]);
console.log("hello world" === qs.parse("msg=hello%20world")["msg"]);

Python 3

urllib を使います。urlencode はデフォルトでスペースを +
に置き換えます。

>>> from urllib.parse import urlencode
>>> urlencode({"msg": "hello world"})
'msg=hello+world'

quote_via=quote を指定すれば、スペースは %20 に置き換わります。

>>> from urllib.parse import urlencode, quote
>>> urlencode({"msg": "hello world"}, quote_via=quote)
'msg=hello%20world'

parse_qsparse_qsl の両方とも +%20 をスペースに置き換えます。

>>> from urllib.parse import parse_qs
>>> parse_qs("msg=hello+world")
{'msg': ['hello world']}
>>> parse_qs("msg=hello%20world")
{'msg': ['hello world']}

Go

net/url モジュールを使います。url.Values 型の Encode はスペースを + に置き換えます。ParseQuery+ と %20 をスペースに置き換えます。

package main

import (
    "fmt"
    "net/url"
)

func main() {
    v := url.Values{}
    v.Set("msg", "hello world")
    fmt.Println(v.Encode())
  // msg=hello+world


  m, _ := url.ParseQuery("msg=hello+world")
  fmt.Println(m["msg"][0])
  // hello world

  m2, _ := url.ParseQuery("msg=hello%20world")
  fmt.Println(m2["msg"][0])
}

QueryEscape がスペースを + に変換するのに対して PathEscape%20 に変換します。さらに PathUnescape+ をスペースに変換しません。

func main() {
  fmt.Println("QueryEscape")
  fmt.Println(url.QueryEscape("hello world"))
  // hello+world
  fmt.Println(url.QueryUnescape("hello+world"))
  // hello world
  fmt.Println(url.QueryUnescape("hello%20world"))
  // hello world

  fmt.Println()
  fmt.Println("PathEscape")
  fmt.Println(url.PathEscape("hello world"))
  // hello%20world
  fmt.Println(url.PathUnescape("hello+world"))
  // hello+world
  fmt.Println(url.PathUnescape("hello%20world"))
  // hello world
}
11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4