HttpContext在通过HTTPClient.PostAsJsonAsync调用的JsonConverter中不可用
本文关键字:JsonConverter 调用 HTTPClient PostAsJsonAsync HttpContext | 更新日期: 2023-09-27 18:03:09
设置
- 我有一个WebAPI控制器,它使用HttpClient调用web端点。PostAsJsonAsync
- 假设控制器方法的响应和对web端点的请求是相同的实体类型 我已经为这个实体类型注册了一个自定义JsonConverter。我有一个用例来访问这个转换器中的HttpContext
问题:当调用转换器的WriteJson方法时,在HttpClient期间序列化实体。PostAsJsonAsync HttpContext。当前为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
构造一个适当的数据传输对象,并将它们序列化。 -
在
Entity
构造函数中缓存HttpContext
的必要信息,以便在序列化期间使用:public class Entity : IEntity { protected internal readonly IPrincipal Principal = HttpContext.Current?.User; }
缓存
HttpContext
本身可能不是一个好主意,因为文档中声明此类型的任何公共静态(在Visual Basic中共享)成员都是线程安全的。不能保证任何实例成员都是线程安全的。
-
对于
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);
这仍然会在序列化中留下对全局状态的依赖,这可能会在以后引起问题。