Most examples that show the creation of a Razor Class Library that I have seen don't include guidance on incorporating static resources like Bootstrap or custom JavaScript files. It's not a particularly intuitive process or well documented. My example will demonstrate the steps required to do this. The example itself will show the starting point of creating a headless CMS - a perfect candidate for a Razor Class Library. The example won't include any CMS functionality. That would only obscure the purpose of this article.
Creating the Class Library
- Start a new project in Visual Studio choosing the New ASP.NET Core Web Application template and name it EditorRCL
- Select the Razor Class Library option, ensuring that the version is ASP.NET Core 2.1 or higher
- Build (or wait for VS to perform a restore) to get rid of warnings and then rename the MyFeature folder to Editor and delete Page1.cshtml and its PageModel class file
- Add a Razor Page named Index.cshtml to the Pages folder in the Editor area with the following code:
- Right click on the solution in Soultion Explorer and choose Add » New Project, and select ASP.NET Core Web Application. Name it EditorHost and then choose Web Application. The Razor Pages application that gets generated will provide the test environment for the Razor class library.
- Add a reference to the EditorRCL from within the EditorHost site. You can do this by right-clicking on EditorHost project in Solution Explorer and choosing
Add » Reference, or by modifying the
EditorHost.csproj file
to include a
ProjectReference
entry so that it looks like this:
- Make sure that EditorHost is set as the startup project
and run the application. You should get the default template home page. Add/editor
to the URL. The result should look like this:
If it does, then the Razor class library has been referenced correctly and is working.
However, the output is totally unstyled. If you look at
the source for the page, it consists of a single line of HTML:
<h1>RCL Editor</h1>
. The page needs a Layout.
Adding a Layout
- Add a new folder named Shared to the Pages folder in the EditorRCL project.
- Add a Razor Layout named _Layout.cshtml to this folder with the following code:
This is a slightly modified version of the template generated for Razor Pages 2.2 applications. It references Bootstrap 4 and a Google Icon pack. It has most of the navigation section removed. - Add a new Razor View Start file to the Pages folder in the EditorRCL project named
_ViewStart.cshtml. The default template should contain the following code:
Now if you run the application, it looks like the Layout page is working:
And it is working - to an extent. It is being referenced correctly from the Razor Page, but if you look at the source code, you can see
environment
tags and other tag helper attributes:The Tag Helpers are not being processed. They look like they might be, because Bootstrap is being applied. However the CSS file that should be used in development mode is supposed to be located in a folder named css, which doesn't exist yet. The rendered
environment
tags are being ignored by the browser and the CDN version of Bootstrap is being used instead. - Tag helpers are an opt-in feature, so we shall opt in. Add a new
Razor View Imports file to the Pages folder in the EditorRCL project.
Add the following code to the file:
Now when you run the page, you will find that no Bootstrap styles are being applied, and the browser cannot find any css or js files being referenced in Development mode:
Adding Static Resources
The final step involves adding local copies of script and style files to the class library and then configuring them to work.
- Create a folder named resources in the root of the EditorRCL project (at the same level as the
Areas folder) ands within that add two further folders named css
and js:
- Add Bootstrap 4 and Jquery files to the folders. I copied these across
from a Razor Pages 2.2 project template (created using the dotnet new razor
command with the preview of .NET Core 2.2 SDK installed). Alternatively you
can obtain them yourself using npm, Nuget or by going to the project sites
and obtaining the files. You should also create a site.css file and a
site.js file and add those to the relevant folders:
- Static files in a standalone class library cannot be browsed like
those in a web application. They need to be included in the resulting
compiled assembly as
embedded resources. So the next step is to alter
the EditorRCL.csproj file to specify that the contents of the
resources
folder should be included as an embedded resource (lines 14-16), include
the
Microsoft.Extensions.FileProviders.Embedded
package (line 10) andMicrosoft.AspNetCore.StaticFiles
(line 100), and importantly, to specify that a manifest is generated for the embedded resources (line 5): - Next, borrowing code from the
IdentityUI source, add the following class to the EditorRCL project:
This code creates an additional
FileProvider
, pointing to the resources folder, and adds it to those that retrieve static files. - Add the following extension method to simplify applying the configuration in the
Startup
classConfigureService
method: - Modify the
ConfigureServices
method in the EditorHost application's Startup class to include a call to the extension method above (line 10 below): Now when you run the application, you can see that the embedded resources are retrieved successfully:
Summary
Getting static files to work in a Razor Class Library isn't particularly difficult, once you know how. The trick is to remember that they are embedded resources. That's why I don't name the folder that I place them in wwwroot, or anything similar that could confuse the issue. There is an open issue to improve this experience, but it looks like it's been kicked into the long grass. So in the meantime, the steps above should help you if you are trying to author a Razor Class Library that relies on its own static resources.