diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/IPetService.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/IPetService.cs index 958d814c22..3e9b5df1bf 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/IPetService.cs +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/IPetService.cs @@ -4,5 +4,9 @@ public interface IPetService { Task PostAsStringContentAsync(); - Task PostAsJsonAsync(); + Task PostWithPostAsJsonAsync(); + + Task PostAsJsonContentAsync(); + + Task PostAsSourceGeneratedJsonAsync(); } \ No newline at end of file diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient.csproj b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient.csproj index 66b0ae6aa0..f95b959309 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient.csproj +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient.csproj @@ -1,15 +1,14 @@ - net8.0 + net10.0 enable enable true - - + diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetContext.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetContext.cs new file mode 100644 index 0000000000..4d46aeff67 --- /dev/null +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetContext.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace JsonObjectsWithHttpClient; + +[JsonSerializable(typeof(PetDto))] +public partial class PetContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetService.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetService.cs index 5370dbe89d..fb19a791fc 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetService.cs +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/PetService.cs @@ -1,19 +1,11 @@ -using System.Net; -using System.Net.Http.Headers; +using System.Net.Http.Headers; using System.Text; using System.Text.Json; namespace JsonObjectsWithHttpClient; -public class PetService : IPetService +public class PetService(HttpClient httpClient) : IPetService { - private readonly HttpClient _httpClient; - - public PetService(HttpClient httpClient) - { - _httpClient = httpClient; - } - public async Task PostAsStringContentAsync() { var petData = CreatePet(); @@ -22,29 +14,43 @@ public PetService(HttpClient httpClient) var request = new HttpRequestMessage(HttpMethod.Post, "pet"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - request.Content = new StringContent(pet, Encoding.UTF8); - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + request.Content = new StringContent(pet, Encoding.UTF8, "application/json"); - var response = await _httpClient.SendAsync(request); + var response = await httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStringAsync(); - var createdPet = JsonSerializer.Deserialize(content); + return await response.Content.ReadFromJsonAsync(); + } - return createdPet; + public async Task PostWithPostAsJsonAsync() + { + var petData = CreatePet(); + + var response = await httpClient.PostAsJsonAsync("pet", petData); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadFromJsonAsync(); } - - public async Task PostAsJsonAsync() + + public async Task PostAsJsonContentAsync() { var petData = CreatePet(); - var response = await _httpClient.PostAsJsonAsync("pet", petData); + using var content = JsonContent.Create(petData); + var response = await httpClient.PostAsync("pet", content); response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStringAsync(); - var createdPet = JsonSerializer.Deserialize(content); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task PostAsSourceGeneratedJsonAsync() + { + var petData = CreatePet(); + + var response = await httpClient.PostAsJsonAsync("pet", petData, PetContext.Default.PetDto); + response.EnsureSuccessStatusCode(); - return createdPet; + return await response.Content.ReadFromJsonAsync(PetContext.Default.PetDto); } private PetDto CreatePet() diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Program.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Program.cs index b0ab789c30..81c0642993 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Program.cs +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Program.cs @@ -2,9 +2,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddScoped(); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddOpenApi(); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri("https://petstore.swagger.io/v2/"); @@ -14,14 +12,19 @@ if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); } app.MapPost("/postAsStringContent", async (IPetService petService) => await petService.PostAsStringContentAsync()); app.MapPost("/postAsJson", async (IPetService petService) - => await petService.PostAsJsonAsync()); + => await petService.PostWithPostAsJsonAsync()); + +app.MapPost("/postAsJsonContent", async (IPetService petService) + => await petService.PostAsJsonContentAsync()); + +app.MapPost("/postAsSourceGeneratedJson", async (IPetService petService) + => await petService.PostAsSourceGeneratedJsonAsync()); app.Run(); \ No newline at end of file diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Properties/launchSettings.json b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Properties/launchSettings.json index d181ae5c59..114a280578 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Properties/launchSettings.json +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/JsonObjectsWithHttpClient/Properties/launchSettings.json @@ -4,7 +4,7 @@ "https": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "openapi/v1.json", "applicationUrl": "https://localhost:7072;http://localhost:5229", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/GlobalUsings.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/GlobalUsings.cs index 74a1c45e41..263a2cd287 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/GlobalUsings.cs +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/GlobalUsings.cs @@ -1,4 +1,6 @@ global using System.Net; +global using System.Net.Http.Json; +global using System.Text; global using JsonObjectsWithHttpClient; global using Moq; global using Moq.Protected; diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/PetTest.cs b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/PetTest.cs index 102526cec5..3cbc49d121 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/PetTest.cs +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/PetTest.cs @@ -4,57 +4,91 @@ public class PetTest { private const string _baseAddress = "https://mockdomain.mock"; - [Fact] - public async void GivenPetObjectHasValues_WhenPostAsStringContentIsCalled_ThenPetResultIsReturned() + private static (HttpClient Client, List ContentTypes) CreateClient() { + var contentTypes = new List(); var httpMessageHandlerMock = new Mock(); httpMessageHandlerMock.Protected() .Setup>("SendAsync", ItExpr.Is(r => r.RequestUri!.AbsoluteUri == $"{_baseAddress}/pet"), ItExpr.IsAny()) - .ReturnsAsync(new HttpResponseMessage + .Callback((request, _) => contentTypes.Add(request.Content?.Headers.ContentType?.ToString())) + .ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, - Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}") + Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}", Encoding.UTF8, "application/json") }); - var httpClientMock = new HttpClient(httpMessageHandlerMock.Object) + var httpClient = new HttpClient(httpMessageHandlerMock.Object) { BaseAddress = new Uri(_baseAddress) }; - var successResult = await new PetService(httpClientMock) + return (httpClient, contentTypes); + } + + [Fact] + public async Task GivenPetObjectHasValues_WhenPostAsStringContentIsCalled_ThenPetResultIsReturned() + { + var (httpClient, contentTypes) = CreateClient(); + + var successResult = await new PetService(httpClient) .PostAsStringContentAsync(); Assert.NotNull(successResult); Assert.Equal(12, successResult!.Id); - Assert.Equal("German Shepherd", successResult!.Name); + Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes)); } [Fact] - public async void GivenPetObjectHasValues_WhenPostAsJsonIsCalled_ThenPetResultIsReturned() + public async Task GivenPetObjectHasValues_WhenPostWithPostAsJsonIsCalled_ThenPetResultIsReturned() { - var httpMessageHandlerMock = new Mock(); + var (httpClient, contentTypes) = CreateClient(); - httpMessageHandlerMock.Protected() - .Setup>("SendAsync", ItExpr.Is(r => r.RequestUri!.AbsoluteUri == $"{_baseAddress}/pet"), ItExpr.IsAny()) - .ReturnsAsync(new HttpResponseMessage - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent("{\n \"id\": 12,\n \"name\": \"German Shepherd\"\n}") - }); + var successResult = await new PetService(httpClient) + .PostWithPostAsJsonAsync(); - var httpClientMock = new HttpClient(httpMessageHandlerMock.Object) - { - BaseAddress = new Uri(_baseAddress) - }; - - var successResult = await new PetService(httpClientMock) - .PostAsJsonAsync(); + Assert.NotNull(successResult); + Assert.Equal(12, successResult!.Id); + Assert.Equal("German Shepherd", successResult!.Name); + Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes)); + } + + [Fact] + public async Task GivenPetObjectHasValues_WhenPostAsJsonContentIsCalled_ThenPetResultIsReturned() + { + var (httpClient, contentTypes) = CreateClient(); + + var successResult = await new PetService(httpClient) + .PostAsJsonContentAsync(); Assert.NotNull(successResult); Assert.Equal(12, successResult!.Id); + Assert.Equal("German Shepherd", successResult!.Name); + Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes)); + } + + [Fact] + public async Task GivenPetObjectHasValues_WhenPostAsSourceGeneratedJsonIsCalled_ThenPetResultIsReturned() + { + var (httpClient, contentTypes) = CreateClient(); + + var successResult = await new PetService(httpClient) + .PostAsSourceGeneratedJsonAsync(); + Assert.NotNull(successResult); + Assert.Equal(12, successResult!.Id); Assert.Equal("German Shepherd", successResult!.Name); + Assert.Equal("application/json; charset=utf-8", Assert.Single(contentTypes)); + } + + [Fact] + public void GivenStringContent_WhenMediaTypeIsOmitted_ThenContentTypeIsTextPlain() + { + var withoutMediaType = new StringContent("{}", Encoding.UTF8); + var withMediaType = new StringContent("{}", Encoding.UTF8, "application/json"); + + Assert.Equal("text/plain; charset=utf-8", withoutMediaType.Headers.ContentType!.ToString()); + Assert.Equal("application/json; charset=utf-8", withMediaType.Headers.ContentType!.ToString()); } } \ No newline at end of file diff --git a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/Tests.csproj b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/Tests.csproj index 4a78d33f4e..389e749286 100644 --- a/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/Tests.csproj +++ b/dotnet-client-libraries/JsonObjectsWithHttpClient/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -10,17 +10,18 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + +