URL エンコーディングの定義と、それを扱うための .NET Framework のライブラリを検証しました。
パーセント エンコーディングと URL エンコーディング
パーセント エンコーディングとは、文字列を UTF-8 でエンコードし、各バイトをパーセント記号 % とその 16 進数を用いて表すことです。
例えば、"/" は "%2F" に、"あ" は "%E3%81%82" に変換されます。
URL エンコーディングとは、URI の中で使われている記号と混在しないように一部の文字列をパーセント エンコーディングにより
変換することです。両者の言葉を区別せずに使うこともあります。
URL エンコーディング
RFC 3986 では、文字は次のように分類されます。
- 非予約文字
- エンコードしなくても利用できる文字
- アルファベット、数字、および 4 種類の記号
-._~
- 予約文字
- URI で意味を持つ記号
- 18 種類の記号
!#$&'()*+,/:;=?@[]
- その他の文字
- エンコードが必要な文字
- 11 種類の記号
" %<>\^`{|}、その他のすべての文字 (日本語など)
URL エンコーディングは、主に次の 2 通りで利用されます。
- URI の各セグメント (クエリ文字列を除く)
https://tempuri.org/messages/Hello%20World%21のmessagesやHello%20World%21の部分- 非予約文字以外をパーセント エンコーディング
- ただし、Web フレームワーク個別の仕様により、パーセント エンコーディングしても使用を制限されることがある
- URI のクエリ文字列や、POST などで送信するときの本文 (フォーム)
key=value&message=Hello+World%21のkeyやHello+World%21の部分- 非予約文字以外をパーセント エンコーディングし、さらに
%20(スペース) を+に変換 - MIME タイプ
application/x-www-form-urlencodedと定義されている
.NET Framework のライブラリ
.NET Framework では、URL エンコーディングのために次の方法が用意されています。
- System.Uri.EscapeDataString メソッド
- RFC 3986 に従って非予約文字以外をパーセント エンコーディング
- System.Uri.EscapeUriString メソッド
- RFC 3986 に従って非予約文字・予約文字以外をパーセント エンコーディング
- 既に全体が URI の形式になっているときに利用する
- クエリ文字列も同様の規則で変換される。
application/x-www-form-urlencodedには変換されない
- クエリ文字列も同様の規則で変換される。
- System.Uri インスタンスの AbsoluteUri プロパティ
- 基本的に Uri.EscapeUriString メソッドと同じだが、下記の点が異なる
%XXの形式になっているかどうかで扱いが異なるhttps://tempuri.org/%2はhttps://tempuri.org/%252にhttps://tempuri.org/%25はhttps://tempuri.org/%25のまま
- クエリ文字列でない部分の
\は/に変換される
- System.Net.WebUtility.UrlEncode メソッド
- RFC 2396 (旧版) に近い仕様で非予約文字以外をパーセント エンコーディングし、さらに
%20(スペース) を+に変換
- RFC 2396 (旧版) に近い仕様で非予約文字以外をパーセント エンコーディングし、さらに
- System.Web.HttpUtility.UrlEncode メソッド
- System.Net.WebUtility.UrlEncode メソッドと同じだが、小文字になる
- System.Net.Http.FormUrlEncodedContent クラス
- key-value データをまとめて
application/x-www-form-urlencodedに変換
- key-value データをまとめて
.NET では System.Uri.EscapeDataString メソッド、System.Uri.EscapeUriString メソッド、
System.Net.Http.FormUrlEncodedContent クラスを使えばよいでしょう。
アプリケーションから HTTP 接続をするために System.Net.Http.HttpClient クラスを使うことが多いと思いますが、
接続先の URI を string で渡しても、HttpClient の内部では Uri インスタンスで扱われます。
したがって、URI を HttpClient に渡す前に、セグメントもクエリ文字列も URL エンコーディングしておくのがよさそうです。
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Net.Http; | |
| using System.Threading.Tasks; | |
| namespace UnitTest.Client | |
| { | |
| public static class HttpHelper | |
| { | |
| public static string AddQuery(this string uri, IDictionary<string, string> data) => | |
| $"{uri}?{data.ToFormUrlEncoded()}"; | |
| public static string ToFormUrlEncoded(this IDictionary<string, string> data) | |
| { | |
| using (var content = new FormUrlEncodedContent(data)) | |
| return content.ReadAsStringAsync().GetAwaiter().GetResult(); | |
| } | |
| async public static Task<T> GetAsync<T>(string uri) | |
| { | |
| using (var http = new HttpClient()) | |
| { | |
| var response = await http.GetAsync(uri); | |
| response.EnsureSuccessStatusCode(); | |
| return await response.Content.ReadAsAsync<T>(); | |
| } | |
| } | |
| async public static Task<T> PostAsFormAsync<T>(string uri, IDictionary<string, string> data) | |
| { | |
| using (var http = new HttpClient()) | |
| { | |
| var response = await http.PostAsync(uri, new FormUrlEncodedContent(data)); | |
| response.EnsureSuccessStatusCode(); | |
| return await response.Content.ReadAsAsync<T>(); | |
| } | |
| } | |
| } | |
| } |
| using System; | |
| using System.Collections.Generic; | |
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |
| namespace UnitTest.Client | |
| { | |
| [TestClass] | |
| public class UriQueryTest | |
| { | |
| [TestMethod] | |
| public void Get_Segment() | |
| { | |
| // http://localhost:1961/api/uriquery/Hello~%2C%20World%21 | |
| var uri = $"http://localhost:1961/api/uriquery/{Uri.EscapeDataString("Hello~, World!")}"; | |
| var result = HttpHelper.GetAsync<string>(uri).GetAwaiter().GetResult(); | |
| } | |
| [TestMethod] | |
| public void Get_Query() | |
| { | |
| var data = new Dictionary<string, string> | |
| { | |
| { "id", "Hello, the \"World+\"." }, | |
| }; | |
| // http://localhost:1961/api/uriquery?id=Hello%2C+the+%22World%2B%22. | |
| var uri = "http://localhost:1961/api/uriquery".AddQuery(data); | |
| var result = HttpHelper.GetAsync<string>(uri).GetAwaiter().GetResult(); | |
| } | |
| [TestMethod] | |
| public void Post_Form() | |
| { | |
| var data = new Dictionary<string, string> | |
| { | |
| { "name", "Hello, the \"World+\"." }, | |
| }; | |
| var uri = "http://localhost:1961/api/uriquery"; | |
| var result = HttpHelper.PostAsFormAsync<string>(uri, data).GetAwaiter().GetResult(); | |
| } | |
| } | |
| } |
作成したサンプル
バージョン情報
- .NET Framework 4.5
参照


コメントを残す