You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Umbraco 13后台搜索重写:能否替换Examine驱动的SearchAll API调用?

重写Umbraco 13后台Examine搜索功能的实现方案

完全可以重写Umbraco 13后台的搜索逻辑,替换默认的/umbraco/backoffice/umbracoapi/entity/SearchAll接口调用。下面是几种可行的实现方式:

1. 覆盖默认的EntityApiController

Umbraco的后台API基于ASP.NET Core WebAPI,默认搜索逻辑在EntityApiController的SearchAll方法中。你可以创建自定义控制器继承该类,重写SearchAll方法实现自定义Examine搜索。

实现步骤:

  • 创建自定义控制器,标记[PluginController("Umbraco")]确保Umbraco优先使用该控制器
  • 注入IExamineManager和必要服务,实现自定义搜索逻辑
  • 将Examine搜索结果转换为后台所需的EntitySearchResultModel格式
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.Common.Attributes;
using Examine;
using Umbraco.Cms.Core.Models.ContentEditing;

[PluginController("Umbraco")]
public class CustomEntityApiController : EntityApiController
{
    private readonly IExamineManager _examineManager;
    private readonly IContentTypeService _contentTypeService;

    public CustomEntityApiController(
        IEntityService entityService,
        ILocalizedTextService localizedTextService,
        IExamineManager examineManager,
        IContentTypeService contentTypeService)
        : base(entityService, localizedTextService)
    {
        _examineManager = examineManager;
        _contentTypeService = contentTypeService;
    }

    public override IEnumerable<EntitySearchResultModel> SearchAll(string query)
    {
        // 获取Examine搜索器(这里用内置的InternalSearcher,可按需替换)
        if (!_examineManager.TryGetSearcher("InternalSearcher", out ISearcher searcher))
            return Enumerable.Empty<EntitySearchResultModel>();

        // 执行自定义Examine查询
        var searchResults = searcher.CreateQuery()
            .ManagedQuery(query)
            .Execute();

        // 转换为后台要求的结果格式
        return searchResults.Select(result =>
        {
            if (int.TryParse(result.Id, out int nodeId) && Guid.TryParse(result.Fields["nodeKey"], out Guid nodeKey))
            {
                var contentType = _contentTypeService.Get(int.Parse(result.Fields["contentTypeId"]));
                return new EntitySearchResultModel
                {
                    Id = nodeId,
                    Key = nodeKey,
                    Name = result.Fields["nodeName"],
                    Icon = contentType?.Icon ?? "icon-document",
                    ParentId = int.Parse(result.Fields["parentId"]),
                    Path = result.Fields["path"],
                    EntityType = "Document" // 根据节点类型调整为Media/Member
                };
            }
            return null;
        }).Where(model => model != null);
    }
}

2. 使用请求拦截器(IAsyncActionFilter)

如果不想直接覆盖控制器,可以通过动作过滤器拦截目标API请求,替换搜索逻辑后直接返回结果,跳过默认控制器执行。

实现步骤:

  • 实现IAsyncActionFilter接口,检查请求路径是否为目标API
  • 提取查询参数,执行自定义Examine搜索
  • 构造结果并设置context.Result,终止默认流程
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Cms.Core.Examine;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;

public class SearchAllInterceptorFilter : IAsyncActionFilter
{
    private readonly IExamineManager _examineManager;
    private readonly IContentTypeService _contentTypeService;

    public SearchAllInterceptorFilter(IExamineManager examineManager, IContentTypeService contentTypeService)
    {
        _examineManager = examineManager;
        _contentTypeService = contentTypeService;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 匹配目标API路径
        var requestPath = context.HttpContext.Request.Path;
        if (requestPath.Equals("/umbraco/backoffice/umbracoapi/entity/SearchAll", StringComparison.OrdinalIgnoreCase)
            && context.ActionArguments.TryGetValue("query", out var queryObj)
            && queryObj is string query)
        {
            // 执行自定义搜索
            if (!_examineManager.TryGetSearcher("InternalSearcher", out ISearcher searcher))
            {
                context.Result = new Microsoft.AspNetCore.Mvc.JsonResult(Enumerable.Empty<EntitySearchResultModel>());
                return;
            }

            var searchResults = searcher.CreateQuery()
                .ManagedQuery(query)
                .Execute();

            // 转换结果格式
            var resultModels = searchResults.Select(result =>
            {
                if (int.TryParse(result.Id, out int nodeId) && Guid.TryParse(result.Fields["nodeKey"], out Guid nodeKey))
                {
                    var contentType = _contentTypeService.Get(int.Parse(result.Fields["contentTypeId"]));
                    return new EntitySearchResultModel
                    {
                        Id = nodeId,
                        Key = nodeKey,
                        Name = result.Fields["nodeName"],
                        Icon = contentType?.Icon ?? "icon-document",
                        ParentId = int.Parse(result.Fields["parentId"]),
                        Path = result.Fields["path"],
                        EntityType = "Document"
                    };
                }
                return null;
            }).Where(model => model != null);

            // 返回自定义结果,跳过默认逻辑
            context.Result = new Microsoft.AspNetCore.Mvc.JsonResult(resultModels);
            return;
        }

        // 非目标请求,继续执行默认流程
        await next();
    }
}

注册拦截器

通过Umbraco Composer注册过滤器:

using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;

public class SearchFilterComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.Services.AddMvcOptions(options =>
        {
            options.Filters.Add<SearchAllInterceptorFilter>();
        });
    }
}

3. 自定义后台搜索前端组件(可选)

如果需要完全自定义搜索UI,可以替换Umbraco后台的默认搜索组件(Umbraco 13使用React构建后台)。通过Package Manifest注册自定义React组件,替换默认搜索框,使其调用你的自定义API而非默认接口。这种方式适合深度定制搜索UI的场景,但实现复杂度较高。


内容的提问来源于stack exchange,提问作者Funky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 14:20:54