asp.net - webapi - web api actionresult




如何在自定義WebAPI HttpMessageHandler中安全地設置用戶主體? (2)

使用自定義MessageHandler,您可以通過調用System.ServiceModel.Channels定義的HttpRequestMessageExtensionMethods.SetUserPrincipal擴展方法來添加MS_UserPrincipal屬性:

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    var user = new GenericPrincipal(new GenericIdentity("UserID"), null);
    request.SetUserPrincipal(user);
    return base.SendAsync(request, cancellationToken);
}

請注意,這只會將此屬性添加到R​​equest的Properties集合中,它不會更改附加到ApiController的User。

對於基本身份驗證,我已經基於Darin Dimitrov在這裡回答的示例實現了一個自定義HttpMessageHandler : https://stackoverflow.com/a/11536349/270591 : HttpMessageHandler

代碼使用用戶名和角色創建GenericPrincipal類型的實例principal ,然後將此主體設置為線程的當前主體:

Thread.CurrentPrincipal = principal;

稍後在ApiController方法中,可以通過訪問控制器User屬性來讀取主體:

public class ValuesController : ApiController
{
    public void Post(TestModel model)
    {
        var user = User; // this should be the principal set in the handler
        //...
    }
}

這似乎工作正常,直到我最近添加了一個使用Task庫的自定義MediaTypeFormatter ,如下所示:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream,
    HttpContent content, IFormatterLogger formatterLogger)
{
    var task = Task.Factory.StartNew(() =>
    {
        // some formatting happens and finally a TestModel is returned,
        // simulated here by just an empty model
        return (object)new TestModel();
    });
    return task;
}

(我有這種方法從一些示例代碼中使用ReadFromStreamAsync Task.Factory.StartNew啟動任務。這是錯誤的,也許是問題的唯一原因嗎?)

現在,“有時” - 對我而言似乎是隨機的 - 控制器方法中的User主體不再是我在MessageHandler中設置的主體,即用戶名, Authenticated標誌和角色都丟失了。 原因似乎是自定義MediaTypeFormatter導致MessageHandler和控制器方法之間的線程發生更改。 我已經通過比較MessageHandler和控制器方法中的Thread.CurrentThread.ManagedThreadId的值來證實了這一點。 “有時”他們是不同的,然後校長“迷失”。

我現在看一下將Thread.CurrentPrincipal設置為以某種方式將主體安全地從自定義MessageHandler轉移到控制器方法的替代方法,並且在此博客中使用了post請求屬性:

request.Properties.Add(HttpPropertyKeys.UserPrincipalKey,
    new GenericPrincipal(identity, new string[0]));

我想測試一下,但似乎HttpPropertyKeys類(在命名空間System.Web.Http.Hosting )在最近的WebApi版本中不再具有UserPrincipalKey屬性(上週發布候選版本和最終版本) 。

我的問題是:如何更改上面的最後一個代碼段,以便與當前的WebAPI版本一起使用? 或者通常:如何在自定義MessageHandler中設置用戶主體並在控制器方法中可靠地訪問它?

編輯

here提到“ HttpPropertyKeys.UserPrincipalKey ...解析為“MS_UserPrincipal” ”,所以我嘗試使用:

request.Properties.Add("MS_UserPrincipal",
    new GenericPrincipal(identity, new string[0]));

但它不能像我預期的那樣工作: ApiController.User屬性不包含添加到上面Properties集合的主體。


要避免上下文切換,請嘗試使用TaskCompletionSource<object>而不是在自定義MediaTypeFormatter中手動啟動另一個任務:

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
    var tcs = new TaskCompletionSource<object>();

    // some formatting happens and finally a TestModel is returned,
    // simulated here by just an empty model
    var testModel = new TestModel();

    tcs.SetResult(testModel);
    return tcs.Task;
}






asp.net-web-api