Replies: 2 comments
-
Hi there! Great question and I happen to have something for that. In summary, you need to:
In code, that would look like this: NoLongerExistsFeature.cs public interface INoLongerExistsFeature
{
}
public class NoLongerExistsFeature : INoLongerExistsFeature
{
// A marker class to mark that the httpcontext requests an no-longer-exists page
} NoLongerExistsMiddleware.cs public class NoLongerExistsMiddleware
{
// This marker is shared between requests, because it doesn't have any properties.
// If you add properties, please create a new instance for each request.
private static readonly INoLongerExistsFeature _marker = new NoLongerExistsFeature();
private readonly RequestDelegate _next;
public NoLongerExistsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await _next(context);
if (context.Response.StatusCode != StatusCodes.Status410Gone || context.Response.HasStarted) return;
// taken from the asp net core source code: https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
ClearHttpContext(context);
context.Response.OnStarting(ClearCacheHeadersAsync, context.Response);
await _next(context);
}
private static void ClearHttpContext(HttpContext context)
{
context.Response.Clear();
// An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
// the endpoint and route values to ensure things are re-calculated.
context.SetEndpoint(endpoint: null);
var routeValuesFeature = context.Features.Get<IRouteValuesFeature>();
if (routeValuesFeature is not null)
{
routeValuesFeature.RouteValues = null!;
}
context.Features.Set(_marker);
context.Features.Set<UmbracoRouteValues>(null);
context.Features.Set<IEndpointFeature>(null);
}
private static Task ClearCacheHeadersAsync(object state)
{
HttpResponse response = (HttpResponse)state;
var headers = response.Headers;
headers.CacheControl = "no-cache,no-store";
headers.Pragma = "no-cache";
headers.Expires = "-1";
headers.ETag = default;
response.StatusCode = StatusCodes.Status410Gone;
return Task.CompletedTask;
}
} NoLongerExistsContentFinder.cs public class NoLongerExistsContentFinder : IContentFinder
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
public NoLongerExistsContentFinder(IHttpContextAccessor httpContextAccessor, IUmbracoContextAccessor umbracoContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_umbracoContextAccessor = umbracoContextAccessor;
}
public Task<bool> TryFindContent(IPublishedRequestBuilder request)
{
if (_httpContextAccessor.GetRequiredHttpContext().Features.Get<INoLongerExistsFeature>() is null)
{
return Task.FromResult(false);
}
// at this point, we know that we are routing an no-longer-exists page
var rootNode = GetRootNode(request);
var errorPage = // <-- enter your logic here to select your 410 page
request.SetPublishedContent(errorPage);
return Task.FromResult(true);
}
private IPublishedContent GetRootNode(IPublishedRequestBuilder request)
{
var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
if (request.HasDomain())
{
return umbracoContext.Content!.GetById(request.Domain!.ContentId)!;
}
// if no domain is assigned, we have to fall back to the first root node
return umbracoContext.Content!.GetAtRoot().First();
}
} UrlTrackerReentryFilter.cs public class UrlTrackerReentryFilter
: IRequestInterceptFilter, IClientErrorFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UrlTrackerReentryFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ValueTask<bool> EvaluateCandidateAsync(HttpContext context)
{
return new ValueTask<bool>(!HasReentryFeature(context));
}
public ValueTask<bool> EvaluateCandidateAsync(Url url)
{
return new ValueTask<bool>(!HasReentryFeature(_httpContextAccessor.GetRequiredHttpContext()));
}
private static bool HasReentryFeature(HttpContext context)
{
return context.Features.Get<IExceptionHandlerPathFeature>() is not null
|| context.Features.Get<INoLongerExistsFeature>() is not null;
}
} You should register all the dependencies like this: public void Compose(IUmbracoBuilder builder)
{
builder.ContentFinders()
.Insert<NoLongerExistsContentFinder>(0);
builder.RequestInterceptFilters()!
.Append<UrlTrackerReentryFilter>();
builder.ClientErrorFilters()!
.Append<UrlTrackerReentryFilter>();
} I hope that helps! |
Beta Was this translation helpful? Give feedback.
-
Hi,
Greets, |
Beta Was this translation helpful? Give feedback.
-
Hi,
I'm using Umbraco v10.6.1 and UrlTracker 10.3.0.
When there's a redirect rule from page1 to page2, and I delete page2, UrlTracker ensures that the website returns a 410 status code. This also works when page1 still exists, and I use ForceRedirect. However, in this case, the website returns a browser error page with a 410 status code. Instead of this, I want to display a custom error page managed by Umbraco, similar to how it handles the 404 error.
How can I achieve this? I'm writing here because this is related to UrlTracker. I've attempted to do this, but I'm facing difficulties in intercepting the pipeline and replacing the content request. Even if I create a ResponseInterceptorHandler, I'm unable to intercept the current Umbraco PublishedRequest and set the PublishedContent.
Is there a simpler way to achieve this?
Thank you.
Giordano Polidoro
Beta Was this translation helpful? Give feedback.
All reactions