Performance: Add create-and-publish and update-and-publish endpoints - #21284
AndyButland wants to merge 8 commits into
Conversation
…ted, skipping validation, and DRY up the creation of the content schedule data.
There was a problem hiding this comment.
Pull request overview
This PR adds new Management API endpoints to combine create/update and publish operations into single requests, improving efficiency and developer experience. The changes introduce create-and-publish and update-and-publish endpoints that perform both operations atomically, along with a new PublishAsync overload that accepts an already-loaded content entity to avoid redundant database lookups.
Key Changes
- New combined operation endpoints that perform create/update and immediate publish in one request
- Service layer optimization with an
IContentoverload that skips redundant database queries - Extracted
BuildCultureAndScheduleModelhelper method to reduce code duplication in the service layer
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| CreateAndPublishDocumentController.cs | New controller providing POST endpoint to create and immediately publish documents in a single request |
| UpdateAndPublishDocumentController.cs | New controller providing PUT endpoint to update and immediately publish documents in a single request |
| CreateAndPublishDocumentRequestModel.cs | Request model extending CreateDocumentRequestModel with cultures property for immediate publishing |
| UpdateAndPublishDocumentRequestModel.cs | Request model extending UpdateDocumentRequestModel with cultures property for immediate publishing |
| IContentPublishingService.cs | Added new PublishAsync overload accepting IContent entity with skipValidation parameter to avoid database round-trips |
| ContentPublishingService.cs | Implemented new PublishAsync overload and extracted BuildCultureAndScheduleModel helper method for code reuse |
| ContentPublishingServiceTests.Publish.cs | Added comprehensive tests for the new PublishAsync overload with skipValidation parameter |
| PublishDocumentRequestModel.cs | Removed extra blank line (formatting cleanup) |
| PublishDocumentController.cs | Removed unused import statement (code cleanup) |
# Conflicts: # src/Umbraco.Cms.Api.Management/ViewModels/Document/PublishDocumentRequestModel.cs # src/Umbraco.Core/Services/IContentPublishingService.cs
| [ProducesResponseType(StatusCodes.Status201Created)] | ||
| [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] | ||
| [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] | ||
| public async Task<IActionResult> CreateAndPublish( |
Check failure
Code scanning / CodeQL
Missing cross-site request forgery token validation High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 months ago
In general, the fix is to ensure that any state-changing POST action that can be called from a browser with cookie-based authentication validates an anti-forgery token. In ASP.NET Core MVC/Web API controllers using [ApiController]-style patterns, the common approach is to add [ValidateAntiForgeryToken] (or the ASP.NET Core equivalent [AutoValidateAntiforgeryToken] at a broader scope) on POST actions, and ensure clients send the token with their requests.
For this specific controller, the minimal, non-breaking change is to decorate the CreateAndPublish POST action with the anti-forgery validation attribute while leaving the rest of the logic untouched. The project already uses Microsoft.AspNetCore.Mvc, which defines ValidateAntiForgeryTokenAttribute, so no new imports are needed. Concretely, in src/Umbraco.Cms.Api.Management/Controllers/Document/CreateAndPublishDocumentController.cs, just above the CreateAndPublish method (around line 48), add [ValidateAntiForgeryToken] alongside the existing attributes ([HttpPost("create-and-publish")], [MapToApiVersion("1.0")], etc.). This will cause ASP.NET Core’s anti-forgery system to validate the token whenever this method is invoked via an HTTP POST, reusing the existing infrastructure without changing the method body or signatures.
| @@ -41,6 +41,7 @@ | ||
| } | ||
|
|
||
| [HttpPost("create-and-publish")] | ||
| [ValidateAntiForgeryToken] | ||
| [MapToApiVersion("1.0")] | ||
| [ProducesResponseType(StatusCodes.Status201Created)] | ||
| [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] |
|
Closing now, as if and when introduced, will come in #22812. |
Description
With Umbraco 14+ we've split the save and publish operation into two. As a performance improvement, we might consider new API endpoints that perform create/update and publish in one operation, similar to how we do in Umbraco 13 - which may give us some options for optimization.
I've done that in a fairly simple way in this PR. Testing comparing the API calls of separate save and publish versus combined doesn't suggest much value - around 3% in a rough test - but there may be more we could do here. For now the only optimizations I've found are in:
It feels like any further benefit would require a larger change at the service level - and mean we'd have to blur the nice split we currently have between the
IContentEditingServiceandIContentPublishingService. If we did that for example we could look at putting the whole operation in a single lock and scope.If we move forward with this, there's still a task needed to actually use the new API endpoints from the backoffice.
Change Summary
POST /document/create-and-publish- Creates a document and immediately publishes itPUT /document/{id}/update-and-publish- Updates a document and immediately publishes itPublishAsync(IContent, ...)overload toIContentPublishingServicethat accepts an already-loaded content entity, avoiding an extra database round-tripskipValidationparameter to skip redundant validation when the caller has already validated the contentBuildCultureAndScheduleModelhelper method to DRY up schedule model creationReview and Testing
create-and-publishendpoint creates and publishes a document in a single requestupdate-and-publishendpoint updates and publishes a document in a single request