From 874eef9de994bc77a856cb9dd014af08dcdfe140 Mon Sep 17 00:00:00 2001 From: kjac Date: Sat, 25 May 2024 13:48:32 +0200 Subject: [PATCH 1/2] Moved "custom backoffice APIs" to tutorials + update code snippets --- 14/umbraco-cms/SUMMARY.md | 2 +- .../creating-a-backoffice-api/README.md} | 58 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) rename 14/umbraco-cms/{fundamentals/backoffice/create-your-own-api.md => tutorials/creating-a-backoffice-api/README.md} (87%) diff --git a/14/umbraco-cms/SUMMARY.md b/14/umbraco-cms/SUMMARY.md index 3d099d60862..daee54a0929 100644 --- a/14/umbraco-cms/SUMMARY.md +++ b/14/umbraco-cms/SUMMARY.md @@ -19,7 +19,6 @@ * [Version Specific Upgrades](fundamentals/setup/upgrading/version-specific/README.md) * [Backoffice](fundamentals/backoffice/README.md) * [Document Blueprints](fundamentals/backoffice/document-blueprints.md) - * [Creating a custom API](fundamentals/backoffice/create-your-own-api.md) ## Extending @@ -107,4 +106,5 @@ * [Creating a Property Editor](tutorials/creating-a-property-editor/README.md) * [Adding configuration to a Property Editor](tutorials/creating-a-property-editor/adding-configuration-to-a-property-editor.md) * [Integrating context with a Property Editor](tutorials/creating-a-property-editor/integrating-context-with-a-property-editor.md) +* [Creating a backoffice API](tutorials/creating-a-backoffice-api/README.md) * [Implementing Custom Error Pages](tutorials/custom-error-page.md) diff --git a/14/umbraco-cms/fundamentals/backoffice/create-your-own-api.md b/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md similarity index 87% rename from 14/umbraco-cms/fundamentals/backoffice/create-your-own-api.md rename to 14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md index b541fd97d53..3a2c1f3223a 100644 --- a/14/umbraco-cms/fundamentals/backoffice/create-your-own-api.md +++ b/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md @@ -1,15 +1,16 @@ -# Creating a custom API +# Creating a backoffice API -In this article, you will learn how to create your custom API in the Umbraco backoffice. This is a great way to extend the functionality of the Umbraco backoffice and create custom endpoints for your use. +In this article, you will learn how to create your own API in the Umbraco backoffice. This is a great way to extend the functionality of the Umbraco backoffice and create custom endpoints for your use. ## Creating the class -To create a custom API, you need to create a class that inherits from `Umbraco.Cms.Web.BackOffice.Controllers.ManagementApiControllerBase`. +To create a custom API, you need to create a class that inherits from `Umbraco.Cms.Web.BackOffice.Controllers.ManagementApiControllerBase`. -The `ManagementApiControllerBase` serves as the foundation for your custom API class. It provides essential functionalities and utilities required for managing APIs within the Umbraco backoffice environment. +The `ManagementApiControllerBase` serves as the foundation for your custom API class. It provides essential functionalities and utilities required for managing APIs within the Umbraco backoffice environment. We also use the `VersionedApiBackOfficeRoute` attribute to define the route for our API. This attribute takes a string parameter that defines the route for the API. This route will be appended to the base route for the backoffice API. +{% code title="MyItemApiController.cs" %} ```csharp [VersionedApiBackOfficeRoute("my/item")] [ApiExplorerSettings(GroupName = "My item API")] @@ -17,13 +18,15 @@ public class MyItemApiController : ManagementApiControllerBase { } ``` +{% endcode %} ## Retrieving all items -Now that we have our class set up, we can add an action to get all items. We will use the `HttpGet` attribute to define the `HTTP` method and route for the action. +Now that we have our class set up, we can add an action to get all items. We will use the `HttpGet` attribute to define the `HTTP` method and route for the action. The `AllItems` field is an in-memory list of items to simulate the use of a repository. We use the `skip` & `take` parameters here, so users of this endpoint can implement paging. We also use the `PagedViewModel` to return the given items (10 by default), and then the total number of items. +{% code title="MyItemApiController.cs" %} ```csharp [HttpGet] public IActionResult GetAllItems(int skip = 0, int take = 10) @@ -35,17 +38,21 @@ public IActionResult GetAllItems(int skip = 0, int take = 10) } ); ``` +{% endcode %} AllItems is a local list: +{% code title="MyItemApiController.cs" %} ```csharp private static readonly List AllItems = Enumerable.Range(1, 100) .Select(i => new MyItem($"My Item #{i}")) .ToList(); ``` +{% endcode %} The model for `MyItem` is a basic class with an `Id` and a `Value` property. +{% code title="MyItem.cs" %} ```csharp public class MyItem(string value) { @@ -54,15 +61,17 @@ public class MyItem(string value) public string Value { get; set; } = value; } ``` +{% endcode %} ## Retrieving a single item -We can now create some logic to return a response based on the `ID`. The route parameter `{id:guid}` specifies that the `id` parameter should be a `GUID`. Here we're creating a local in-memory list of items and returning the item with the matching `ID`. +We can now create some logic to return a response based on the `ID`. The route parameter `{id:guid}` specifies that the `id` parameter should be a `GUID`. Here we're creating a local in-memory list of items and returning the item with the matching `ID`. -To note here is the use of the `OperationStatusResult` method. This method allows you to return a response with a status code and a body. This is useful for returning error responses with additional information. +To note here is the use of the `OperationStatusResult` method. This method allows you to return a response with a status code and a body. This is useful for returning error responses with additional information. The method also needs an `enum` operationStatus, as it will be attached to the response. This is a basic example, however, this `OperationStatus` would be returned from your service layer, based on the error in the service layer method. +{% code title="MyItemApiController.cs" %} ```csharp [HttpGet("{id:guid}")] public IActionResult GetItem(Guid id) @@ -82,7 +91,9 @@ public IActionResult GetItem(Guid id) ); } ``` +{% endcode %} +{% code title="MyItemOperationStatus.cs" %} ```csharp public enum MyItemOperationStatus { @@ -91,15 +102,17 @@ public enum MyItemOperationStatus DuplicateValue } ``` +{% endcode %} ## Creating a new item -We can now add an action to create a new item. We use the `HttpPost` attribute to define the `HTTP` method and route for the action. Here we can see some validation logic. +We can now add an action to create a new item. We use the `HttpPost` attribute to define the `HTTP` method and route for the action. Here we can see some validation logic. If the value does not start with "New", we return a `BadRequest` response with an error message. This highlights why we use the `OperationStatusResult` method. We can return a detailed response. We also use `CreatedAtId`, a helper method to create a response with a `201 Created` status code and a `Location` header. +{% code title="MyItemApiController.cs" %} ```csharp [HttpPost] public IActionResult CreateItem(string value) @@ -136,11 +149,13 @@ public IActionResult CreateItem(string value) ); } ``` +{% endcode %} ## Updating an item Now we can add an action to update an item. We can use the `HttpPut` attribute to define the `HTTP` method and route for the action. +{% code title="MyItemApiController.cs" %} ```csharp [HttpPut("{id:guid}")] public IActionResult UpdateItem(Guid id, string value) @@ -175,11 +190,13 @@ public IActionResult UpdateItem(Guid id, string value) return Ok(); } ``` +{% endcode %} ## Deleting an item Finally, we can add an action to delete an item. We can use the `HttpDelete` attribute to define the `HTTP` method and route for the action. +{% code title="MyItemApiController.cs" %} ```csharp [HttpDelete("{id:guid}")] public IActionResult DeleteItem(Guid id) @@ -203,12 +220,22 @@ public IActionResult DeleteItem(Guid id) return Ok(); } ``` +{% endcode %} Now we have created the custom API for our Umbraco project. Below you can see the full example of the implementation. ## Full implementation +{% code title="MyItemApiController.cs" %} ```csharp +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Common.ViewModels.Pagination; +using Umbraco.Cms.Api.Management.Controllers; +using Umbraco.Cms.Api.Management.Routing; + +namespace UmbracoDocs.Samples; + [VersionedApiBackOfficeRoute("my/item")] [ApiExplorerSettings(GroupName = "My item API")] public class MyItemApiController : ManagementApiControllerBase @@ -335,4 +362,19 @@ public class MyItemApiController : ManagementApiControllerBase return Ok(); } } + +public class MyItem(string value) +{ + public Guid Id { get; } = Guid.NewGuid(); + + public string Value { get; set; } = value; +} + +public enum MyItemOperationStatus +{ + NotFound, + InvalidValue, + DuplicateValue +} ``` +{% endcode %} From 6eda5f57f3d63c80128c0dd14d6d0fed73bd084e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 27 May 2024 09:00:00 +0200 Subject: [PATCH 2/2] Update 14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md Co-authored-by: Alina-Magdalena Tincas <83591955+alina-tincas@users.noreply.github.com> --- 14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md b/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md index 3a2c1f3223a..e306ad2cd15 100644 --- a/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md +++ b/14/umbraco-cms/tutorials/creating-a-backoffice-api/README.md @@ -2,6 +2,8 @@ In this article, you will learn how to create your own API in the Umbraco backoffice. This is a great way to extend the functionality of the Umbraco backoffice and create custom endpoints for your use. +The end result for this article is to create a custom API called "My item API" in the Management API found at `/umbraco/swagger/`. + ## Creating the class To create a custom API, you need to create a class that inherits from `Umbraco.Cms.Web.BackOffice.Controllers.ManagementApiControllerBase`.