はじめに 「URLデコード」は“URL用に変形された文字列を、人間の世界に戻す”処理
URLエンコードは、
「URLの中で問題になりそうな文字(日本語・スペース・記号など)を、安全な形(%xx や +)に変換する」処理でした。
URLデコードは、その逆です。
%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AFC%23+入門
といった「URL用に変形された文字列」を、
元の "こんにちは" や "C# 入門" に戻すのが役割です。
業務では、クエリパラメータやルーティングの値、外部サービスから返ってきたURLパラメータなどを扱うときに、ほぼ確実に登場します。
ここから、C#でのURLデコードを、
「考え方 → 基本メソッド → クエリでの使い方 → パスでの使い方 → 実務ユーティリティ」
という流れでかみ砕いて説明していきます。
URLデコードの基本イメージ
エンコードとデコードはペアで考える
URLエンコードとデコードの関係は、こうなります。
"C# 入門"
→ URLエンコード
→ "C%23+入門"
"C%23+入門"
→ URLデコード
→ "C# 入門"
つまり、「URLの世界に合わせて変形したもの」を、
元の文字列に戻すのがURLデコードです。
ここで大事なのは、
どの方法でエンコードされたか(どのメソッドを使ったか)
どの部分をエンコードしたか(値だけか、パスか)
によって、デコード側も合わせる必要がある、ということです。
C#での基本:WebUtility.UrlDecode を使う
一番素直なURLデコード
.NET(.NET Core / .NET 5+)でまず使うべきなのは、System.Net.WebUtility.UrlDecode です。
using System.Net;
public static class UrlDecodeUtil
{
public static string Decode(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.UrlDecode(value);
}
}
C#UrlDecode は、次のような変換をしてくれます。
%xx → 対応するバイト → 文字列(UTF-8前提)+ → スペース
つまり、URLエンコードで変形された文字列を、
元の文字列に戻してくれるメソッドです。
動作例でイメージをつかむ
Console.WriteLine(UrlDecodeUtil.Decode("%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"));
// こんにちは
Console.WriteLine(UrlDecodeUtil.Decode("C%23+入門"));
// C# 入門
C#ここでのポイントは、
%E3%81%93... のような日本語のエンコードも、UTF-8として正しく戻してくれる+ がスペースに戻る(クエリパラメータでよく使われるルール)
ということです。
HttpUtility.UrlDecode との関係
レガシーASP.NETでは HttpUtility.UrlDecode
古いASP.NET(.NET Framework)では、System.Web.HttpUtility.UrlDecode がよく使われていました。
using System.Web;
string decoded = HttpUtility.UrlDecode("%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF");
C#新しめの .NET(.NET Core / .NET 5+)では、System.Web 自体をあまり使わないので、
基本的には System.Net.WebUtility.UrlDecode を選べばOKです。
「古いコードでは HttpUtility、新しいコードでは WebUtility」
くらいの理解で十分です。
実務での使い方:クエリパラメータをデコードする
典型的なシナリオ
例えば、次のようなURLを受け取ったとします。
https://example.com/search?q=C%23+入門&lang=ja-JP
ここで、q の値を取り出して、
人間が読める "C# 入門" にしたい、というケースです。
フレームワーク(ASP.NET Coreなど)を使っていれば、Request.Query["q"] の時点でデコード済みになっていることが多いですが、
「自分で生のクエリ文字列を扱う」場面もあります。
手動でデコードするイメージ
string rawQueryValue = "C%23+入門";
string keyword = UrlDecodeUtil.Decode(rawQueryValue);
Console.WriteLine(keyword); // C# 入門
C#ここで重要なのは、
エンコード側が UrlEncode を使っているなら、
デコード側も UrlDecode を使う
という「ペア関係」を守ることです。
パスの一部をデコードする場合
パスセグメントとしてエンコードされていた場合
例えば、次のようなURLを考えます。
https://example.com/users/%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E
この %E5%B1%B1%E7%94%B0... は、"山田太郎" をURLエンコードしたものです。
パスセグメントとしてエンコードされている場合、WebUtility.UrlDecode でも戻せますが、Uri.UnescapeDataString を使うこともあります。
string encodedSegment = "%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E";
string name1 = WebUtility.UrlDecode(encodedSegment);
string name2 = Uri.UnescapeDataString(encodedSegment);
Console.WriteLine(name1); // 山田太郎
Console.WriteLine(name2); // 山田太郎
C#クエリパラメータの値 → UrlDecode
パスセグメント → UrlDecode か UnescapeDataString
というイメージで覚えておくとよいです。
ここが重要:二重デコードに注意する
「すでにデコード済みのもの」をもう一度デコードしない
URLエンコードと同じく、
URLデコードでも「二重デコード」はよくある落とし穴です。
例えば、フレームワーク側がすでにデコードしてくれている値に対して、
さらに UrlDecode をかけてしまうと、+ が消えたり、% が不正な形になったりします。
string once = UrlDecodeUtil.Decode("C%23+入門"); // "C# 入門"
string twice = UrlDecodeUtil.Decode(once); // "C# 入門"(2回目は変化しないこともあるが、常に安全とは限らない)
C#特に、%25(% のエンコード)などが絡むと、
「1回だけデコードする前提」の文字列を2回デコードしてしまい、
意図しない文字列になることがあります。
実務では、
どの層でURLデコードするか
コントローラに渡ってくる値は「生」か「デコード済み」か
をチーム内で決めておくことが大事です。
エラーや不正な文字列への向き合い方
UrlDecode は例外を投げず、できる範囲でデコードする
WebUtility.UrlDecode は、
Base64デコードのように例外を投げることはあまりなく、
「解釈できるところだけデコードする」動きをします。
例えば、%ZZ のような不正なエスケープが混ざっていても、
そのまま %ZZ として残ることがあります。
業務ユーティリティとしては、
「不正な形式を検出したいのか」
「とりあえず読めるところだけ読めればいいのか」
によって、追加のチェックを入れるかどうかを決めることになります。
初心者向けの段階では、
まずは「UrlDecode は例外で落ちにくい」という感覚だけ持っておけば十分です。
業務ユーティリティとしてのまとめ方
用途ごとにメソッドを分ける
実務で使いやすくするには、
「何をデコードするのか」をメソッド名で明確にしておくとよいです。
using System;
using System.Net;
public static class UrlDecoding
{
// クエリパラメータの値用
public static string DecodeQueryValue(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.UrlDecode(value);
}
// パスセグメント用
public static string DecodePathSegment(string? segment)
{
if (string.IsNullOrEmpty(segment))
{
return string.Empty;
}
return WebUtility.UrlDecode(segment);
// あるいは Uri.UnescapeDataString(segment);
}
}
C#呼び出し側は、
クエリの値 → DecodeQueryValue
パスの一部 → DecodePathSegment
というように、意図に応じて使い分けられます。
nullや空文字の扱いをユーティリティ側で決めておく
どちらのメソッドも、
null や空文字なら空文字を返すようにしています。
これにより、呼び出し側で毎回
if (value != null) { ... }
C#といったガードを書く必要がなくなり、
業務コードがすっきりします。
まとめ 「URLデコードユーティリティ」は“URLの世界からアプリの世界へ橋渡しする変換器”
URLデコードは、
「URLのルールに合わせて変形された文字列」を、
アプリケーションが扱いやすい普通の文字列に戻すための処理です。
押さえておきたいポイントは、
WebUtility.UrlDecode を使えば、%xx や + を元の文字列に戻せること。
エンコード側とデコード側はペアで考え、同じルール(同じメソッド)を使うこと。
クエリパラメータとパスセグメントで、使うメソッドや前提が少し違うこと。
二重デコードを避けるために、「どの層でデコードするか」を設計として決めておくこと。
ここまで理解できれば、「なんとなくUrlDecodeしている」段階から一歩進んで、
“URLの構造とデータの流れを意識した、業務で使えるURLデコードユーティリティ”を、
自分のC#コードの中に自然に組み込めるようになっていきます。

