Publishing: Resolve exceptions on publish branch (#20464)

* Reduce log level of image cropper converter to avoid flooding logs with expected exceptions.

* Don't run publish branch long running operation on a background thread such that UmbracoContext is available.

* Revert to background thread and use EnsureUmbracoContext to ensure we can get an IUmbracoContext in the URL providers.

* Updated tests.

* Applied suggestion from code review.

* Clarified comment.
This commit is contained in:
Andy Butland
2025-10-21 12:05:10 +02:00
committed by GitHub
parent 1ceec183a3
commit daace4b4a0
2 changed files with 14 additions and 5 deletions

View File

@@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.ContentPublishing;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Core.Web;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Services;
@@ -26,6 +27,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
private readonly IRelationService _relationService;
private readonly ILogger<ContentPublishingService> _logger;
private readonly ILongRunningOperationService _longRunningOperationService;
private readonly IUmbracoContextFactory _umbracoContextFactory;
public ContentPublishingService(
ICoreScopeProvider coreScopeProvider,
@@ -37,7 +39,8 @@ internal sealed class ContentPublishingService : IContentPublishingService
IOptionsMonitor<ContentSettings> optionsMonitor,
IRelationService relationService,
ILogger<ContentPublishingService> logger,
ILongRunningOperationService longRunningOperationService)
ILongRunningOperationService longRunningOperationService,
IUmbracoContextFactory umbracoContextFactory)
{
_coreScopeProvider = coreScopeProvider;
_contentService = contentService;
@@ -53,6 +56,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
{
_contentSettings = contentSettings;
});
_umbracoContextFactory = umbracoContextFactory;
}
/// <inheritdoc />
@@ -290,7 +294,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
return MapInternalPublishingAttempt(minimalAttempt);
}
_logger.LogInformation("Starting async background thread for publishing branch.");
_logger.LogDebug("Starting long running operation for publishing branch {Key} on background thread.", key);
Attempt<Guid, LongRunningOperationEnqueueStatus> enqueueAttempt = await _longRunningOperationService.RunAsync(
PublishBranchOperationType,
async _ => await PerformPublishBranchAsync(key, cultures, publishBranchFilter, userKey, returnContent: false),
@@ -324,6 +328,10 @@ internal sealed class ContentPublishingService : IContentPublishingService
Guid userKey,
bool returnContent)
{
// Ensure we have an UmbracoContext in case running on a background thread so operations that run in the published notification handlers
// have access to this (e.g. webhooks).
using UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext();
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
IContent? content = _contentService.GetById(key);
if (content is null)

View File

@@ -1,6 +1,7 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Models.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
@@ -53,10 +54,10 @@ public class ImageCropperValueConverter : PropertyValueConverterBase, IDeliveryA
{
value = _jsonSerializer.Deserialize<ImageCropperValue>(sourceString);
}
catch (Exception ex)
catch (JsonException ex)
{
// cannot deserialize, assume it may be a raw image URL
_logger.LogError(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString);
// Cannot deserialize, assume it may be a raw image URL.
_logger.LogDebug(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString);
value = new ImageCropperValue { Src = sourceString };
}