简体   繁体   中英

HttpContext unavailable in JsonConverter called via HTTPClient.PostAsJsonAsync

The setup

  1. I've a WebAPI Controller, that makes a call to a web endpoint, using HttpClient.PostAsJsonAsync
  2. Let's say, that the response of the controller method and the request to the web endpoint is the same entity type
  3. I've a custom JsonConverter registered for this entity type. I have a use case to access the HttpContext in this converter

The issue : when the converter's WriteJson method is invoked, to serialize the entity during HttpClient.PostAsJsonAsync, HttpContext.Current is NULL.

However, when the same flow is invoked when serializing the entity in WebAPI response the context is available fine.

Has anyone faced similar issues before? I'm not sure what the cause of this issue is and what could be possible solutions / workarounds.

I am able to re-produce this behavior with a sample WebAPI project. Here are the relevant code snips:

[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());
    }
}

As explained in this answer , HttpContext.Current is in fact thread static, so what is possibly happening is that HttpClient.PostAsJsonAsync() is actually doing the serialization on a separate thread, one on which HttpContext.Current has not been initialized. While an awaited async task will not necessarily be run on a separate thread, it might be - especially since Json.NET doesn't support asynchronous serialization directly and instead recommends using Task.Factory.StartNew() .

To work around the issue, I'd recommend removing dependency on global state from inside serialization. Alternatives include:

  1. Inside your ApiController methods, construct an appropriate data transfer object from the HttpContext and each Entity , and serialize those instead.

  2. Cache the necessary information from HttpContext inside the Entity constructor for use during serialization:

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

    Caching the HttpContext itself might not be a good idea since the documentation states

    Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

  3. For the call to PostAsJsonAsync() you could pre-serialize to a JToken then post that:

     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); 

    This still leaves the dependency on global state inside serialization, which may cause problems later on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM