Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions aspnetcore/blazor/call-web-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,35 @@ Laid out with indentation, spacing, and unescaped quotes, the unencoded PATCH do

To simplify the creation of PATCH documents in the app issuing PATCH requests, an app can use .NET JSON PATCH support, as the following guidance demonstrates.

:::moniker-end

:::moniker range=">= aspnetcore-10.0"

<!-- UPDATE 10.0 - API doc cross-link -->

Install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package and use the API features of the package to compose a `JsonPatchDocument` for a PATCH request.

[!INCLUDE[](~/includes/package-reference.md)]

Add `@using` directives for the <xref:System.Text.Json?displayProperty=fullName>, <xref:System.Text.Json.Serialization?displayProperty=fullName>, and `Microsoft.AspNetCore.JsonPatch.SystemTextJson` <!-- <xref:Microsoft.AspNetCore.JsonPatch.SystemTextJson?displayProperty=fullName> --> namespaces to the top of the Razor component:

```razor
@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch.SystemTextJson
```

Compose the `JsonPatchDocument` for a `TodoItem` with `IsComplete` set to `true` using the `JsonPatchDocument.Replace` method:

```csharp
var patchDocument = new JsonPatchDocument<TodoItem>()
.Replace(p => p.IsComplete, true);
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-10.0"

Install the [`Microsoft.AspNetCore.JsonPatch`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch) NuGet package and use the API features of the package to compose a <xref:Microsoft.AspNetCore.JsonPatch.JsonPatchDocument> for a PATCH request.

[!INCLUDE[](~/includes/package-reference.md)]
Expand All @@ -695,6 +724,10 @@ var patchDocument = new JsonPatchDocument<TodoItem>()
.Replace(p => p.IsComplete, true);
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0"

Pass the document's operations (`patchDocument.Operations`) to the <xref:System.Net.Http.Json.HttpClientJsonExtensions.PatchAsJsonAsync%2A> call:

```csharp
Expand All @@ -716,6 +749,24 @@ Add <xref:System.Text.Json.JsonSerializerOptions.WriteIndented?displayProperty=n

Follow the guidance in the <xref:web-api/jsonpatch> article to add a PATCH controller action to the web API. Alternatively, PATCH request processing can be implemented as a [Minimal API](xref:fundamentals/minimal-apis) with the following steps.

:::moniker-end

:::moniker range=">= aspnetcore-10.0"

<!-- UPDATE 10.0 - API doc cross-link -->

Add a package reference for the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson) NuGet package to the web API app.

In the `Program` file add an `@using` directive for the `Microsoft.AspNetCore.JsonPatch.SystemTextJson` <!-- <xref:Microsoft.AspNetCore.JsonPatch.SystemTextJson?displayProperty=fullName> --> namespace:

```csharp
using Microsoft.AspNetCore.JsonPatch.SystemTextJson;
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-10.0"

Add a package reference for the [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson) NuGet package to the web API app.

> [!NOTE]
Expand All @@ -727,6 +778,10 @@ In the `Program` file add an `@using` directive for the <xref:Microsoft.AspNetCo
using Microsoft.AspNetCore.JsonPatch;
```

:::moniker-end

:::moniker range=">= aspnetcore-7.0"

Provide the endpoint to the request processing pipeline of the web API:

```csharp
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
## Support for IOpenApiDocumentProvider in the DI container.
### Support for IOpenApiDocumentProvider in the DI container.

ASP.NET Core in .NET 10 supports [IOpenApiDocumentProvider](https://source.dot.net/#Microsoft.AspNetCore.OpenApi/Services/IOpenApiDocumentProvider.cs) in the dependency injection (DI) container. Developers can inject `IOpenApiDocumentProvider` into their apps and use it to access the OpenAPI document. This approach is useful for accessing OpenAPI documents outside the context of HTTP requests, such as in background services or custom middleware.

Previously, running application startup logic without launching an HTTP server could be done by using `HostFactoryResolver` with a no-op `IServer` implementation. The new feature simplifies this process by providing a streamlined API inspired by Aspire's <xref:Aspire.Hosting.Publishing.IDistributedApplicationPublisher>, which is part of Aspire's framework for distributed application hosting and publishing. For more information, see [Aspire Documentation](https://aspire.example.com/docs/distributed-application-publisher).

Previously, running application startup logic without launching an HTTP server could be done by using `HostFactoryResolver` with a no-op `IServer` implementation. The new feature simplifies this process by providing a streamlined API inspired by Aspire's <xref:Aspire.Hosting.Publishing.IDistributedApplicationPublisher>, which is part of Aspire's framework for distributed application hosting and publishing. For more information, see [Aspire Documentation](https://aspire.example.com/docs/distributed-application-publisher) and [dotnet/aspnetcore #61463](https://github.com/dotnet/aspnetcore/pull/61463).

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using JsonPatchSample.Models;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Dynamic;

namespace JsonPatchSample.Controllers;

[Route("jsonpatch/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
// <snippet_PatchAction>
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();

patchDoc.ApplyTo(customer, ModelState);

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
// </snippet_PatchAction>

[HttpPatch]
public IActionResult JsonPatchWithModelStateAndPrefix(
[FromBody] JsonPatchDocument<Customer> patchDoc,
string prefix)
{
var customer = CreateCustomer();

patchDoc.ApplyTo(customer, ModelState, prefix);

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

return new ObjectResult(customer);
}

[HttpPatch]
public IActionResult JsonPatchWithoutModelState([FromBody] JsonPatchDocument<Customer> patchDoc)
{
var customer = CreateCustomer();

patchDoc.ApplyTo(customer);

return new ObjectResult(customer);
}

[HttpPatch]
public IActionResult JsonPatchForProduct([FromBody] JsonPatchDocument<Product> patchDoc)
{
var product = new Product();

patchDoc.ApplyTo(product);

return new ObjectResult(product);
}

// <snippet_Dynamic>
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);

return Ok(obj);
}
// </snippet_Dynamic>

private Customer CreateCustomer()
{
return new Customer
{
CustomerName = "John",
Orders = new List<Order>()
{
new Order
{
OrderName = "Order0"
},
new Order
{
OrderName = "Order1"
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<Folder Include="Controllers\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Newtonsoft.Json;

namespace JsonPatchSample.Models
{
public class Category
{
public string CategoryName { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JsonPatchSample.Models;

public class Customer
{
public string? CustomerName { get; set; }
public List<Order>? Orders { get; set; }
}
7 changes: 7 additions & 0 deletions aspnetcore/web-api/jsonpatch/samples/10.x/api/Models/Order.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JsonPatchSample.Models;

public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace JsonPatchSample.Models
{
public class Product
{
public string ProductName { get; set; }
public Category ProductCategory { get; set; }
}
}
24 changes: 24 additions & 0 deletions aspnetcore/web-api/jsonpatch/samples/10.x/api/MyJPIF.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Options;

namespace JsonPatchSample;

public static class MyJPIF
{
public static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();

return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
}
42 changes: 42 additions & 0 deletions aspnetcore/web-api/jsonpatch/samples/10.x/api/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#define BOTH // FIRST BOTH
#if NEVER
#elif FIRST
// <snippet1>
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
.AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
// </snippet1>
#elif BOTH
// <snippet_both>
using JsonPatchSample;
using Microsoft.AspNetCore.Mvc.Formatters;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, MyJPIF.GetJsonPatchInputFormatter());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
// </snippet_both>
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Loading