miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET Web API クッキーのエンコードについて

stackoverflow - WebAPI encoding cookie values

ASP.NET MVC ではクッキーの取得・設定時に、値のエンコード/デコードは行われませんが、
ASP.NET Web API の場合は、URL エンコード/デコードが行われます。

ASP.NET Web API

ASP.NET Web API におけるクッキーの取得・設定の例。
キー「abcd」に「"!()_-*.aA0 ?#$%&|@\\/[]{}<>+=^~\"'`;:,あ"」を設定しています。

using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http;

namespace CookieTry.Controllers
{
    public class MyApiController : ApiController
    {
        [Route("apitest")]
        public HttpResponseMessage Get()
        {
            var requestCookie = Request.Headers.GetCookies("abcd").FirstOrDefault()?["abcd"]?.Value;
            Debug.WriteLine(requestCookie);
            
            string abcd = "!()_-*.aA0 ?#$%&|@\\/[]{}<>+=^~\"'`;:,あ";

            var cookie = new CookieHeaderValue("abcd", abcd);
            cookie.Path = "/";
            cookie.Expires = DateTime.Now.AddDays(1);

            var response = Request.CreateResponse(HttpStatusCode.OK);
            response.Headers.AddCookies(new CookieHeaderValue[] { cookie });

            return response;
        }

    }
}

この時、HTTP Response のヘッダーのクッキーの部分は、

Set-Cookie: abcd=!()_-*.aA0+%3f%23%24%25%26%7c%40%5c%2f%5b%5d%7b%7d%3c%3e%2b%3d%5e%7e%22%27%60%3b%3a%2c%e3%81%82; path=/

とエンコードされて書き出します。
また、リクエストにあるクッキーを Debug.WriteLine(requestCookie); で出力していますが、この時の値はデコードされたものとなります。

response.Headers.AddCookies(..) は、System.Net.Http.Formatting.dll にある拡張メソッド HttpResponseHeadersExtensions.cs です。


cookie.Domain = Request.RequestUri.Host;
でホストを指定した場合、デバッグ時等で URL が localhost:xxxx の環境ではレスポンスにて「domain=localhost;」になるので注意(リクエスト時にクッキーが送信されない)。

ASP.NET MVC

同じ例で、ASP.NET MVC の場合。

using System.Diagnostics;
using System.Web;
using System.Web.Mvc;

namespace CookieTry.Controllers
{
    public class MyMvcController : Controller
    {
        public ActionResult Index()
        {
            string requestCookie = Request.Cookies["abcd"]?.Value;
            Debug.WriteLine(requestCookie);
            
            var cookie = new HttpCookie("abcd", "!()_-*.aA0 ?#$%&|@\\/[]{}<>+=^~\"'`;:,あ");
            cookie.Path = "/";
            Response.Cookies.Add(cookie);

            return View();
        }
    }
}

この時、HTTP Response のヘッダーのクッキーの部分は

Set-Cookie: abcd=!()_-*.aA0 ?#$%&|@\/[]{}<>+=^~"'`;:,あ; path=/

と、エンコードされません。
Request.Cookies["abcd"]?.Valueで取得するリクエストのクッキーは、デコードされません。

補足

ASP.NET Web API の「response.Headers.AddCookies(new CookieHeaderValue[] { cookie });」のメソッドでは丁寧にコメントがかかれています。
ここでは Cookie に関するオブジェクトの説明と、構造化の仕組みについてあります。
構造化…ASP.NET Web API では一つのキーに対して複数の値を仕込むことができます。(詳細:HTTP Cookies in ASP.NET Web API

        // 概要:
        //     応答に Cookie を追加します。各 [Set-Cookie] ヘッダーは、1 つの System.Net.Http.Headers.CookieHeaderValue
        //     インスタンスとして表現されます。System.Net.Http.Headers.CookieHeaderValue には、ドメインやパスなどの Cookie
        //     情報だけでなく、1 つ以上の System.Net.Http.Headers.CookieState インスタンスも含まれます。各 System.Net.Http.Headers.CookieState
        //     インスタンスには、Cookie の名前、およびその名前と関連付けられているすべての Cookie の状態が含まれます。状態の形式は System.Collections.Specialized.NameValueCollection
        //     で、ワイヤーは HTML フォームの URL エンコード データとしてエンコードされています。このような表現により、複数の関連した "Cookie"
        //     を同じ [Cookie] ヘッダー内で伝達すると同時に、各 Cookie で状態を分離することが可能になります。[Cookie] ヘッダーの例を以下に示します。この例には、それぞれ
        //     [state1] および [state2] という名前が含まれる 2 つの System.Net.Http.Headers.CookieState
        //     が存在します。さらに、各 Cookie の状態には 2 つの名前/値のペア (name1/value1 と name2/value2) および (name3/value3
        //     と name4/value4) が含まれています。<code> Set-Cookie: state1:name1=value1&amp;name2=value2;
        //     state2:name3=value3&amp;name4=value4; domain=domain1; path=path1; </code>
        //
        // パラメーター:
        //   headers:
        //     応答のヘッダー
        //
        //   cookies:
        //     応答に追加する Cookie の値。
        public static void AddCookies(this HttpResponseHeaders headers, IEnumerable<CookieHeaderValue> cookies);

Web API の内部で行われる URL エンコードは、System.Net.WebUtility.UrlEncode とほぼ同様とみていいと思います。
Web API の URL エンコードまわりのソース:

「NETFX_CORE」(Portable Class Library かどうか)で、WebUtilityを使うかどうか分岐があります。PCL でない場合、UriQueryUtility.cs に描かれている実装でエンコード/デコードされます。

.NET Framework 4.6 の WebUtility.cs

WebUtility.cs の方と UriQueryUtility.cs にある実装ですが、若干コードの違いが。一つ、大文字と小文字の差異がありました。