【问题标题】:HttpClient.PostAsJsonAsync using private members instead of public propertiesHttpClient.PostAsJsonAsync 使用私有成员而不是公共属性
【发布时间】:2018-01-27 20:53:54
【问题描述】:

我有一个正在发布到 Web API 的客户端。当收到 POST 内容时,JSON 使用属性的私有成员名称而不是公共属性。 (客户端应用程序使用的是 Asp.Net-Web-api2 库,而 Web API 本身是使用 IIS7.5 上的 IHttpHandler 实现的,不是 MVC 样式的应用程序,代码也在使用现有的 Usage 类之前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;}}
}

我使用以下方法发帖

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 时,它如下所示,这会导致反序列化返回一个未填充的 Usage 对象

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

我期待

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

注意事项:

如果我新建了一个 Usage 对象并尝试在 Web API 中对其进行序列化并返回其格式正确的字符串

JsonConvert.SerializeObject(usage);

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

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

【问题讨论】:

  • 在发布之前如何初始化“使用”对象?
  • 我已经用将要使用的代码更新了问题。
  • 您可以尝试两件事:1) 检查您的默认 JSON 序列化程序。 2)由于您可以使用Netwonsoft.Json.JsonConvert.Serialize 成功序列化,因此您可以构建自己的HttpRequestMessage 并改用SendAsync()
  • @Jasen,如果我没有解决根本问题(谢谢 dbc!),那将是一个很好的解决方法,感谢您的帮助!

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


【解决方案1】:

原因您看到这是因为您的框架使用 进行 JSON 序列化,并且在代码库的某处,选项 DefaultContractResolver.IgnoreSerializableAttribute 已设置为 false。当此设置时,Json.NET 将序列化标记为[Serializable] 的类型的公共和私有字段——你的就是这样。

要确认发生这种情况的位置,我们可以检查参考来源。首先,HttpClientExtensions.PostAsJsonAsync&lt;T&gt;() 使用新构建的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);
}

它的基类BaseJsonMediaTypeFormatter 又具有一个JsonContractResolver 类型的内部IContractResolver _defaultContractResolver,它在其构造函数中设置

// 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 重置为其默认值MemberSerialization.OptOut 来覆盖[Serializable]

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

[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 创建自己的扩展方法,替换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.
}

【讨论】:

  • 非常感谢!是的,#1 和#2 建议都解决了这个问题,我将使用 DataContract 路线。我非常感谢围绕解决方案的解释,刚开始在这个领域玩。我还没有测试 #3,但如果有机会,我也会尝试一下。
猜你喜欢
  • 2016-01-25
  • 2017-12-09
  • 2017-04-02
  • 1970-01-01
  • 2018-07-17
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
相关资源
最近更新 更多