HttpWebRequest
/を使用して GET および POST リクエストを行う基本的なクラスがありますHttpWebResponse
。
クラスを使用して API にログインし、データを要求します。Windows 8 の "Metro" アプリケーションでは、期待どおりに動作します。Windows Phone 8 アプリケーションでは、ログインは成功したように見えますが、その後のデータ要求では Cookie が送信されず、サーバーはクライアントがログインしていないかのように応答します。
クラスは次のとおりです。これとまったく同じコードが Windows 8 アプリと Windows Phone アプリで使用されています。
class Class1
{
CookieContainer cookieJar = new CookieContainer();
CookieCollection responseCookies = new CookieCollection();
public async Task<string> httpRequest(HttpWebRequest request)
{
string received;
using (var response = (HttpWebResponse)(await Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null)))
{
using (var responseStream = response.GetResponseStream())
{
using (var sr = new StreamReader(responseStream))
{
cookieJar = request.CookieContainer;
responseCookies = response.Cookies;
received = await sr.ReadToEndAsync();
}
}
}
return received;
}
public async Task<string> get(string path)
{
var request = WebRequest.Create(new Uri(path)) as HttpWebRequest;
request.CookieContainer = cookieJar;
return await httpRequest(request);
}
public async Task<string> post(string path, string postdata)
{
var request = WebRequest.Create(new Uri(path)) as HttpWebRequest;
request.Method = "POST";
request.CookieContainer = cookieJar;
byte[] data = Encoding.UTF8.GetBytes(postdata);
using (var requestStream = await Task<Stream>.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, null))
{
await requestStream.WriteAsync(data, 0, data.Length);
}
return await httpRequest(request);
}
}
そして、リクエストを開始するコード:
var n = new Class1();
await n.post("https://mydomain.com/api/login/", "username=myusername&password=mypassword");
await n.get("https://mydomain.com/reader/feeds/");
奇妙なことに、ドメイン名の前に「www.」を付けると、Windows Phone 8 アプリと Windows 8 Metro アプリの両方で機能します。
ドメインの扱い方に関係があると思います。Cookie のドメインは「.mydomain.com」であり、プレフィックスがなければ、Cookie はそのドメインに属していないと見なす必要があります。いくつかの検索の後、誰かが同様の問題に気付いたというレポートを見つけました。
私が理解していないのは、Windows 8 アプリと Windows Phone アプリでこれが異なる方法で処理される理由です。そのため、1 行ごとに同じコードが 1 つのプラットフォームでは機能するが、別のプラットフォームでは失敗します。
私はこれをさらに掘り下げました。
これに使用したサーバー コードは PHP です。
<?php
if ($_REQUEST["what"] == "set")
{
setcookie("TestCookie",$_REQUEST["username"] . " " . $_REQUEST["password"], time()+3600*24, "/", "subd.mydomain.com");
}
if ($_GET["what"] == "get")
{
var_dump($_COOKIE);
C# のクライアント コード:
var n = new ClassLibrary1.Class1();
await n.get("http://subd.mydomain.com/?what=set&username=foo&password=bar");
await n.get("http://subd.mydomain.com/?what=get");
サーバーからの Cookie 応答の例を次に示します。
Set-Cookie: ARRAffinity=295612ca; Path=/;Domain=subd.mydomain.com
Set-Cookie: TestCookie=foo+bar; expires=Fri, 04-Jan-2013 17:19:25 GMT; path=/; domain=subd.mydomain.com
Windows 8 Store/Metro アプリでは、これが結果です。
array(2) {
["ARRAffinity"]=>
string(8) "295612ca"
["TestCookie"]=>
string(7) "foo bar"
}
Windows Phone アプリでは、これが結果です。
array(0){
}
このように設定されている場合、Windows Phone は Cookie を認識しません。
の設定方法を変更するTestCookie
と、サーバーからの結果は次のようになります。
Set-Cookie: ARRAffinity=295612ca;Path=/;Domain=subd.mydomain.com
Set-Cookie: TestCookie=foo+bar; expires=Fri, 04-Jan-2013 17:29:59 GMT; path=/
TestCookie
はドメインを明示的に設定しなくなりました。ARRAffinity は変更されていません。
Windows 8 Store/Metro アプリは、これを返すようになりました:
array(2) {
["TestCookie"]=>
string(7) "foo bar"
["ARRAffinity"]=>
string(8) "295612ca"
}
Windows Phone 8 アプリは、これを返します。
array(1) {
["TestCookie"]=>
string(7) "foo bar"
}
ARRAffinity
ドメインを明示的に宣言しているため、Cookie は送信されません。
いくつかのブレークポイントを割り当てCookieContainer
てリクエストを確認すると、m_domainTable
+ [0] {[.subd.mydomain.com, System.Net.PathList]} System.Collections.Generic.KeyValuePair<string,System.Net.PathList>
+ [1] {[subd.mydomain.com, System.Net.PathList]} System.Collections.Generic.KeyValuePair<string,System.Net.PathList>
送信されない Cookie は.subd.mydomain.com
コンテナー内にあります。これは、Windows 8 と Windows Phone 8 の両方で同じです。
ただし、Cookie が次のように宣言されている場合:
Set-Cookie: TestCookie=foo+bar; expires=Fri, 04-Jan-2013 17:19:25 GMT; path=/; domain=.mydomain.com
Windows Phone 8 では正しく送信されます。
私の最初のケースでは、mydomain.com または www.mydomain.com のどちらからアクセスされたかに関係なく、サーバーは Cookie を同じ方法で宣言します。「.mydomain.com」と同じですが、Windows Phone 8 は「.mydomain.com」の Cookie を「mydomain.com」に送信する必要があるとは考えていないようです。これには問題があります。サーバーが「subd.mydomain.com」をドメインとして設定しても、前にドットがあるものとして扱われ、それ自体の障害がなければ機能しないためです。Cookie を正しく処理するには、ドメイン情報を Cookie と一緒に送信しないようにする必要があるようです。