[英]HttpClient.PostAsJsonAsync throws TaskCanceledException
[英]HttpContext unavailable in JsonConverter called via HTTPClient.PostAsJsonAsync
設置
問題 :當調用轉換器的WriteJson方法以在HttpClient.PostAsJsonAsync期間將實體序列化時,HttpContext.Current為NULL。
但是,當在WebAPI響應中序列化實體時調用相同的流程時,上下文可用。
有人遇到過類似的問題嗎? 我不確定導致此問題的原因是什么以及可能的解決方案/解決方法。
我可以使用示例WebAPI項目來重現此行為。 以下是相關的代碼片段:
[JsonConverter(typeof(EntityConverter))]
public interface IEntity
{
}
public class Entity : IEntity
{
}
public class EntityConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// httContext is NULL when deserializing the HttpClient request entity
var httpContext = HttpContext.Current;
var principal = httpContext?.User;
Console.WriteLine("");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return new Entity();
}
public override bool CanConvert(Type objectType)
{
return typeof(Entity) == objectType;
}
}
public class ValuesController : ApiController
{
// POST api/values
public async Task<HttpResponseMessage> Post([FromBody]string value)
{
HttpClient client = new HttpClient();
var message = await client.PostAsJsonAsync("http://example.com", new Entity());
Console.WriteLine(message);
return Request.CreateResponse(HttpStatusCode.Created, new Entity());
}
}
如此答案中所述 , HttpContext.Current
實際上是線程靜態的,因此可能發生的是HttpClient.PostAsJsonAsync()
實際上是在單獨的線程上進行序列化的,該線程尚未初始化HttpContext.Current
。 盡管等待的async
任務不一定要在單獨的線程上運行,但它可能是-尤其是因為Json.NET不直接支持異步序列化,而是建議使用Task.Factory.StartNew()
。
要變通解決此問題,我建議從序列化內部刪除對全局狀態的依賴。 備選方案包括:
在ApiController
方法內部,從HttpContext
和每個Entity
構造一個適當的數據傳輸對象 ,然后對它們進行序列化。
將HttpContext
的必要信息緩存在Entity
構造函數中,以便在序列化期間使用:
public class Entity : IEntity { protected internal readonly IPrincipal Principal = HttpContext.Current?.User; }
緩存HttpContext
本身可能不是一個好主意,因為文檔指出
此類型的任何公共static(在Visual Basic中為Shared)成員都是線程安全的。 不保證任何實例成員都是線程安全的。
對於對PostAsJsonAsync()
的調用,您可以預序列化為JToken
然后發布:
var entity = new Entity(); var formatter = new System.Net.Http.Formatting.JsonMediaTypeFormatter(); // Or use GlobalConfiguration.Configuration.Formatters.JsonFormatter; var token = JToken.FromObject(entity, JsonSerializer.Create(formatter.SerializerSettings)); HttpClient client = new HttpClient(); var message = await client.PostAsJsonAsync("http://example.com", token); Console.WriteLine(message);
這仍然保留了序列化內部對全局狀態的依賴,這可能會在以后引起問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.