代码之家  ›  专栏  ›  技术社区  ›  Luke Vo

Razor类库也可以打包静态文件(js、css等)吗?

  •  8
  • Luke Vo  · 技术社区  · 7 年前

    也许 吧 duplicate of this 已经有了,但是由于那篇文章没有任何答案,所以我要发布这个问题。

    新的 Razor Class Library 很棒,但它不能打包库文件(如jQuery、共享CSS)。

    我可以在多个Razor页面项目中重用CSS吗,可以使用Razor类库,也可以使用其他工具(我的目的是,多个网站使用相同的CSS,一个更改应用于所有项目)。

    我试过创建文件夹 wwwroot 在Razor类库项目中,但它没有按预期工作(我能理解为什么)。

    2 回复  |  直到 7 年前
        1
  •  12
  •   Ehsan Mirsaeedi    7 年前

    您需要将静态资产嵌入Razor类库程序集中。我想最好的办法就是看看 ASP.NET Identity UI source codes .

    您应该采取以下4个步骤来嵌入您的资产并为其提供服务。

    1. 编辑Razor类库的csproj文件并添加以下行。

       <PropertyGroup>
        ....
             <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
        ....
       </PropertyGroup>
      
       <ItemGroup>
           ....
           <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.2" />
           <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
           <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.1.1" />
           <PackageReference Include="Microsoft.NET.Sdk.Razor" Version="$(MicrosoftNETSdkRazorPackageVersion)" PrivateAssets="All" />
          .....
       </ItemGroup>
      
      <ItemGroup>
          <EmbeddedResource Include="wwwroot\**\*" />
          <Content Update="**\*.cshtml" Pack="false" />
      </ItemGroup>
      
    2. 在Razor类库中,创建以下类来服务和路由资产。(假设您的资产位于wwwroot文件夹中)

      public class UIConfigureOptions : IPostConfigureOptions<StaticFileOptions>
      {
          public UIConfigureOptions(IHostingEnvironment environment)
          {
              Environment = environment;
          }
          public IHostingEnvironment Environment { get; }
      
          public void PostConfigure(string name, StaticFileOptions options)
          {
              name = name ?? throw new ArgumentNullException(nameof(name));
              options = options ?? throw new ArgumentNullException(nameof(options));
      
              // Basic initialization in case the options weren't initialized by any other component
              options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider();
              if (options.FileProvider == null && Environment.WebRootFileProvider == null)
              {
                  throw new InvalidOperationException("Missing FileProvider.");
              }
      
              options.FileProvider = options.FileProvider ?? Environment.WebRootFileProvider;
      
              var basePath = "wwwroot";
      
              var filesProvider = new ManifestEmbeddedFileProvider(GetType().Assembly, basePath);
              options.FileProvider = new CompositeFileProvider(options.FileProvider, filesProvider);
          }
      }
      
    3. 使依赖的web应用程序使用Razor类库路由器。在 配置服务 方法 启动 类,添加以下行。

      services.ConfigureOptions(typeof(UIConfigureOptions));
      
    4. 所以,现在可以添加对文件的引用。(假设它位于wwwroot/js/app.bundle.js)。

      <script src="~/js/app.bundle.js" asp-append-version="true"></script>
      
        2
  •  5
  •   Luke Vo    6 年前

    Ehsan在询问.NET Core 2.2和.NET Core 3.0时的回答是正确的, RCL can include static assets 不费吹灰之力:

    若要将配套资源作为RCL的一部分包括在内,请在类库中创建一个wwwroot文件夹,并在该文件夹中包括所有必需的文件。

    打包RCL时,wwwroot文件夹中的所有相关资产都会自动包含在包中。

    RCL的wwwroot文件夹中包含的文件以前缀_content/{LIBRARY NAME}/公开给正在使用的应用程序。例如,一个名为Razor.Class.Lib的库会在_content/Razor.Class.Lib/中生成静态内容的路径。

        3
  •  1
  •   KJ.Coding    7 年前

    谢谢你提供的有用信息。

    这是一个扩展版本,允许调试javascript和typescript,并且能够在不重新编译的情况下进行更改。TypeScript调试在Chrome中不起作用,但在IE中。如果您碰巧知道原因,请发布响应。谢谢!

    public class ContentConfigureOptions : IPostConfigureOptions<StaticFileOptions>
    {
        private readonly IHostingEnvironment _environment;
    
        public ContentConfigureOptions(IHostingEnvironment environment)
        {
            _environment = environment;
        }
    
        public void PostConfigure(string name, StaticFileOptions options)
        {
            // Basic initialization in case the options weren't initialized by any other component
            options.ContentTypeProvider = options.ContentTypeProvider ?? new FileExtensionContentTypeProvider();
    
            if (options.FileProvider == null && _environment.WebRootFileProvider == null)
            {
                throw new InvalidOperationException("Missing FileProvider.");
            }
    
            options.FileProvider = options.FileProvider ?? _environment.WebRootFileProvider;
    
            if (_environment.IsDevelopment())
            {
                // Looks at the physical files on the disk so it can pick up changes to files under wwwroot while the application is running is Visual Studio.
                // The last PhysicalFileProvider enalbles TypeScript debugging but only wants to work with IE. I'm currently unsure how to get TS breakpoints to hit with Chrome.
                options.FileProvider = new CompositeFileProvider(options.FileProvider, 
                                                                 new PhysicalFileProvider(Path.Combine(_environment.ContentRootPath, $"..\\{GetType().Assembly.GetName().Name}\\wwwroot")),
                                                                 new PhysicalFileProvider(Path.Combine(_environment.ContentRootPath, $"..\\{GetType().Assembly.GetName().Name}")));
            }
            else
            {
                // When deploying use the files that are embedded in the assembly.
                options.FileProvider = new CompositeFileProvider(options.FileProvider, 
                                                                 new ManifestEmbeddedFileProvider(GetType().Assembly, "wwwroot")); 
            }
    
            _environment.WebRootFileProvider = options.FileProvider; // required to make asp-append-version work as it uses the WebRootFileProvider. https://github.com/aspnet/Mvc/issues/7459
        }
    }
    
    public class ViewConfigureOptions : IPostConfigureOptions<RazorViewEngineOptions>
    {
        private readonly IHostingEnvironment _environment;
    
        public ViewConfigureOptions(IHostingEnvironment environment)
        {
            _environment = environment;
        }
    
        public void PostConfigure(string name, RazorViewEngineOptions options)
        {
            if (_environment.IsDevelopment())
            {
                // Looks for the physical file on the disk so it can pick up any view changes.
                options.FileProviders.Add(new PhysicalFileProvider(Path.Combine(_environment.ContentRootPath, $"..\\{GetType().Assembly.GetName().Name}")));
            }
        }
    }
    
        4
  •  0
  •   Jean    7 年前

    我很久以前就谈到这个问题了。 你可以照着这篇文章做 how to include static files in a razor library 这就解释了如何绕过这个问题。

    经过大量调查,我得出的解决方案与这个问题的公认答案相距不远:

    1将文件设置为嵌入式资源

      <ItemGroup>
        <EmbeddedResource Include="wwwroot\**\*" />
        <EmbeddedResource Include="Areas\*\wwwroot\**\*" />
      </ItemGroup>
    

    2在清单中包含静态文件

      <PropertyGroup>
        <TargetFramework>netcoreapp2.1</TargetFramework>
        <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
      </PropertyGroup>
    

    3查找相关文件夹

    (您可以在链接的文章中找到更多详细信息,甚至可以从 this article )

    // first we get the assembly in which we want to embedded the files
    var assembly = typeof(TypeInFeatureAssembly).Assembly;
    
    // we filter only files including a wwwroot name part
    filePaths = (from rn in assembly.GetManifestResourceNames().Where(rnn => rnn.Contains(".wwwroot."))
        let hasArea = rn.Contains(".Areas.")
        let root = rn.Substring(0, rn.LastIndexOf(".wwwroot.") + ".wwwroot.".Length)
        let rootPath = !hasArea ? root : root.Substring(0, root.IndexOf(".Areas."))
        let rootSubPath = !hasArea ? "" : root.Substring(root.IndexOf(".Areas.")).Replace('.', '/')
        select  hasArea ? rootSubPath.Substring(1, rootSubPath.Length - 2) : "wwwroot" )
        .Distinct().ToList();
    

    这将提取 wwwroot 位于相关路径中的文件夹。

    4将找到的路径添加到webroot文件提供程序

    var allProviders = new List<IFileProvider>();
    allProviders.Add(env.WebRootFileProvider);
    allProviders.AddRange(filePaths.Select(t => new ManifestEmbeddedFileProvider(t.Assembly, t.Path)));
    env.WebRootFileProvider = new CompositeFileProvider(allProviders);
    
    
        5
  •  0
  •   Métoule    6 年前

    有一个更简单的解决方案:在RCL的项目中,您可以标记 wwwroot 要复制到发布目录:

    <ItemGroup>
      <Content Include="wwwroot\**\*.*" CopyToPublishDirectory="Always" />
    </ItemGroup>
    

    当您部署依赖于RCL的应用程序时,所有文件都可以按预期访问。你只需要小心不要有命名冲突。

    警告:这仅在Azure上部署时有效,但在本地计算机上不起作用(为此需要提供程序)。

        6
  •  0
  •   revobtz    6 年前

    请注意,所提供的解决方案仅适用于服务器端应用程序 . 如果您使用的是Blazor客户端,它将无法工作。要从razor类库在Blazor客户端包含静态资产,您需要直接引用这些资产,如下所示:

    <script src="_content/MyLibNamespace/js/mylib.js"></script>
    

    我花了好几个小时想弄清楚这件事。希望这能帮助别人。

    推荐文章