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

如何在ASP.NET Core MVC中用C#反射列出所有视图及其元素?

在ASP.NET Core MVC中列出所有视图及元素信息的实现方案

可以通过反射定位项目结构+HTML解析提取元素的组合方式实现需求,因为视图(.cshtml)并非编译后的CLR类型,无法直接通过反射获取元素信息,反射主要用来辅助识别Area、Controller对应的视图文件路径,再通过HTML解析器提取元素属性。

核心思路

  1. 利用反射获取项目中所有Controller类型,结合AreaAttribute识别Area信息,构建视图文件的物理路径。
  2. 遍历所有.cshtml视图文件,使用HTML解析器(如AngleSharp)解析文件内容,提取目标元素(button、input等)的属性。
  3. 将提取的信息封装为统一模型,返回或存储。

实现步骤

1. 定义数据模型

用于存储提取的视图元素信息:

public class ViewElementInfo
{
    public string Area { get; set; }
    public string Controller { get; set; }
    public string ViewName { get; set; }
    public string ElementType { get; set; }
    public string AspFor { get; set; }
    public string Id { get; set; }
    public string Name { get; set; }
}

2. 扫描所有视图文件

通过反射获取Controller信息,定位项目中所有.cshtml文件的路径:

using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Hosting;

public class ViewScanner
{
    private readonly IWebHostEnvironment _webHostEnv;
    private readonly IControllerTypeProvider _controllerTypeProvider;

    public ViewScanner(IWebHostEnvironment webHostEnv, IControllerTypeProvider controllerTypeProvider)
    {
        _webHostEnv = webHostEnv;
        _controllerTypeProvider = controllerTypeProvider;
    }

    public IEnumerable<string> GetAllViewPaths()
    {
        var viewPaths = new List<string>();
        var rootPath = _webHostEnv.ContentRootPath;
        var controllerTypes = _controllerTypeProvider.ControllerTypes;

        foreach (var controllerType in controllerTypes)
        {
            // 提取Area名称
            var areaAttr = controllerType.GetCustomAttribute<AreaAttribute>();
            string areaName = areaAttr?.RouteValue;

            // 提取Controller名称(移除Controller后缀)
            string controllerName = controllerType.Name.Replace("Controller", "", StringComparison.OrdinalIgnoreCase);

            // 构建Controller对应的视图目录
            string viewDirPath = string.IsNullOrEmpty(areaName)
                ? Path.Combine(rootPath, "Views", controllerName)
                : Path.Combine(rootPath, "Areas", areaName, "Views", controllerName);

            if (Directory.Exists(viewDirPath))
            {
                viewPaths.AddRange(Directory.GetFiles(viewDirPath, "*.cshtml", SearchOption.AllDirectories));
            }

            // 添加共享视图路径
            string sharedViewDir = string.IsNullOrEmpty(areaName)
                ? Path.Combine(rootPath, "Views", "Shared")
                : Path.Combine(rootPath, "Areas", areaName, "Views", "Shared");

            if (Directory.Exists(sharedViewDir))
            {
                viewPaths.AddRange(Directory.GetFiles(sharedViewDir, "*.cshtml", SearchOption.AllDirectories));
            }
        }

        return viewPaths.Distinct();
    }
}

3. 解析视图元素

使用AngleSharp解析视图内容,提取目标元素及属性(需先安装NuGet包:Install-Package AngleSharp):

using AngleSharp;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Hosting;

public class ViewElementParser
{
    public async Task<List<ViewElementInfo>> ParseViewElements(string viewPath, IWebHostEnvironment webHostEnv)
    {
        var elements = new List<ViewElementInfo>();
        var rootPath = webHostEnv.ContentRootPath;
        var relativePath = Path.GetRelativePath(rootPath, viewPath);
        var pathSegments = relativePath.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);

        string area = null;
        string controller = null;
        string viewName = Path.GetFileNameWithoutExtension(viewPath);

        // 解析Area信息
        if (pathSegments.Contains("Areas"))
        {
            int areaIndex = Array.IndexOf(pathSegments, "Areas");
            area = pathSegments[areaIndex + 1];

            int viewsIndex = Array.IndexOf(pathSegments, "Views");
            if (viewsIndex != -1 && viewsIndex + 1 < pathSegments.Length 
                && !pathSegments[viewsIndex + 1].Equals("Shared", StringComparison.OrdinalIgnoreCase))
            {
                controller = pathSegments[viewsIndex + 1];
            }
        }
        else
        {
            int viewsIndex = Array.IndexOf(pathSegments, "Views");
            if (viewsIndex != -1 && viewsIndex + 1 < pathSegments.Length 
                && !pathSegments[viewsIndex + 1].Equals("Shared", StringComparison.OrdinalIgnoreCase))
            {
                controller = pathSegments[viewsIndex + 1];
            }
        }

        // 读取并解析视图内容
        var htmlContent = await File.ReadAllTextAsync(viewPath);
        var config = Configuration.Default;
        using var context = BrowsingContext.New(config);
        var document = await context.OpenAsync(req => req.Content(htmlContent));

        // 提取button和input元素
        var targetElements = document.QuerySelectorAll("button, input");
        foreach (var element in targetElements)
        {
            elements.Add(new ViewElementInfo
            {
                Area = area,
                Controller = controller,
                ViewName = viewName,
                ElementType = element.TagName.ToLower(),
                AspFor = element.GetAttribute("asp-for"),
                Id = element.GetAttribute("id"),
                Name = element.GetAttribute("name")
            });
        }

        return elements;
    }
}

4. 注册服务与使用

在Program.cs中注册所需服务:

builder.Services.AddSingleton<IControllerTypeProvider, DefaultControllerTypeProvider>();
builder.Services.AddScoped<ViewScanner>();
builder.Services.AddScoped<ViewElementParser>();

创建API接口用于获取结果:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/views")]
public class ViewElementsController : ControllerBase
{
    private readonly ViewScanner _viewScanner;
    private readonly ViewElementParser _elementParser;
    private readonly IWebHostEnvironment _webHostEnv;

    public ViewElementsController(ViewScanner viewScanner, ViewElementParser elementParser, IWebHostEnvironment webHostEnv)
    {
        _viewScanner = viewScanner;
        _elementParser = elementParser;
        _webHostEnv = webHostEnv;
    }

    [HttpGet("elements")]
    public async Task<IActionResult> GetAllViewElements()
    {
        var viewPaths = _viewScanner.GetAllViewPaths();
        var allElements = new List<ViewElementInfo>();

        foreach (var path in viewPaths)
        {
            var elements = await _elementParser.ParseViewElements(path, _webHostEnv);
            allElements.AddRange(elements);
        }

        return Ok(allElements);
    }
}

注意事项

  • 反射的作用:仅用于获取Controller的Area和名称,从而定位视图文件,无法直接提取视图元素(因为视图是Razor模板,编译后是动态类,无法通过反射获取HTML结构)。
  • AngleSharp兼容性:能正确解析Razor视图中的HTML元素,即使存在Razor代码(如@model、@if),也不会影响元素属性的提取。
  • 共享视图:共享视图的Controller字段会为null,因为它们不属于特定Controller。
  • 动态视图:运行时动态生成的视图(无物理.cshtml文件)无法被扫描到。
  • 文件权限:确保应用程序拥有读取项目视图文件的权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 10:36:01