Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
How to apply the Umbraco schema and operation IDs for custom Management APIs
All core Management APIs have a custom scheme for their generated OpenAPI schema and operation IDs.
This scheme is strictly opt-in to avoid affecting custom APIs by default. In this article, we'll see how to opt-in to the scheme.
If you are happy with your APIs' default schema and operation IDs, nothing is likely gained by using the Umbraco ones.
Schema IDs are handled by ISchemaIdHandler
implementations. To opt-in to the Umbraco schema IDs, we base our implementation on the core handler:
Then, we implement a composer to register the new schema ID handler:
Operation IDs follow the same pattern as schema IDs. The only difference is that the IOperationIdHandler
operates at the API level, not at the type level.
Again, to opt-in to the Umbraco operation IDs, we base our implementation on the core handler:
Then, we implement a composer to register the new operation ID handler:
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/
.
The Umbraco Backoffice API is also known as the Management API. Thus, a Backoffice API Controller is often referred to as a Management API Controller.
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.
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.
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.
AllItems is a local list:
The model for MyItem
is a basic class with an Id
and a Value
property.
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.
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.
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<MyItemApiController>
, a helper method to create a response with a 201 Created
status code and a Location
header.
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.
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.
Now we have created the custom API for our Umbraco project. Below you can see the full example of the implementation.
How to apply access policies for Management APIs
A Management API is by default available to any authorized Umbraco backoffice user.
To further restrict access we can apply access policies using the [Authorize]
attribute.
Umbraco maintains a set of built-in access policies we can leverage in our own APIs. The policy names are defined in Umbraco.Cms.Web.Common.Authorization.AuthorizationPolicies
.
For example, the following makes the API accessible only to users with Content section access:
We can also define our own access policies. Custom access policies are a great way of keeping access control in sync across multiple endpoints, as projects evolve over time.
A custom access policy is defined by means of composition.
The following access policy definition requires the user to be a member of both the Umbraco Administrators group and a custom defined group:
With the policy defined, we can apply it to the API controller:
Documenting your API controllers using Swagger in Umbraco Version 14 simplifies the creation of detailed and interactive API documentation. Adding Swagger attributes automatically generates comprehensive information about routes, parameters, and response types. This will enhance the developer experience and ensure clarity and consistency in your API documentation.
With the ApiExplorerSettings
attribute, we can put all our endpoints into a given group. This is a nice way of organizing our endpoints in the Swagger UI.
Use [ProducesResponseType] to specify the possible responses for each action method. This helps Swagger generate accurate documentation for your API. For example, in the GetItem method:
Here, [ProducesResponseType]
specifies that a 200 OK response will return a MyItem, and a 404 Not Found response will return a ProblemDetails.
To get an idea of how to document each controller method, below are some examples of how to document each operation for an API controller. The controller is from the Creating your own API article
Run your application and navigate to the Swagger UI (typically found at /swagger). Verify that your API documentation is correctly displaying the routes, parameters, and response types.
Adding new versions of custom Management APIs
All APIs register as version 1.0 by default, which means their endpoints are routed under /umbraco/management/api/v1/
.
As projects evolve, adding new versions of your APIs sometimes becomes necessary. Multiple versions of the same API can co-exist to retain backward compatibility.
APIs are versioned using attribute annotation:
[ApiVersion]
attributes on the API controllers.
[MapToApiVersion]
attributes on the API controller actions.
It is recommended to annotate all API controller actions, as well as the version 1.0 actions.
Using the API controller from the Creating your own API article as an example, we can add version 2.0 implementations of select actions:
Version 2.0 of the "get" and "create" endpoints - GetItemV2
and CreateItemV2
respectively, are added with the code above. The rest of the endpoints remain version 1.0 only.
The version 2.0 endpoints are routed under /umbraco/management/api/v2/
.
In the example above, the version 2.0 actions are added to the same API controller as their version 1.0 counterparts. If you prefer, they can be added to a new API controller instead. This will leave you with separate API controllers, one for each version of the API. See the examples below:
With the version 1.0 actions added in a controller sampled above, the version 2.0 actions are added in a new controller, as shown below.
While perhaps tempting, do not name your API controller V2
- e.g. MyItemApiVersionV2
. Due to an upstream issue in the API versioning system, this will currently cause routing issues in certain scenarios.
How to support polymorphic outputs from custom Management APIs
For security reasons, the System.Text.Json
serializer will not serialize types that are not explicitly referenced at compile time.
This can be a challenge when dealing with polymorphic API outputs. As a workaround, the Management API provides two options for enabling polymorphic outputs.
This approach requires that all output models implement the same interface - for example:
The ProducesResponseType
annotation on the endpoints must also be updated to use the interface:
This approach requires that all output models implement a common base class. The base class will define all its derived types by annotation - for example:
The ProducesResponseType
annotation on the endpoints must also be updated to use the base class:
Adding a custom Swagger document for a custom Management API
By default, all controllers based on ManagementApiControllerBase will be included in the default Management API Swagger document.
When building custom Management API controllers, sometimes it's preferable to have a dedicated Swagger document for them. Doing so is a three-step process:
Register the Swagger document with Swagger UI.
Instruct Swagger UI to utilize Umbraco authentication for the Swagger document.
Move the controllers to the Swagger document.
The following code exemplifies how to achieve the first two steps;
With this in place, the last step is to annotate the relevant API controllers with the MapToApi attribute:
Now when we visit the Swagger UI, "My item API" has its own Swagger document: