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

Can Razor Class Library pack static files (js, css etc) too?

也许 duplicate of this 已经,但由于 post 没有任何答案,我正在 post 提出这个问题。

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

我能否以某种方式在多个 Razor 页面项目中重用 CSS,使用 Razor Class 库或其他任何东西(我的目的是,多个网站使用相同的 CSS,并且单个更改适用于所有项目)。

我已经尝试在 Razor Class 库项目中创建文件夹 wwwroot,但它没有按预期工作(我 可以 理解为什么不应该工作)。

您需要将静态资产嵌入到 Razor Class 库程序集中。我认为最好的方法是看一下 ASP.NET Identity UI source codes.

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

  1. 编辑 Razor Class 库的 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 Class 库中,创建以下 class 来为资源提供服务和路由。 (假设您的资产位于 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 Class 库路由器。在 Startup Class 的 ConfigureServices 方法中,添加以下行。

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

    <script src="~/js/app.bundle.js" asp-append-version="true"></script>
    

感谢 Ehsan 提供的有用信息。

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

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}")));
        }
    }
}

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

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

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

注意:这仅在部署在 Azure 上时有效,但在您的本地计算机上无效(为此您需要一个提供程序)。

Ehsan 在提问时的回答是正确的(对于 .NET Core 2.2),对于 .NET Core 3.0,RCL can include static assets 毫不费力:

To include companion assets as part of an RCL, create a wwwroot folder in the class library and include any required files in that folder.

When packing an RCL, all companion assets in the wwwroot folder are automatically included in the package.

The files included in the wwwroot folder of the RCL are exposed to the consuming app under the prefix _content/{LIBRARY NAME}/. For example, a library named Razor.Class.Lib results in a path to static content at _content/Razor.Class.Lib/.

请注意,提供的此解决方案仅适用于服务器端应用程序。如果您使用的是 Blazor 客户端,它将不起作用。要从 razor class 库在 Blazor 客户端包含静态资产,您需要像这样直接引用资产:

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

我浪费了几个小时试图弄清楚这一点。希望这对某人有所帮助。

在 .NET Core 3.1 中,RCL 将 wwwroot 文件夹中的资产包含在 _content/{LIBRARY NAME} 下的消费应用中。

我们可以通过编辑 RCL 项目属性并放置 StaticWebAssetBasePath.

将 _content/{LIBRARY NAME} 路径更改为不同的路径名
PropertyGroup>
    <StaticWebAssetBasePath Condition="$(StaticWebAssetBasePath) == ''">/path</StaticWebAssetBasePath>
  </PropertyGroup>

现在您可以使用 /path/test.js.

访问文件

@revobtz 的回答补充

我的项目文件的名称与命名空间不同,所以我发现应该是项目文件的名称而不是命名空间:

<script src="_content/<Name of project file>/js/mylib.js"></script>