Update the default serializer to use System.Text.Json

This commit is contained in:
Claire Novotny 2020-11-25 11:25:47 -05:00
Родитель 75b3a6b1e3
Коммит 765ef2cac5
Не удалось извлечь подпись
24 изменённых файлов: 231 добавлений и 124 удалений

Просмотреть файл

@ -18,6 +18,7 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)buildtask.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">

Просмотреть файл

@ -3,8 +3,7 @@
<PropertyGroup>
<Product>Refit HTTP Client Factory Extensions</Product>
<Description>Refit HTTP Client Factory Extensions</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<DebugType>embedded</DebugType>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

Просмотреть файл

@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
@ -15,7 +17,7 @@ namespace Refit
/// <summary>
/// The <see cref="Lazy{T}"/> instance providing the JSON serialization settings to use
/// </summary>
private readonly Lazy<JsonSerializerSettings> jsonSerializerSettings;
readonly Lazy<JsonSerializerSettings> jsonSerializerSettings;
/// <summary>
/// Creates a new <see cref="NewtonsoftJsonContentSerializer"/> instance
@ -52,5 +54,15 @@ namespace Refit
return serializer.Deserialize<T>(jsonTextReader);
}
public string GetFieldNameForProperty(PropertyInfo propertyInfo)
{
if (propertyInfo is null)
throw new ArgumentNullException(nameof(propertyInfo));
return propertyInfo.GetCustomAttributes<JsonPropertyAttribute>(true)
.Select(a => a.PropertyName)
.FirstOrDefault();
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Product>Refit Serializer for Newtonsoft.Json ($(TargetFramework))</Product>
<Description>Refit Serializers for Newtonsoft.Json</Description>
<TargetFrameworks>netstandard2.0;netstandard2.1;net461</TargetFrameworks>
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<RootNamespace>Refit</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies"
Version="1.0.0"
PrivateAssets="All" />
<Reference Include="System.Web" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Refit\Refit.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
@ -328,10 +329,12 @@ namespace Refit.Tests
public string Foo { get; set; }
[JsonProperty(PropertyName = "b")]
[JsonPropertyName("b")]
public string Bar { get; set; }
[AliasAs("a")]
[JsonProperty(PropertyName = "z")]
[JsonPropertyName("z")]
public string Baz { get; set; }

Просмотреть файл

@ -150,7 +150,7 @@ namespace Refit.Tests
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.WithHeaders("X-Refit", "99")
.WithQueryString("param", "foo")
.Respond("application/json", "{'url': 'https://httpbin.org/get', 'args': {'param': 'foo'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get\", \"args\": {\"param\": \"foo\"}}");
var fixture = RestService.For<IUseOverloadedGenericMethods<HttpBinGet, string, int>>("https://httpbin.org/", settings);
@ -173,7 +173,7 @@ namespace Refit.Tests
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.WithHeaders("X-Refit", "foo")
.WithQueryString("param", "99")
.Respond("application/json", "{'url': 'https://httpbin.org/get', 'args': {'param': '99'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get\", \"args\": {\"param\": \"99\"}}");
var fixture = RestService.For<IUseOverloadedGenericMethods<HttpBinGet, string, int>>("https://httpbin.org/", settings);

Просмотреть файл

@ -18,7 +18,7 @@
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<ProjectReference Include="..\InterfaceStubGenerator.App\InterfaceStubGenerator.App.csproj" />
<ProjectReference Include="..\Refit.HttpClientFactory\Refit.HttpClientFactory.csproj" />
<ProjectReference Include="..\Refit\Refit.csproj" />
<ProjectReference Include="..\Refit.Newtonsoft.Json\Refit.Newtonsoft.Json.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461' ">

Просмотреть файл

@ -1857,6 +1857,7 @@ namespace Refit.Tests
using global::Refit.Buffers;
using global::Xunit;
using JsonSerializer = global::Newtonsoft.Json.JsonSerializer;
using global::System.Text.Json.Serialization;
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

Просмотреть файл

@ -1857,6 +1857,7 @@ namespace Refit.Tests
using global::Refit.Buffers;
using global::Xunit;
using JsonSerializer = global::Newtonsoft.Json.JsonSerializer;
using global::System.Text.Json.Serialization;
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

Просмотреть файл

@ -1857,6 +1857,7 @@ namespace Refit.Tests
using global::Refit.Buffers;
using global::Xunit;
using JsonSerializer = global::Newtonsoft.Json.JsonSerializer;
using global::System.Text.Json.Serialization;
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

Просмотреть файл

@ -1857,6 +1857,7 @@ namespace Refit.Tests
using global::Refit.Buffers;
using global::Xunit;
using JsonSerializer = global::Newtonsoft.Json.JsonSerializer;
using global::System.Text.Json.Serialization;
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]

