Skip to content

Commit dd887b7

Browse files
Merge branch 'main' into seo/79087-csharp-update-the-value-stored-in-a-dictionary
2 parents 996a0a1 + a203444 commit dd887b7

9 files changed

Lines changed: 119 additions & 62 deletions

File tree

‎dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/IPetService.cs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@ public interface IPetService
44
{
55
Task<PetDto?> PostAsStringContentAsync();
66

7-
Task<PetDto?> PostAsJsonAsync();
7+
Task<PetDto?> PostWithPostAsJsonAsync();
8+
9+
Task<PetDto?> PostAsJsonContentAsync();
10+
11+
Task<PetDto?> PostAsSourceGeneratedJsonAsync();
812
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<InvariantGlobalization>true</InvariantGlobalization>
88
</PropertyGroup>
99

10-
<PropertyGroup Condition=" '$(RunConfiguration)' == 'https' " />
1110
<ItemGroup>
12-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
11+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
1312
</ItemGroup>
1413

1514
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace JsonObjectsWithHttpClient;
4+
5+
[JsonSerializable(typeof(PetDto))]
6+
public partial class PetContext : JsonSerializerContext
7+
{
8+
}

‎dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetService.cs‎

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1-
using System.Net;
2-
using System.Net.Http.Headers;
1+
using System.Net.Http.Headers;
32
using System.Text;
43
using System.Text.Json;
54

65
namespace JsonObjectsWithHttpClient;
76

8-
public class PetService : IPetService
7+
public class PetService(HttpClient httpClient) : IPetService
98
{
10-
private readonly HttpClient _httpClient;
11-
12-
public PetService(HttpClient httpClient)
13-
{
14-
_httpClient = httpClient;
15-
}
16-
179
public async Task<PetDto?> PostAsStringContentAsync()
1810
{
1911
var petData = CreatePet();
@@ -22,29 +14,43 @@ public PetService(HttpClient httpClient)
2214

2315
var request = new HttpRequestMessage(HttpMethod.Post, "pet");
2416
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
25-
request.Content = new StringContent(pet, Encoding.UTF8);
26-
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
17+
request.Content = new StringContent(pet, Encoding.UTF8, "application/json");
2718

28-
var response = await _httpClient.SendAsync(request);
19+
var response = await httpClient.SendAsync(request);
2920
response.EnsureSuccessStatusCode();
3021

31-
var content = await response.Content.ReadAsStringAsync();
32-
var createdPet = JsonSerializer.Deserialize<PetDto>(content);
22+
return await response.Content.ReadFromJsonAsync<PetDto>();
23+
}
3324

34-
return createdPet;
25+
public async Task<PetDto?> PostWithPostAsJsonAsync()
26+
{
27+
var petData = CreatePet();
28+
29+
var response = await httpClient.PostAsJsonAsync("pet", petData);
30+
response.EnsureSuccessStatusCode();
31+
32+
return await response.Content.ReadFromJsonAsync<PetDto>();
3533
}
36-
37-
public async Task<PetDto?> PostAsJsonAsync()
34+
35+
public async Task<PetDto?> PostAsJsonContentAsync()
3836
{
3937
var petData = CreatePet();
4038

41-
var response = await _httpClient.PostAsJsonAsync("pet", petData);
39+
using var content = JsonContent.Create(petData);
40+
var response = await httpClient.PostAsync("pet", content);
4241
response.EnsureSuccessStatusCode();
4342

44-
var content = await response.Content.ReadAsStringAsync();
45-
var createdPet = JsonSerializer.Deserialize<PetDto>(content);
43+
return await response.Content.ReadFromJsonAsync<PetDto>();
44+
}
45+
46+
public async Task<PetDto?> PostAsSourceGeneratedJsonAsync()
47+
{
48+
var petData = CreatePet();
49+
50+
var response = await httpClient.PostAsJsonAsync("pet", petData, PetContext.Default.PetDto);
51+
response.EnsureSuccessStatusCode();
4652

47-
return createdPet;
53+
return await response.Content.ReadFromJsonAsync(PetContext.Default.PetDto);
4854
}
4955

5056
private PetDto CreatePet()

‎dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Program.cs‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
var builder = WebApplication.CreateBuilder(args);
44

5-
builder.Services.AddScoped<IPetService, PetService>();
6-
builder.Services.AddEndpointsApiExplorer();
7-
builder.Services.AddSwaggerGen();
5+
builder.Services.AddOpenApi();
86
builder.Services.AddHttpClient<IPetService, PetService>(client =>
97
{
108
client.BaseAddress = new Uri("https://petstore.swagger.io/v2/");
@@ -14,14 +12,19 @@
1412

1513
if (app.Environment.IsDevelopment())
1614
{
17-
app.UseSwagger();
18-
app.UseSwaggerUI();
15+
app.MapOpenApi();
1916
}
2017

2118
app.MapPost("/postAsStringContent", async (IPetService petService)
2219
=> await petService.PostAsStringContentAsync());
2320

2421
app.MapPost("/postAsJson", async (IPetService petService)
25-
=> await petService.PostAsJsonAsync());
22+
=> await petService.PostWithPostAsJsonAsync());
23+
24+
app.MapPost("/postAsJsonContent", async (IPetService petService)
25+
=> await petService.PostAsJsonContentAsync());
26+
27+
app.MapPost("/postAsSourceGeneratedJson", async (IPetService petService)
28+
=> await petService.PostAsSourceGeneratedJsonAsync());
2629

2730
app.Run();

‎dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Properties/launchSettings.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"https": {
55
"commandName": "Project",
66
"launchBrowser": true,
7-
"launchUrl": "swagger",
7+
"launchUrl": "openapi/v1.json",
88
"applicationUrl": "https://localhost:7072;http://localhost:5229",
99
"environmentVariables": {
1010
"ASPNETCORE_ENVIRONMENT": "Development"

‎dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/GlobalUsings.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
global using System.Net;
2+
global using System.Net.Http.Json;
3+
global using System.Text;
24
global using JsonObjectsWithHttpClient;
35
global using Moq;
46
global using Moq.Protected;

‎dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/PetTest.cs‎

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,91 @@ public class PetTest
44
{
55
private const string _baseAddress = "https://mockdomain.mock";
66

7-
[Fact]
8-
public async void GivenPetObjectHasValues_WhenPostAsStringContentIsCalled_ThenPetResultIsReturned()
7+
private static (HttpClient Client, List<string?> ContentTypes) CreateClient()
98
{
9+
var contentTypes = new List<string?>();
1010
var httpMessageHandlerMock = new Mock<HttpMessageHandler>();
1111

1212
httpMessageHandlerMock.Protected()
1313
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(r => r.RequestUri!.AbsoluteUri == $"{_baseAddress}/pet"), ItExpr.IsAny<CancellationToken>())
14-
.ReturnsAsync(new HttpResponseMessage
14+
.Callback<HttpRequestMessage, CancellationToken>((request, _) => contentTypes.Add(request.Content?.Headers.ContentType?.ToString()))
15+
.ReturnsAsync(() => new HttpResponseMessage
1516
{
1617
StatusCode = HttpStatusCode.OK,
17-
Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}")
18+
Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}", Encoding.UTF8, "application/json")
1819
});
1920

20-
var httpClientMock = new HttpClient(httpMessageHandlerMock.Object)
21+
var httpClient = new HttpClient(httpMessageHandlerMock.Object)
2122
{
2223
BaseAddress = new Uri(_baseAddress)
2324
};
2425

25-
var successResult = await new PetService(httpClientMock)
26+
return (httpClient, contentTypes);
27+
}
28+
29+
[Fact]
30+
public async Task GivenPetObjectHasValues_WhenPostAsStringContentIsCalled_ThenPetResultIsReturned()
31+
{
32+
var (httpClient, contentTypes) = CreateClient();
33+
34+
var successResult = await new PetService(httpClient)
2635
.PostAsStringContentAsync();
2736

2837
Assert.NotNull(successResult);
2938
Assert.Equal(12, successResult!.Id);
30-
3139
Assert.Equal("German Shepherd", successResult!.Name);
40+
Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes));
3241
}
3342

3443
[Fact]
35-
public async void GivenPetObjectHasValues_WhenPostAsJsonIsCalled_ThenPetResultIsReturned()
44+
public async Task GivenPetObjectHasValues_WhenPostWithPostAsJsonIsCalled_ThenPetResultIsReturned()
3645
{
37-
var httpMessageHandlerMock = new Mock<HttpMessageHandler>();
46+
var (httpClient, contentTypes) = CreateClient();
3847

39-
httpMessageHandlerMock.Protected()
40-
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(r => r.RequestUri!.AbsoluteUri == $"{_baseAddress}/pet"), ItExpr.IsAny<CancellationToken>())
41-
.ReturnsAsync(new HttpResponseMessage
42-
{
43-
StatusCode = HttpStatusCode.OK,
44-
Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}")
45-
});
48+
var successResult = await new PetService(httpClient)
49+
.PostWithPostAsJsonAsync();
4650

47-
var httpClientMock = new HttpClient(httpMessageHandlerMock.Object)
48-
{
49-
BaseAddress = new Uri(_baseAddress)
50-
};
51-
52-
var successResult = await new PetService(httpClientMock)
53-
.PostAsJsonAsync();
51+
Assert.NotNull(successResult);
52+
Assert.Equal(12, successResult!.Id);
53+
Assert.Equal("German Shepherd", successResult!.Name);
54+
Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes));
55+
}
56+
57+
[Fact]
58+
public async Task GivenPetObjectHasValues_WhenPostAsJsonContentIsCalled_ThenPetResultIsReturned()
59+
{
60+
var (httpClient, contentTypes) = CreateClient();
61+
62+
var successResult = await new PetService(httpClient)
63+
.PostAsJsonContentAsync();
5464

5565
Assert.NotNull(successResult);
5666
Assert.Equal(12, successResult!.Id);
67+
Assert.Equal("German Shepherd", successResult!.Name);
68+
Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes));
69+
}
70+
71+
[Fact]
72+
public async Task GivenPetObjectHasValues_WhenPostAsSourceGeneratedJsonIsCalled_ThenPetResultIsReturned()
73+
{
74+
var (httpClient, contentTypes) = CreateClient();
75+
76+
var successResult = await new PetService(httpClient)
77+
.PostAsSourceGeneratedJsonAsync();
5778

79+
Assert.NotNull(successResult);
80+
Assert.Equal(12, successResult!.Id);
5881
Assert.Equal("German Shepherd", successResult!.Name);
82+
Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes));
83+
}
84+
85+
[Fact]
86+
public void GivenStringContent_WhenMediaTypeIsOmitted_ThenContentTypeIsTextPlain()
87+
{
88+
var withoutMediaType = new StringContent("{}", Encoding.UTF8);
89+
var withMediaType = new StringContent("{}", Encoding.UTF8, "application/json");
90+
91+
Assert.Equal("text/plain; charset=utf-8", withoutMediaType.Headers.ContentType!.ToString());
92+
Assert.Equal("application/json; charset=utf-8", withMediaType.Headers.ContentType!.ToString());
5993
}
6094
}

‎dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/Tests.csproj‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

@@ -10,17 +10,18 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
14-
<PackageReference Include="xunit" Version="2.4.2" />
15-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
14+
<PackageReference Include="xunit" Version="2.9.3" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>
19-
<PackageReference Include="coverlet.collector" Version="6.0.0">
19+
<PackageReference Include="coverlet.collector" Version="10.1.0">
2020
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2121
<PrivateAssets>all</PrivateAssets>
2222
</PackageReference>
23-
<PackageReference Include="Moq" Version="4.20.70" />
23+
<!-- Moq stays at 4.18.4: versions from 4.20.0 on ship the SponsorLink analyzer. -->
24+
<PackageReference Include="Moq" Version="4.18.4" />
2425
</ItemGroup>
2526

2627
<ItemGroup>

0 commit comments

Comments
 (0)