本題に入る前のぼやきです。RawUrlの命名が気に食わない。
普通にRawUrlと言ったらアクセスしたフルパスが入ると思うんですが、実際にはホストやポートを含まない部分のみが返されます。
queryStringをデコードしてUriオブジェクトに戻そうと思ったらRawUrlだとエラーになり、あれ?おかしい?と思ったらこういう事でした。
以下のUrlにアクセスしたと仮定して
var url = "http://160.251.74.83/?hoge=fuga"
HttpListenerContext context;
HttpListenerRequest request = context.Request
request.RawUrl
// => /?hoge=fuga
request.Url.OriginalString
// => http://160.251.74.83/?hoge=fuga
本題はここから。
Urlのエンコードとデコードは初心者にはわかりづらかったので、楽に実装する方法だけを記します。
Encodeする側は簡単です。適当に文字列をつなげて最後にUri.EscapeUriString()に投げてしまえばいいようにしてくれます。
Encodeする側
// まとめてQuery Stringをエンコードしたいとき。
var url = "http://160.251.74.83/?hoge=ふが"
var encodedUrl = Uri.EscapeUriString(url);
// 以下だとQueryString以外の部分もEncodeされてしまうので注意。
var encoded1 = Uri.EscapeDataString(url);
var encoded2 = HttpUtility.UrlEncode(url);
Decodeする側も特に難しくありません。
基本的にQueryStringのみを扱いたいと思うので、ParseQueryStringにUrl.Queryを渡してあげればOKです。ParseQueryStringの中でDecodeしてくれます(第2引数で指定。ない場合はUTF-8)。
返り値はNameValueCollectionですが、Dictionaryと同じようなものです。Getメソッドが追加されているため、これを使って該当するクエリを取り出します。
Decodeする側
// decodeされたフルパスが欲しい時(どっちでもよい)
var decodedUrl = HttpUtility.UrlDecode(context.Request.Url.OriginalString);
var decodedUrl2 = Uri.UnescapeDataString(context.Request.Url.OriginalString);
// QueryStringを扱いたい時
var queryDictionary = HttpUtility.ParseQueryString(context.Request.Url.Query);
var hoge = queryDictionary.Get("hoge");
// hoge => "ふが" ※該当なしの場合はNull
最初はエンコードデコードのタイミングが良く分からなかったので、Url.OriginalStringをデコードしてUriオブジェクトを作成して、それを使ってParseさせたりと回りくどいことをしてました。
頻用するからもっと簡単なはずだと思って調べてみると、やはりシンプルに実装できますね。
ハマりそうなポイントは半角スペースの扱いです。
HttpUtility.UrlEncodeは「+」に変換し、
Uri.EscapeUriString & EscapeDataStringは「%20」にエンコードします。
逆にデコード時にUriUnescapeDataStringは「+」を半角スペースに変換しないため、HttpUtilityとUriクラスを混ぜて使うと問題になる場合があります。
「%20」はどちらであっても変換されますので、エンコードで常にEscapeUriString使う分には問題なさそうです。が、実装者によって変わってしまうとデコード側でバグになるかもしれないので可能な限り統一しましょう。
コメント