AspNetStatic 0.18.0
See the version list below for details.
dotnet add package AspNetStatic --version 0.18.0
NuGet\Install-Package AspNetStatic -Version 0.18.0
<PackageReference Include="AspNetStatic" Version="0.18.0" />
paket add AspNetStatic --version 0.18.0
#r "nuget: AspNetStatic, 0.18.0"
// Install AspNetStatic as a Cake Addin #addin nuget:?package=AspNetStatic&version=0.18.0 // Install AspNetStatic as a Cake Tool #tool nuget:?package=AspNetStatic&version=0.18.0
AspNetStatic
Transform ASP.NET Core into a Static Site Generator
Okay, so you want to create a static website. After doing some research, you learn that all the cool kids are using tools like Jekyll, Hugo, Gatsby, Statiq, and others. But what you also learn is that all of these tools require you to learn an entirely new way of constructing sites and pages. And then it occurs to you, hey wait a minute, I already know how to use ASP.NET Core to create websites, so why do I need to learn & use a whole other stack just for SSG? Isn't there a better way that lets me use the tools and skills I already have?
Well, now there is!
Create a static site from your ASP.NET Core generated content
AspNetStatic lets you generate a static website with the same ASP.NET Core tools you love and use every day. Just add this module and a bit of configuration, and BAM!, you have yourself a static site generator!
But wait, there's more!
AspNetStatic can also be used in a mixed mode configuration where some of the pages in your site are static html files (generated with the same _layout & page layers that define the look & feel of the rest of your site), while others remain dynamically generated per request. See Partial Static Site under Scenarios section below.
No Frameworks. No Engines. No Opinions!
Build your ASP.NET site the way you've always done. AspNetStatic doesn't have any opinions about how you should build your site. AspNetStatic is not a framework. It's not a CMS. There's no blog engine. It has no templating system. AspNetStatic does just one thing (well, two, if you count the fallback middleware): create static files for selected routes in your ASP.NET Core app. That means you can use whatever framework, component, package, or architectural style you like. Want to use a blog engine like BlogEngine.NET? No problem. Want to use a CMS like Orchard or Umbraco? No problem. Want to create a documentation site using a markdown processor to render page content? No problem! AspNetStatic doesn't care; it will create optimized static files no matter how the content is produced.
<br/>
Great, So How Do I Use It?
It's a piece of cake!
- Add the Nuget Package to your ASP.NET Core web app project
dotnet add package AspNetStatic
- Create and register an object that implements
IStaticPagesInfoProvider
- Create an instance of
StaticPagesInfoProvider
, or an object that derives fromStaticPagesInfoProviderBase
, or one that implements the interface directly - Populate the
Pages
collection to specify the routes for which to generate static pages- Set required
Route
property of eachPageInfo
- Set other
PageInfo
properties as appropriate
- Set required
- Set other
IStaticPagesInfoProvider
attributes as appropriate - Register the implementation class in the DI container
builder.Services.AddSingleton<IStaticPagesInfoProvider>( new StaticPagesInfoProvider( new PageInfo[] { new("/") { ... }, new("/privacy") { ... }, new("/blog/posts/1") { OutFile = @"blog\post-1.html" }, new("/blog/posts/2") { OutFile = @"blog\post-2-dark.html", Query = "?theme=dark" } }));
- Create an instance of
- Add the AspNetStatic module
... app.MapRazorPages(); ... app.GenerateStaticPages(app.Environment.WebRootPath); ... app.Run();
- Run your web app
dotnet run
Now, whenever you start your app, the static files will be (re-)created from their source Razor pages (or controller action views).
There are also options for exiting the app after generating static pages, or to periodically regenerate static pages while your app is running. See the Scenarios section below for details.
<br/>
Routes
Keep the following in mind when specifying routes in the IStaticPagesInfoProvider.Pages
collection.
- Routes must exclude the site's base URI (e.g. http:<span>://</span>localhost:5000, https<span>://www</span>.example.com)
- As a rule, don't specify an 'index' page name; instead, opt for a route with a terminating slash (/ instead of /index).
- You can directly specify the pathname of the file to be generated for routes you add to the
Pages
collection (seeOutFile
property). The only requirement is that the specified path be relative to the destination root folder. If you do not specify a value forOutFile
, the pathname for the generated file will be determined as demonstrated below. - You can specify route parameters for routes you add to the
Pages
collection. The route parameters are treated as part of the route, and are used in constructing the output file pathname. - You can specify a query string for routes you add to the
Pages
collection (seeQuery
property). You can specify the sameRoute
with differentQuery
values, but you will need to specify a uniqueOutFile
value for each instance of that route. - You can skip content optimization<sup>1</sup> or choose a specific optimizer type for routes you add to the
Pages
collection (seeOptimizerType
property). The default optimizer type setting,OptimizerType.Auto
, automatically selects the appropriate optimizer. - You can set the encoding for content written to output files for routes you add to the
Pages
collection (seeOutputEncoding
property). Default is UTF8.
1: Content optimizer options apply only when content optimization is enabled. Please see the Content Optimization section below for details.
Routes vs. Generated Static Files
Assumes the following:
- Destination root: "C:\MySite"
- OutFile: null / empty / whitespace
Url<br/>(route + query) | Always Default<br/>false | Always Default<br/>true |
---|---|---|
/ | C:\MySite\index.html | C:\MySite\index.html |
/index | C:\MySite\index.html | C:\MySite\index.html |
/index/ | C:\MySite\index\index.html | C:\MySite\index\index.html |
/page | C:\MySite\page.html | C:\MySite\page\index.html |
/page/ | C:\MySite\page\index.html | C:\MySite\page\index.html |
/page/123 | C:\MySite\page\123.html | C:\MySite\page\123\index.html |
/page/123/ | C:\MySite\page\123\index.html | C:\MySite\page\123\index.html |
/page/123?p1=v1 | C:\MySite\page\123.html | C:\MySite\page\123\index.html |
/page/123/?p1=v1 | C:\MySite\page\123\index.html | C:\MySite\page\123\index.html |
/blog/articles/ | C:\MySite\blog\articles/index.html | C:\MySite\blog\articles\index.html |
/blog/articles/post1 | C:\MySite\blog\articles\post1.html | C:\MySite\blog\articles\post1\index.html |
Routes vs. Served Content (using fallback middleware)
Assumes the following:
- OutFile: null / empty / whitespace
Url<br/>(route + query) | Is Static Route: false<br/><br/> | Is Static Route: true<br/>Always Default: false | Is Static Route: true<br/>Always Default: true |
---|---|---|---|
/ | /index.cshtml | /index.html | /index.html |
/index | /index.cshtml | /index.html | /index.html |
/index/ | /index/index.cshtml | /index/index.html | /index/index.html |
/page | /page.cshtml | /page.html | /page/index.html |
/page/ | /page/index.cshtml | /page/index.html | /page/index.html |
/page/123 | /page.cshtml | /page/123.html | /page/123/index.html |
/page/123/ | /page.cshtml | /page/123/index.html | /page/123/index.html |
/page/123?p1=v1 | /page.cshtml | /page/123.html | /page/123/index.html |
/page/123/?p1=v1 | /page.cshtml | /page/123/index.html | /page/123/index.html |
/blog/articles/ | /blog/articles/index.cshtml | /blog/articles/index.html | /blog/articles/index.html |
/blog/articles/post1 | /blog/articles/post1.cshtml | /blog/articles/post1.html | /blog/articles/post1/index..html |
The same rules apply when links in static files are updated to refer to other generated static files.
IMPORTANT NOTE: In ASP.NET Core, UrlHelper (and the asp-* tag helpers) generate link URLs based on the routing configuration of your app, so if you're using them, be sure to specify an appropriate value for alwaysDefaultFile
, as shown below. (NOTE: Specify the same value if/when configuring the fallback middleware).
builder.Services.AddRouting(
options =>
{ // default configuration in ASP.NET Core
options.AppendTrailingSlash = false; // generated links: / and /page
});
...
app.GenerateStaticPages(
alwaysDefaultFile: false); // generated pages: /index.html and /page.index.html
--OR--
builder.Services.AddRouting(
options =>
{
options.AppendTrailingSlash = true; // generated links: / and /page/
});
...
app.GenerateStaticPages(
alwaysDefaultFile: true); // generated pages: /index.html and /page/index.html
<br/>
Scenarios
In all scenarios, ensure that routes for static pages are unincumbered by authentication or authorization requirements.
Standalone Static Site
In this scenario, you want to generate a completely static website (to host on Netlify or Azure/AWS storage, for instance). Once the static pages are generated, you will take the files in the destination folder (e.g. wwwroot), along with any .css, .js, and image files, and xcopy deploy them to your web host.
Sample Configuration 1:
- Specify any accessible folder (e.g. wwwroot) as the destination-root for the generated static files.
- Generate a default file only for routes ending with a slash.
- Update href attribute value for <a> and <area> tags that refer to static pages (e.g. /page to /page.html).
app.GenerateStaticPages( app.Environment.WebRoot, exitWhenDone: true, alwaysDefaultFile: false, dontUpdateLinks: false);
Sample Configuration 2:
- Specify any accessible folder (e.g. wwwroot) as the destination-root for the generated static files.
- Generate a default file for all routes (e.g. /page and /page/ to /page/index.html).
- Don't update href attribute value for <a> and <area> tags that refer to static pages.
- Use your web server's redirect or url-rewrite module to re-route requests (e.g. /page/ or /page/index to /page/index.html).
// true when app is executed with one of the following args... // dotnet run -- static-only // dotnet run -- exit-when-done var exitWhenDone = args.HasExitWhenDoneArg(); app.GenerateStaticPages( @"C:\path\to\destination\root\folder", exitWhenDone: exitWhenDone, alwaysDefaultFile: true, dontUpdateLinks: true);
If you want to omit static-file generation while you're still developing the site (to save CPU cycles?), you could configure a profile in launchSettings.json and surround the GenerateStaticPages()
call with an IF gate.
"profiles": {
"GenStaticPages": {
"commandName": "Project",
"commandLineArgs": "exit-when-done",
"launchBrowser": false,
"applicationUrl": "https://localhost:5000",
}
}
if (args.HasExitWhenDoneArg())
{
app.GenerateStaticPages(
@"path\to\destination\root\folder",
exitWhenDone: true,
);
}
Partial Static Site
In this scenario, you want some of the pages in your ASP.NET Core app to be static, but still want other routes to be served as dynamic content per request (pages/views and JSON API's). When the app runs, static (.html) files will be generated for routes you specify. The website will then serve these static files for the specified routes, and dynamic content, as usual, for others.
While static files are being generated, requests for which the static file hasn't yet been generated will be served as dynamic content using the source (.cshtml) page. Once the static file has been generated, it will be used to satisfy requests.
The configuration options are the same as for a standalone static site, except the following:
- The destination root folder must be
app.Environment.WebRoot
. - You must do one of the following (can do both):
- Use the AspNetStatic fallback middleware.
- Allow links in generated static files to be updated.
- Do not exit the app after static files are generated (obviously, right?)
Like this:
...
builder.Services.AddStaticPageFallback();
...
app.UseStaticPageFallback(); // re-route to the static file
app.UseStaticFiles();
...
app.UseRouting();
...
app.Map...();
...
app.GenerateStaticPages(
app.Environment.WebRoot, // must specify wwwroot
exitWhenDone: false, // don't exit after generating static files
alwaysDefaultFile: true/false,
dontUpdateLinks: false); // update links so they refer to the static file
...
app.Run();
The fallback middleware only re-routes requests for routes that are specified in the
Pages
collection, and only if the static file exists.
Periodic Regeneration
If the data used in the content of static files changes while the app is running, you can configure periodic regeneration by specifying a value for the regenerationInterval
parameter in the GenerateStaticPages()
call. This will result in static files being generated when the app starts, and then periodically based on the specified interval.
app.GenerateStaticPages(
...
regenerationInterval: TimeSpan.FromHours(2) // re-generate static files every 2 hours
);
<br/>
Content Optimization
AspNetStatic automatically minifies HTML content (and any embedded CSS or Javascript) in generated static files; configuration is not required.
To disable this feature, specify true
for the dontOptimizeContent
parameter:
app.GenerateStaticPages(
...
dontOptimizeContent: true);
Configuration
To override the default minification settings used by AspNetStatic, register the appropriate objects in the DI container, as shown below.
AspNetStatic uses the excellent WebMarkupMin package to implement the minification feature. For details about the configuration settings, please consult the WebMarkupMin documentation.
Content optimization can be customized in one of two ways:
Create and register an object that implements
IOptimizerSelector
. In addition to specifying custom optimizer configurations, this option allows you to implement your own custom logic for selecting the optimizer to use for a page.public class MyOptimizerSelector : IOptimizerSelector { ... } ... builder.Services.AddSingleton(sp => new MyOptimizerSelector( ... ));
Create and register individual settings objects which internally feed into a default
IOptimizerSelector
implementation.HTML: To configure the HTML minifier, register a configured instance of
HtmlMinificationSettings
:using WebMarkupMin.Core; builder.Services.AddSingleton( sp => new HtmlMinificationSettings() { ... });
XHTML: To configure the XHTML minifier, register a configured instance of
XhtmlMinificationSettings
:using WebMarkupMin.Core; builder.Services.AddSingleton( sp => new XhtmlMinificationSettings() { ... });
XML: To configure the XML minifier, register a configured instance of
XmlMinificationSettings
:using WebMarkupMin.Core; builder.Services.AddSingleton( sp => new XmlMinificationSettings() { ... });
CSS: To configure the CSS minifier, register an object that implements the
ICssMinifier
interface:using WebMarkupMin.Core; builder.Services.AddSingleton<ICssMinifier>( sp => new YuiCssMinifier(...));
Javascript: To configure the Javascript minifier, register an object that implements the
IJsMinifier
interface:using WebMarkupMin.Core; builder.Services.AddSingleton<IJsMinifier>( sp => new YuiJsMinifier(...));
<br/>
License
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- TestableIO.System.IO.Abstractions (>= 19.2.11)
- TestableIO.System.IO.Abstractions.Wrappers (>= 19.2.11)
- WebMarkupMin.Core (>= 2.13.9)
-
net7.0
- TestableIO.System.IO.Abstractions (>= 19.2.11)
- TestableIO.System.IO.Abstractions.Wrappers (>= 19.2.11)
- WebMarkupMin.Core (>= 2.13.9)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on AspNetStatic:
Package | Downloads |
---|---|
AspNetStaticContrib
AspNetStatic community contributed extensions. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.24.0 | 558 | 8/4/2024 |
0.23.0 | 52 | 8/3/2024 |
0.22.0 | 53 | 7/30/2024 |
0.21.0 | 1,047 | 1/14/2024 |
0.20.0 | 148 | 12/28/2023 |
0.19.0 | 140 | 12/11/2023 |
0.18.4 | 97 | 12/8/2023 |
0.18.3 | 90 | 12/5/2023 |
0.18.2 | 120 | 11/24/2023 |
0.18.0 | 379 | 7/18/2023 |
0.17.1 | 155 | 7/17/2023 |
0.17.0 | 155 | 7/16/2023 |
0.16.0 | 152 | 7/16/2023 |
0.15.1 | 147 | 7/11/2023 |
0.15.0 | 151 | 7/6/2023 |
0.14.0 | 143 | 7/6/2023 |
0.13.3 | 137 | 6/26/2023 |
0.13.2 | 138 | 6/25/2023 |
0.13.1 | 152 | 5/7/2023 |
0.13.0 | 132 | 5/7/2023 |
0.12.1 | 128 | 5/5/2023 |
0.11.3 | 155 | 4/15/2023 |
0.11.2 | 167 | 4/15/2023 |
0.11.1 | 162 | 4/14/2023 |
0.11.0 | 162 | 4/14/2023 |
0.10.5 | 159 | 4/13/2023 |
0.10.4 | 162 | 4/13/2023 |
0.10.2 | 159 | 4/12/2023 |
0.10.1 | 162 | 4/11/2023 |
0.10.0 | 157 | 4/10/2023 |
0.9.2 | 174 | 4/9/2023 |
0.9.1 | 166 | 4/8/2023 |