URL エンコーディング

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%21messagesHello%20World%21 の部分
    • 非予約文字以外をパーセント エンコーディング
      • ただし、Web フレームワーク個別の仕様により、パーセント エンコーディングしても使用を制限されることがある
  • URI のクエリ文字列や、POST などで送信するときの本文 (フォーム)
    • key=value&message=Hello+World%21keyHello+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/%2https://tempuri.org/%252
      • https://tempuri.org/%25https://tempuri.org/%25 のまま
    • クエリ文字列でない部分の \/ に変換される
  • System.Net.WebUtility.UrlEncode メソッド
    • RFC 2396 (旧版) に近い仕様で非予約文字以外をパーセント エンコーディングし、さらに %20 (スペース) を + に変換
  • System.Web.HttpUtility.UrlEncode メソッド
    • System.Net.WebUtility.UrlEncode メソッドと同じだが、小文字になる
  • System.Net.Http.FormUrlEncodedContent クラス
    • key-value データをまとめて application/x-www-form-urlencoded に変換

 

Uri.AbsoluteUri

 

.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>();
}
}
}
}
view raw HttpHelper.cs hosted with ❤ by GitHub
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&quot;.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&quot;;
var result = HttpHelper.PostAsFormAsync<string>(uri, data).GetAwaiter().GetResult();
}
}
}
view raw UriQueryTest.cs hosted with ❤ by GitHub

 

作成したサンプル

バージョン情報

  • .NET Framework 4.5

参照

カテゴリー: .NET Framework, サービス. タグ: , . Leave a Comment »

コメントを残す

  • WordPress.com で次のようなサイトをデザイン
    始めてみよう