HttpClient.PostAsJsonAsync 使用私有成员而不是 public 属性

HttpClient.PostAsJsonAsync using private members instead of public properties

我有一个正在 POST 访问 Web API 的客户端。当收到 POST 内容时,JSON 正在使用属性的私有成员名称而不是 public 属性。 (客户端应用程序使用 Asp.Net-Web-api2 库,Web API 本身是使用 IIS7.5 上的 IHttpHandler 实现的,不是 MVC 风格的应用程序,代码也与现有用法 class 在导致我重新编码和现在奇怪行为的 TFS 事故之前)

//Object I am passing  
[Serializable]
public class Usage
{  
   private string _user;  
   private string _trackingUnit;  

   public string User {get {return _user;} set {_user = value;}}  
   public string TrackingUnit { get{return _trackingUnit;} set { _trackingUnit = value;}}
}

我使用以下

创建 post
Usage usage = new Usage();  
usage.User = "pete";
usage.TrackingUnit = "unit of work";

HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync(url, usage);

我正在使用以下

接收 POST
byte[] postData = context.Request.BinaryRead(context.Request.ContentLength);
string strData = Encoding.UTF8.GetString(postData);
Usage newUsage = JsonConvert.DeserializeObject<Usage>(strData);  

当我检查 strData 时,它看起来如下所示,这导致反序列化为 return 未填充的 Usage 对象

{"_user":"pete","_trackingUnit":"unit of work"}

我希望

{"User":"pete","TrackingUnit":"unit of work"}

备注:

如果我新建一个新的 Usage 对象并尝试在 Web 中序列化它 API 并且 return 字符串格式正确

JsonConvert.SerializeObject(usage);

{"User":"pete","TrackingUnit":"unit of work"}

此外,如果我尝试 JsonConvert.Serialize 在客户端应用程序中,它也会按预期工作

您看到的原因是您的框架使用 for JSON serialization, and somewhere in the code base the option DefaultContractResolver.IgnoreSerializableAttribute 已设置为 false。当此设置时,Json.NET 将序列化标记为 [Serializable] 的类型的 public 和私有 字段 -- 这是你的。

要确认发生这种情况的位置,我们可以检查参考来源。首先,HttpClientExtensions.PostAsJsonAsync<T>() 使用新构造的 JsonMediaTypeFormatter:

序列化为 JSON
public static Task<HttpResponseMessage> PostAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value, CancellationToken cancellationToken)
{
    return client.PostAsync(requestUri, value, new JsonMediaTypeFormatter(), cancellationToken);
}

它是基础 class BaseJsonMediaTypeFormatter in turn has an internal IContractResolver _defaultContractResolver of type JsonContractResolver,在其构造函数中设置

// Need this setting to have [Serializable] types serialized correctly
IgnoreSerializableAttribute = false;

这就是你问题的根源。

解决此问题的选项如下。

首先,如果您愿意为您的模型添加 Json.NET 依赖项,您可以将您的类型标记为 [JsonObject]

[Serializable]
[JsonObject]
public class Usage
{  
    private string _user;  
    private string _trackingUnit;  

    public string User {get {return _user;} set {_user = value;}}  
    public string TrackingUnit { get{return _trackingUnit;} set { _trackingUnit = value;}}
}

这通过重置 JsonObjectAttribute.MemberSerialization to its default value MemberSerialization.OptOut 覆盖 [Serializable]

其次,您可以使用 data contract attributes 注释您的类型,Json.NET 尊重:

[Serializable]
[DataContract]
public class Usage
{  
    private string _user;  
    private string _trackingUnit;  

    [DataMember]
    public string User {get {return _user;} set {_user = value;}}  
    [DataMember]
    public string TrackingUnit { get{return _trackingUnit;} set { _trackingUnit = value;}}
}

请注意,数据协定序列化是可选的,因此您需要注释每个要序列化的成员。

第三,您可以删除 [Serializable] -- 但在您的问题中您指出这不是一个选项。

最后,您可以基于 reference source that replace those in HttpClientExtensions 创建自己的扩展方法并使用 IgnoreSerializableAttribute = true 序列化您的对象,例如(未测试):

public static class MyHttpClientExtensions
{
    static JsonMediaTypeFormatter CreateJsonMediaTypeFormatter()
    {
        var JsonMediaTypeFormatter = new JsonMediaTypeFormatter();
        // Use the provided JsonContractResolver but reset IgnoreSerializableAttribute
        ((DefaultContractResolver)JsonMediaTypeFormatter.SerializerSettings).IgnoreSerializableAttribute = true;
        return JsonMediaTypeFormatter;
    }

    public static Task<HttpResponseMessage> MyPostAsJsonAsync<T>(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken)
    {
        return client.PostAsync(requestUri, value, CreateJsonMediaTypeFormatter(), cancellationToken);
    }       

    // Replace other JSON methods as required.
}