【问题标题】:HttpContext unavailable in JsonConverter called via HTTPClient.PostAsJsonAsync通过 HTTPClient.PostAsJsonAsync 调用的 JsonConverter 中的 HttpContext 不可用
【发布时间】:2016-08-20 08:13:53
【问题描述】:

设置

  1. 我有一个 WebAPI 控制器,它使用 HttpClient.PostAsJsonAsync 调用 Web 端点
  2. 假设控制器方法的响应和对 Web 端点的请求是相同的实体类型
  3. 我已经为此实体类型注册了一个自定义 JsonConverter。我有一个用例来访问此转换器中的 HttpContext

问题:当调用转换器的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());
    }
}

【问题讨论】:

  • 你可以尝试使用完整的命名空间 - System.Web.HttpContext.Current

标签: c# asp.net-web-api json.net


【解决方案1】:

正如this answer 中所解释的,HttpContext.Current 实际上是线程静态的,所以可能发生的是HttpClient.PostAsJsonAsync() 实际上是在一个单独的线程上进行序列化,@ 987654327@ 尚未初始化。虽然等待的 async 任务不一定会在单独的线程上运行,但它可能是 - 特别是因为 Json.NET 不直接支持异步序列化,而是 recommends using Task.Factory.StartNew()

要解决此问题,我建议从内部序列化中删除对全局状态的依赖。替代方案包括:

  1. 在您的ApiController 方法中,从HttpContext 和每个Entity 构造一个适当的data transfer object,然后将它们序列化。

  2. Entity 构造函数中缓存来自HttpContext 的必要信息,以便在序列化期间使用:

    public class Entity : IEntity
    {
        protected internal readonly IPrincipal Principal = HttpContext.Current?.User;
    }
    

    缓存 HttpContext 本身可能不是一个好主意,因为 documentation 状态

    此类型的任何公共静态(在 Visual Basic 中为共享)成员都是线程安全的。不保证任何实例成员都是线程安全的。

  3. 对于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);
    

    这仍然会在序列化内部留下对全局状态的依赖,这可能会导致以后出现问题。

【讨论】:

    猜你喜欢
    • 2018-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    相关资源
    最近更新 更多