Просмотреть файл

@ -14,6 +14,7 @@ using Refit.Buffers;
// for the code gen
using Xunit;
using JsonSerializer = Newtonsoft.Json.JsonSerializer;
using System.Text.Json.Serialization;
namespace Refit.Tests
{
@ -23,6 +24,7 @@ namespace Refit.Tests
public string ShortNameForAlias { get; set; }
[JsonProperty(PropertyName = "FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY")]
[JsonPropertyName("FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY")]
public string ShortNameForJsonProperty { get; set; }
}
@ -52,7 +54,7 @@ namespace Refit.Tests
public async Task JsonPropertyCanBeUsedToAliasFieldNamesInResponses()
{
mockHandler.Expect(HttpMethod.Get, "http://api/aliasTest")
.Respond("application/json", "{FIELD_WE_SHOULD_SHORTEN_WITH_ALIAS_AS: 'Hello', FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY: 'World'}");
.Respond("application/json", "{\"FIELD_WE_SHOULD_SHORTEN_WITH_ALIAS_AS\": \"Hello\", \"FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY\": \"World\"}");
var result = await fixture.GetTestObject();
@ -68,7 +70,7 @@ namespace Refit.Tests
{
mockHandler.Expect(HttpMethod.Get, "http://api/aliasTest")
.Respond("application/json", "{FIELD_WE_SHOULD_SHORTEN_WITH_ALIAS_AS: 'Hello', FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY: 'World'}");
.Respond("application/json", "{\"FIELD_WE_SHOULD_SHORTEN_WITH_ALIAS_AS\": \"Hello\", \"FIELD_WE_SHOULD_SHORTEN_WITH_JSON_PROPERTY\": \"World\"}");
var result = await fixture.GetTestObject();

Просмотреть файл

@ -1151,7 +1151,7 @@ namespace Refit.Tests
};
mockHttp.Expect(HttpMethod.Get, "https://registry.npmjs.org/congruence")
.Respond("application/json", "{ '_id':'congruence', '_rev':'rev' , 'name':'name'}");
.Respond("application/json", "{ \"_id\":\"congruence\", \"_rev\":\"rev\" , \"name\":\"name\"}");
@ -1373,37 +1373,6 @@ namespace Refit.Tests
mockHttp.VerifyNoOutstandingExpectation();
}
[Fact]
public async Task ErrorsFromApiReturnErrorContentNonAsync()
{
var mockHttp = new MockHttpMessageHandler();
var settings = new RefitSettings
{
HttpMessageHandlerFactory = () => mockHttp,
ContentSerializer = new NewtonsoftJsonContentSerializer(new JsonSerializerSettings() { ContractResolver = new SnakeCasePropertyNamesContractResolver() })
};
mockHttp.Expect(HttpMethod.Post, "https://api.github.com/users")
.Respond(HttpStatusCode.BadRequest, "application/json", "{ 'errors': [ 'error1', 'message' ]}");
var fixture = RestService.For<IGitHubApi>("https://api.github.com", settings);
var result = await Assert.ThrowsAsync<ApiException>(async () => await fixture.CreateUser(new User { Name = "foo" }));
#pragma warning disable CS0618 // Ensure that this code continues to be tested until it is removed
var errors = result.GetContentAs<ErrorResponse>();
#pragma warning restore CS0618
Assert.Contains("error1", errors.Errors);
Assert.Contains("message", errors.Errors);
mockHttp.VerifyNoOutstandingExpectation();
}
[Fact]
public void NonRefitInterfacesThrowMeaningfulExceptions()
{
@ -1444,7 +1413,7 @@ namespace Refit.Tests
mockHttp.Expect(HttpMethod.Get, "http://httpbin.org/get")
.WithHeaders("X-Refit", "99")
.WithQueryString("param", "foo")
.Respond("application/json", "{'url': 'http://httpbin.org/get?param=foo', 'args': {'param': 'foo'}, 'headers':{'X-Refit':'99'}}");
.Respond("application/json", "{\"url\": \"http://httpbin.org/get?param=foo\", \"args\": {\"param\": \"foo\"}, \"headers\":{\"X-Refit\":\"99\"}}");
@ -1494,7 +1463,7 @@ namespace Refit.Tests
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.WithHeaders("X-Refit", "99")
.Respond("application/json", "{'url': 'https://httpbin.org/get?FirstName=John&LastName=Rambo', 'args': {'FirstName': 'John', 'lName': 'Rambo'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get?FirstName=John&LastName=Rambo\", \"args\": {\"FirstName\": \"John\", \"lName\": \"Rambo\"}}");
var myParams = new MySimpleQueryParams
{
@ -1521,7 +1490,7 @@ namespace Refit.Tests
};
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.Respond("application/json", "{'url': 'https://httpbin.org/get?hardcoded=true&FirstName=John&LastName=Rambo&Addr_Zip=9999&Addr_Street=HomeStreet 99&MetaData_Age=99&MetaData_Initials=JR&MetaData_Birthday=10%2F31%2F1918 4%3A21%3A16 PM&Other=12345&Other=10%2F31%2F2017 4%3A21%3A17 PM&Other=696e8653-6671-4484-a65f-9485af95fd3a', 'args': { 'Addr_Street': 'HomeStreet 99', 'Addr_Zip': '9999', 'FirstName': 'John', 'LastName': 'Rambo', 'MetaData_Age': '99', 'MetaData_Birthday': '10/31/1981 4:32:59 PM', 'MetaData_Initials': 'JR', 'Other': ['12345','10/31/2017 4:32:59 PM','60282dd2-f79a-4400-be01-bcb0e86e7bc6'], 'hardcoded': 'true'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get?hardcoded=true&FirstName=John&LastName=Rambo&Addr_Zip=9999&Addr_Street=HomeStreet 99&MetaData_Age=99&MetaData_Initials=JR&MetaData_Birthday=10%2F31%2F1918 4%3A21%3A16 PM&Other=12345&Other=10%2F31%2F2017 4%3A21%3A17 PM&Other=696e8653-6671-4484-a65f-9485af95fd3a\", \"args\": { \"Addr_Street\": \"HomeStreet 99\", \"Addr_Zip\": \"9999\", \"FirstName\": \"John\", \"LastName\": \"Rambo\", \"MetaData_Age\": \"99\", \"MetaData_Birthday\": \"10/31/1981 4:32:59 PM\", \"MetaData_Initials\": \"JR\", \"Other\": [\"12345\",\"10/31/2017 4:32:59 PM\",\"60282dd2-f79a-4400-be01-bcb0e86e7bc6\"], \"hardcoded\": \"true\"}}");
var myParams = new MyComplexQueryParams
{
@ -1560,7 +1529,7 @@ namespace Refit.Tests
};
mockHttp.Expect(HttpMethod.Post, "https://httpbin.org/post")
.Respond("application/json", "{'url': 'https://httpbin.org/post?hardcoded=true&FirstName=John&LastName=Rambo&Addr_Zip=9999&Addr_Street=HomeStreet 99&MetaData_Age=99&MetaData_Initials=JR&MetaData_Birthday=10%2F31%2F1918 4%3A21%3A16 PM&Other=12345&Other=10%2F31%2F2017 4%3A21%3A17 PM&Other=696e8653-6671-4484-a65f-9485af95fd3a', 'args': { 'Addr_Street': 'HomeStreet 99', 'Addr_Zip': '9999', 'FirstName': 'John', 'LastName': 'Rambo', 'MetaData_Age': '99', 'MetaData_Birthday': '10/31/1981 4:32:59 PM', 'MetaData_Initials': 'JR', 'Other': ['12345','10/31/2017 4:32:59 PM','60282dd2-f79a-4400-be01-bcb0e86e7bc6'], 'hardcoded': 'true'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/post?hardcoded=true&FirstName=John&LastName=Rambo&Addr_Zip=9999&Addr_Street=HomeStreet 99&MetaData_Age=99&MetaData_Initials=JR&MetaData_Birthday=10%2F31%2F1918 4%3A21%3A16 PM&Other=12345&Other=10%2F31%2F2017 4%3A21%3A17 PM&Other=696e8653-6671-4484-a65f-9485af95fd3a\", \"args\": { \"Addr_Street\": \"HomeStreet 99\", \"Addr_Zip\": \"9999\", \"FirstName\": \"John\", \"LastName\": \"Rambo\", \"MetaData_Age\": \"99\", \"MetaData_Birthday\": \"10/31/1981 4:32:59 PM\", \"MetaData_Initials\": \"JR\", \"Other\": [\"12345\",\"10/31/2017 4:32:59 PM\",\"60282dd2-f79a-4400-be01-bcb0e86e7bc6\"], \"hardcoded\": \"true\"}}");
var myParams = new MyComplexQueryParams
{
@ -1682,7 +1651,7 @@ namespace Refit.Tests
};
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.Respond("application/json", "{'url': 'https://httpbin.org/get?hardcoded=true&FirstName=John&LastName=Rambo&Address_Zip=9999&Address_Street=HomeStreet 99', 'args': {'Address_Street': 'HomeStreet 99','Address_Zip': '9999','FirstName': 'John','LastName': 'Rambo','hardcoded': 'true'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get?hardcoded=true&FirstName=John&LastName=Rambo&Address_Zip=9999&Address_Street=HomeStreet 99\", \"args\": {\"Address_Street\": \"HomeStreet 99\",\"Address_Zip\": \"9999\",\"FirstName\": \"John\",\"LastName\": \"Rambo\",\"hardcoded\": \"true\"}}");
var myParams = new Dictionary<string, object>
{
@ -1715,7 +1684,7 @@ namespace Refit.Tests
};
mockHttp.Expect(HttpMethod.Get, "https://httpbin.org/get")
.Respond("application/json", "{'url': 'https://httpbin.org/get?search.FirstName=John&search.LastName=Rambo&search.Addr.Zip=9999&search.Addr.Street=HomeStreet 99', 'args': {'search.Addr.Street': 'HomeStreet 99','search.Addr.Zip': '9999','search.FirstName': 'John','search.LastName': 'Rambo'}}");
.Respond("application/json", "{\"url\": \"https://httpbin.org/get?search.FirstName=John&search.LastName=Rambo&search.Addr.Zip=9999&search.Addr.Street=HomeStreet 99\", \"args\": {\"search.Addr.Street\": \"HomeStreet 99\",\"search.Addr.Zip\": \"9999\",\"search.FirstName\": \"John\",\"search.LastName\": \"Rambo\"}}");
var myParams = new MyComplexQueryParams
{

Просмотреть файл

@ -91,12 +91,12 @@ namespace Refit.Tests
var settings = new RefitSettings();
Assert.NotNull(settings.ContentSerializer);
Assert.IsType<NewtonsoftJsonContentSerializer>(settings.ContentSerializer);
Assert.IsType<SystemTextJsonContentSerializer>(settings.ContentSerializer);
settings = new RefitSettings(new SystemTextJsonContentSerializer());
settings = new RefitSettings(new NewtonsoftJsonContentSerializer());
Assert.NotNull(settings.ContentSerializer);
Assert.IsType<SystemTextJsonContentSerializer>(settings.ContentSerializer);
Assert.IsType<NewtonsoftJsonContentSerializer>(settings.ContentSerializer);
}
/// <summary>

Просмотреть файл

@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InterfaceStubGenerator.Buil
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Refit.HttpClientFactory", "Refit.HttpClientFactory\Refit.HttpClientFactory.csproj", "{01AE14AC-88D1-49C4-A612-2F9B2DEB5DEA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Refit.Newtonsoft.Json", "Refit.Newtonsoft.Json\Refit.Newtonsoft.Json.csproj", "{2210E606-1C91-4EA0-8876-3B2F501F2669}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -138,6 +140,22 @@ Global
{01AE14AC-88D1-49C4-A612-2F9B2DEB5DEA}.Release|x64.Build.0 = Release|Any CPU
{01AE14AC-88D1-49C4-A612-2F9B2DEB5DEA}.Release|x86.ActiveCfg = Release|Any CPU
{01AE14AC-88D1-49C4-A612-2F9B2DEB5DEA}.Release|x86.Build.0 = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|ARM.Build.0 = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|x64.ActiveCfg = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|x64.Build.0 = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|x86.ActiveCfg = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Debug|x86.Build.0 = Debug|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|Any CPU.Build.0 = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|ARM.ActiveCfg = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|ARM.Build.0 = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|x64.ActiveCfg = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|x64.Build.0 = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|x86.ActiveCfg = Release|Any CPU
{2210E606-1C91-4EA0-8876-3B2F501F2669}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -21,12 +21,12 @@ namespace Refit
public bool HasContent => !string.IsNullOrWhiteSpace(Content);
public RefitSettings RefitSettings { get; set; }
protected ApiException(HttpRequestMessage message, HttpMethod httpMethod, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
this(CreateMessage(statusCode, reasonPhrase), message, httpMethod, statusCode, reasonPhrase, headers, refitSettings)
protected ApiException(HttpRequestMessage message, HttpMethod httpMethod, string content, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
this(CreateMessage(statusCode, reasonPhrase), message, httpMethod, content, statusCode, reasonPhrase, headers, refitSettings)
{
}
protected ApiException(string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
protected ApiException(string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, string content, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
base(exceptionMessage)
{
RequestMessage = message;
@ -35,11 +35,9 @@ namespace Refit
ReasonPhrase = reasonPhrase;
Headers = headers;
RefitSettings = refitSettings;
Content = content;
}
[Obsolete("Use GetContentAsAsync<T>() instead", false)]
public T GetContentAs<T>() => GetContentAsAsync<T>().ConfigureAwait(false).GetAwaiter().GetResult();
public async Task<T> GetContentAsAsync<T>() => HasContent ?
await RefitSettings.ContentSerializer.DeserializeAsync<T>(new StringContent(Content)).ConfigureAwait(false) :
default;
@ -56,7 +54,7 @@ namespace Refit
public static async Task<ApiException> Create(string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, HttpResponseMessage response, RefitSettings refitSettings = null)
#pragma warning restore VSTHRD200 // Use "Async" suffix for async methods
{
var exception = new ApiException(exceptionMessage, message, httpMethod, response.StatusCode, response.ReasonPhrase, response.Headers, refitSettings);
var exception = new ApiException(exceptionMessage, message, httpMethod, null, response.StatusCode, response.ReasonPhrase, response.Headers, refitSettings);
if (response.Content == null)
{
@ -71,8 +69,7 @@ namespace Refit
if (response.Content.Headers?.ContentType?.MediaType?.Equals("application/problem+json") ?? false)
{
exception = await ValidationApiException.Create(exception).ConfigureAwait(false);
exception.Content = content;
exception = ValidationApiException.Create(exception);
}
response.Content.Dispose();

Просмотреть файл

@ -5,8 +5,6 @@ using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
namespace Refit
{
/// <summary>
@ -22,8 +20,14 @@ namespace Refit
readonly IList<KeyValuePair<string, string>> formEntries = new List<KeyValuePair<string, string>>();
readonly IContentSerializer contentSerializer;
public FormValueMultimap(object source, RefitSettings settings)
{
if (settings is null)
throw new ArgumentNullException(nameof(settings));
contentSerializer = settings.ContentSerializer;
if (source == null) return;
if (source is IDictionary dictionary)
@ -120,12 +124,7 @@ namespace Refit
var name = propertyInfo.GetCustomAttributes<AliasAsAttribute>(true)
.Select(a => a.Name)
.FirstOrDefault()
?? propertyInfo.GetCustomAttributes<JsonPropertyAttribute>(true)
.Select(a => a.PropertyName)
.FirstOrDefault()
?? propertyInfo.GetCustomAttributes<JsonPropertyNameAttribute>(true)
.Select(a => a.Name)
.FirstOrDefault()
?? contentSerializer.GetFieldNameForProperty(propertyInfo)
?? propertyInfo.Name;
var qattrib = propertyInfo.GetCustomAttributes<QueryAttribute>(true)

Просмотреть файл

@ -1,49 +1,28 @@
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Refit
{
[Obsolete("Use NewtonsoftJsonContentSerializer instead", false)]
[Obsolete("Use NewtonsoftJsonContentSerializer in the Refit.Newtonsoft.Json package instead", true)]
public class JsonContentSerializer : IContentSerializer
{
readonly Lazy<JsonSerializerSettings> jsonSerializerSettings;
public JsonContentSerializer() : this(null) { }
public JsonContentSerializer(JsonSerializerSettings jsonSerializerSettings)
{
this.jsonSerializerSettings = new Lazy<JsonSerializerSettings>(() =>
{
if (jsonSerializerSettings == null)
{
if (JsonConvert.DefaultSettings == null)
{
return new JsonSerializerSettings();
}
return JsonConvert.DefaultSettings();
}
return jsonSerializerSettings;
});
}
public Task<HttpContent> SerializeAsync<T>(T item)
{
var content = new StringContent(JsonConvert.SerializeObject(item, jsonSerializerSettings.Value), Encoding.UTF8, "application/json");
return Task.FromResult((HttpContent)content);
throw new NotImplementedException();
}
public async Task<T> DeserializeAsync<T>(HttpContent content)
public Task<T> DeserializeAsync<T>(HttpContent content)
{
var serializer = JsonSerializer.Create(jsonSerializerSettings.Value);
throw new NotImplementedException();
}
using var stream = await content.ReadAsStreamAsync().ConfigureAwait(false);
using var reader = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(reader);
return serializer.Deserialize<T>(jsonTextReader);
public string GetFieldNameForProperty(PropertyInfo propertyInfo)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Refit
{
@ -18,7 +18,7 @@ namespace Refit
/// Collection of ProblemDetails extensions
/// </summary>
[JsonExtensionData]
public IDictionary<string, object> Extensions { get; } = new Dictionary<string, object>(StringComparer.Ordinal);
public IDictionary<string, object> Extensions { get; set; } = new Dictionary<string, object>(StringComparer.Ordinal);
/// <summary>
/// A URI reference that identifies the problem type.

Просмотреть файл

@ -4,11 +4,9 @@
<Product>Refit ($(TargetFramework))</Product>
<TargetFrameworks>netstandard2.0;netstandard2.1;net461</TargetFrameworks>
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Text.Json" Version="5.0.0" />
</ItemGroup>

Просмотреть файл

@ -7,20 +7,17 @@ using System.Reflection;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Refit
{
public class RefitSettings
{
JsonSerializerSettings jsonSerializerSettings;
{
/// <summary>
/// Creates a new <see cref="RefitSettings"/> instance with the default parameters
/// </summary>
public RefitSettings()
{
ContentSerializer = new NewtonsoftJsonContentSerializer();
ContentSerializer = new SystemTextJsonContentSerializer();
UrlParameterFormatter = new DefaultUrlParameterFormatter();
FormUrlEncodedParameterFormatter = new DefaultFormUrlEncodedParameterFormatter();
ExceptionFactory = new DefaultApiExceptionFactory(this).CreateAsync;
@ -64,17 +61,6 @@ namespace Refit
/// </summary>
public Func<HttpResponseMessage, Task<Exception>> ExceptionFactory { get; set; }
[Obsolete("Set RefitSettings.ContentSerializer = new NewtonsoftJsonContentSerializer(JsonSerializerSettings) instead.", false)]
public JsonSerializerSettings JsonSerializerSettings
{
get => jsonSerializerSettings;
set
{
jsonSerializerSettings = value;
ContentSerializer = new JsonContentSerializer(value);
}
}
public IContentSerializer ContentSerializer { get; set; }
public IUrlParameterFormatter UrlParameterFormatter { get; set; }
public IFormUrlEncodedParameterFormatter FormUrlEncodedParameterFormatter { get; set; }
@ -87,6 +73,13 @@ namespace Refit
Task<HttpContent> SerializeAsync<T>(T item);
Task<T> DeserializeAsync<T>(HttpContent content);
/// <summary>
/// Calculates what the field name should be for the given property. This may be affected by custom attributes the serializer understands
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
string GetFieldNameForProperty(PropertyInfo propertyInfo);
}
public interface IUrlParameterFormatter

Просмотреть файл

@ -1,11 +1,15 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Refit.Buffers;
@ -19,12 +23,21 @@ namespace Refit
/// <summary>
/// The JSON serialization options to use
/// </summary>
private readonly JsonSerializerOptions jsonSerializerOptions;
readonly JsonSerializerOptions jsonSerializerOptions;
/// <summary>
/// Creates a new <see cref="SystemTextJsonContentSerializer"/> instance
/// </summary>
public SystemTextJsonContentSerializer() : this(new JsonSerializerOptions()) { }
public SystemTextJsonContentSerializer() : this(new JsonSerializerOptions())
{
// Set some defaults
// Default to case insensitive property name matching as that's likely the behavior most users expect
jsonSerializerOptions.PropertyNameCaseInsensitive = true;
jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
jsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
}
/// <summary>
/// Creates a new <see cref="SystemTextJsonContentSerializer"/> instance with the specified parameters
@ -105,12 +118,76 @@ namespace Refit
/// <returns>A <typeparamref name="T"/> item deserialized from the UTF8 bytes within <paramref name="buffer"/></returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static T Deserialize<T>(byte[] buffer, int length, JsonSerializerOptions jsonSerializerOptions)
static T Deserialize<T>(byte[] buffer, int length, JsonSerializerOptions jsonSerializerOptions)
{
var span = new ReadOnlySpan<byte>(buffer, 0, length);
var utf8JsonReader = new Utf8JsonReader(span);
return JsonSerializer.Deserialize<T>(ref utf8JsonReader, jsonSerializerOptions);
}
public string GetFieldNameForProperty(PropertyInfo propertyInfo)
{
if (propertyInfo is null)
throw new ArgumentNullException(nameof(propertyInfo));
return propertyInfo.GetCustomAttributes<JsonPropertyNameAttribute>(true)
.Select(a => a.Name)
.FirstOrDefault();
}
}
// From https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#deserialize-inferred-types-to-object-properties
public class ObjectToInferredTypesConverter
: JsonConverter<object>
{
public override object Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out DateTime datetime))
{
return datetime;
}
return reader.GetString();
}
// Use JsonElement as fallback.
// Newtonsoft uses JArray or JObject.
using JsonDocument document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone();
}
public override void Write(
Utf8JsonWriter writer,
object objectToWrite,
JsonSerializerOptions options) =>
throw new InvalidOperationException("Should not get here.");
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace Refit
@ -9,24 +10,35 @@ namespace Refit
[Serializable]
public class ValidationApiException : ApiException
{
static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions();
static ValidationApiException()
{
SerializerOptions.PropertyNameCaseInsensitive = true;
SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
SerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
}
ValidationApiException(ApiException apiException) :
base(apiException.RequestMessage, apiException.HttpMethod, apiException.StatusCode, apiException.ReasonPhrase, apiException.Headers, apiException.RefitSettings)
base(apiException.RequestMessage, apiException.HttpMethod, apiException.Content, apiException.StatusCode, apiException.ReasonPhrase, apiException.Headers, apiException.RefitSettings)
{
}
#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods
/// <summary>
/// Creates a new instance of a ValidationException from an existing ApiException.
/// </summary>
/// <param name="exception">An instance of an ApiException to use to build a ValidationException.</param>
/// <returns>ValidationApiException</returns>
public static async Task<ValidationApiException> Create(ApiException exception)
#pragma warning restore VSTHRD200
public static ValidationApiException Create(ApiException exception)
{
if (exception is null)
throw new ArgumentNullException(nameof(exception));
if (string.IsNullOrWhiteSpace(exception.Content))
throw new ArgumentException("Content must be an 'application/problem+json' compliant json string.");
return new ValidationApiException(exception)
{
Content = await exception.GetContentAsAsync<ProblemDetails>().ConfigureAwait(false)
Content = JsonSerializer.Deserialize<ProblemDetails>(exception.Content, SerializerOptions)
};
}

Просмотреть файл

@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
@ -50,6 +52,20 @@ namespace Refit
using var reader = XmlReader.Create(input, settings.XmlReaderWriterSettings.ReaderSettings);
return (T)xmlSerializer.Deserialize(reader);
}
public string GetFieldNameForProperty(PropertyInfo propertyInfo)
{
if (propertyInfo is null)
throw new ArgumentNullException(nameof(propertyInfo));
return propertyInfo.GetCustomAttributes<XmlElementAttribute>(true)
.Select(a => a.ElementName)
.FirstOrDefault()
??
propertyInfo.GetCustomAttributes<XmlAttributeAttribute>(true)
.Select(a => a.AttributeName)
.FirstOrDefault();
}
}
public class XmlReaderWriterSettings