如何为HttpClient(WinForms)设置默认的json序列化设置

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何为HttpClient(WinForms)设置默认的json序列化设置相关的知识,希望对你有一定的参考价值。

我有一个使用Web API后端的WinForms应用程序,我希望所有的HTTP请求/响应都使用特定的json SerializerSettings。在服务器端,只需在Global.asax.cs中执行此操作即可:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.DateParseHandling = DateParseHandling.None;
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);

在客户端,我尝试了这个:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    Formatting = Formatting.Indented,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    DateParseHandling = DateParseHandling.None
}.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);

这在我明确使用JsonConvert时有用,例如:

var someVm = JsonConvert.DeserializeObject<SomeVm>(jsonString);

但是,对于使用HttpClient.GetAsync / PostAsAsync等的调用,它不会生效,从而导致异常:

将值2/24/2018 4:00:00 PM转换为类型'System.Nullable1[NodaTime.LocalDateTime]'. Path 'folderExpectedDate', line 16, position 45. Newtonsoft.Json.JsonSerializationException: Error converting value 2/24/2018 4:00:00 PM to type 'System.Nullable1 [NodaTime.LocalDateTime]'时出错。路径'folderExpectedDate',第16行,位置45. ---> System.ArgumentException:无法从System.DateTime转换或转换为NodaTime.LocalDateTime。

注意,在上面调用DeserializeObject时,导致上述异常的完全相同的JSON工作正常。

如何设置/修改HttpClient使用的默认序列化设置(我不想在每次调用中指定它 - 有几百个)?

答案

这是我提出的目前似乎运作良好(为了我的目的)而且非常干净。

从服务器读取Json响应(基于:http://nodogmablog.bryanhogan.net/2017/10/httpcontent-readasasync-with-net-core-2/):

public static class HttpContentExtensions
{
    public static async Task<T> ReadAsJsonAsync<T>(this HttpContent content, JsonSerializerSettings jsonSerializerSettings = null)
    {
        if (jsonSerializerSettings == null)
        {
            jsonSerializerSettings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                DateParseHandling = DateParseHandling.None
            }.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
        }

        string json = await content.ReadAsStringAsync();
        T value = JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
        return value;
    }
}

用法:

var someVm = await response.Content.ReadAsJsonAsync<SomeVm>().ConfigureAwait(false);    

对于Puts,Posts等:

//var response = await WebServiceUtil.HttpClient.PostAsJsonAsync("designSalesJob/new", designSalesJob).ConfigureAwait(false);
var response = await WebServiceUtil.HttpClient.PostAsync("designSalesJob/new", designSalesJob, HttpUtil.JsonFormatter).ConfigureAwait(false);

这是JsonFormatter实用程序方法(将重构上面的ReadAsJsonAsync也使用它):

public static class HttpUtil
{
    public static JsonMediaTypeFormatter JsonFormatter => new JsonMediaTypeFormatter
    {
        SerializerSettings = new JsonSerializerSettings
        {
            Formatting = Formatting.Indented,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            DateParseHandling = DateParseHandling.None
        }.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
    };
}

对此解决方案的反馈当然是受欢迎的......

另一答案

HttpContent扩展方法在HttpContentExtensions中定义,它们使用包含MediaTypeFormatterCollectionJsonMediaTypeFormatterXmlMediaTypeFormatter的私有静态new FormUrlEncodedMediaTypeFormatter

这些扩展方法允许您传递MediaTypeFormatter的自定义列表。它们使用您传递的格式化程序,或者如果您不传递任何格式化程序,它们将使用私有静态格式化程序集合。所以作为结论:

  • 您可以创建格式化程序的静态列表并设置它们,并在每次要调用方法时将其传递给方法。
  • 您可以选择使用反射设置HttpContentExtensions类的静态格式化程序集合。然后,这些扩展方法将始终使用该设置。

您可以创建一个公开DefaultMediaTypeFormatterCollection的静态私有HttpContextException属性的类:

public class HttpClientDefaults
{
    public static MediaTypeFormatterCollection MediaTypeFormatters
    {
        get
        {
            var p = typeof(HttpContentExtensions).
                GetProperty("DefaultMediaTypeFormatterCollection",
                    System.Reflection.BindingFlags.NonPublic | 
                    System.Reflection.BindingFlags.Static);
            return (MediaTypeFormatterCollection)p.GetValue(null, null);
        }
    }
}

然后在启动应用程序时,您可以在一个点上设置格式化程序:

var jsonFormatter = HttpClientDefaults.MediaTypeFormatters
    .OfType<JsonMediaTypeFormatter>().FirstOrDefault();

// Setup jsonFormatter, for example using jsonFormatter.SerializerSettings

该设置将用于从json反序列化的所有扩展方法,您无需更改调用这些方法的方式:

HttpClient client = new HttpClient();
var response = await client.GetAsync("http://localhost:58045/api/products/1");
if (response.IsSuccessStatusCode)
{
    var result = await response.Content.ReadAsAsync<Product>();
}

以上是关于如何为HttpClient(WinForms)设置默认的json序列化设置的主要内容,如果未能解决你的问题,请参考以下文章

如何为我的 HttpClient PostAsync 第二个参数设置 HttpContent?

如何为构造函数包含 HttpClient 和一些字符串的类设置一些 .NET Core 依赖注入?

如何为 HttpClient 建立合法的 URL

如何为所有浏览器设置默认搜索提供程序

如何为文本框设置数据类型[重复]

我应该如何为 dnx451 和 dnxcore50 引用 HttpClient?