セッション トークンを送信することにより、カスタム認証メカニズムを使用しています。トークンの存在は、現在のプリンシパルを適切に設定する DelegatingHandler でチェックされます。プリンシパルが ApiController メソッドの呼び出しを承認されていない場合、コントローラーは 401 Unauthorized ステータスを送信します。RFC 2616 では、401 応答を送信するときに常に WWW-Authenticate ヘッダーを設定する必要があるため、私の DelegatingHandler がこれを処理します。
Web API セルフホストのシナリオでは、401 で応答する必要がある要求が Windows 7 で正しく機能しますが、Windows Server 2003 では、「既存の接続がリモート ホストによって強制的に閉じられました」という例外で終了します。さらに、コントローラー メソッドのブレークポイントが、Win7 では 1 回であるのに対し、W2k3 では 2 回ヒットすることに気付きました。これは、HttpClient が 401 応答を受信したときに何らかの形で要求を再試行したかのようです。
WWW-Authenticate ヘッダーのある行のコメントを外すと、プログラムは正しく動作します。コンソール アプリケーション内での最小限の再現例については、以下のコード サンプルを参照してください。
TestController.cs:
public class TestController : ApiController
{
public HttpResponseMessage Get()
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "go away");
}
}
AuthenticationHandler.cs:
public class AuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(task =>
{
HttpResponseMessage response = task.Result;
if ( response.StatusCode == HttpStatusCode.Unauthorized &&
!response.Headers.Contains("WWW-Authenticate") )
{
// comment out this line and the code works
response.Headers.Add("WWW-Authenticate", "SessionToken");
}
return response;
});
}
}
Program.cs:
static void Main(string[] args)
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://localhost:81");
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
config.MessageHandlers.Add(new AuthenticationHandler());
using ( HttpSelfHostServer server = new HttpSelfHostServer(config) )
using ( HttpClient client = new HttpClient() )
{
server.OpenAsync().Wait();
try
{
HttpResponseMessage response = client.GetAsync("http://localhost:81/api/test").Result;
Console.Out.WriteLine(response.StatusCode);
}
catch ( AggregateException ex )
{
Console.Out.WriteLine(ex.ToString());
}
server.CloseAsync().Wait();
}
Console.In.ReadLine();
}
API を正しく呼び出していますか? 何が間違っている可能性がありますか?