DocFX is a static site generator from source code files and markdown. Its primary use is for documentation. However, it is essential to mention that it is flexible and is applicable for many different purposes. Some use it for blogging and profile sites. It is really up to you to define what to use it for. For more information on DocFX
you can visit their official website.
Why Serve It From .NET?
There are different advantages to serving the static site generated by DocFX
from .NET
since it allows adding different functionalities on top of the static site. One practical advantage is adding authentication to access the static site. The reason for adding the authentication could be that you have internal documentation within your company and want it to be accessible only by authorized persons. This scenario is only to demonstrate a practical use case, & the use case can be different depending on your scenario.
Pre-request
- IdentityServer4: For authentication
- DocFX: For generating static site
- Add
DocFX
to environment variable for smooth build process
I used IdentityServer4
for authentication in this article, however this implementation works fine with other methods of authentications as well.
Setting up IdentityServer4
Create IdentityServer4 project
dotnet new is4inmem -n Identity
The templates for IdentityServer4
projects is available on IdentityServer's Github repo. After creating the identity project we don't need to make further change.
Setting up Static site server
Create ASP.NET Api project
dotnet new webapi -n StaticSiteServer
Add reference to Microsoft.AspNetCore.Authentication.OpenIdConnect
NuGet package.
There are a couple of changes to be made here and there when setting up the authentication. Not to go off topic, I will not cover that in this article. However, the source code can be found in my Github repository.
Setting up static site
In the same directory as StaticSiteServer
create static site project:
docfx init -q -o Docs
Setting up the build process
Let's update StaticSiteServer.csproj
to define the root directory of the DocFX
file system:
<PropertyGroup>
.
.
.
<DocFXRoot>Docs\</DocFXRoot>
</PropertyGroup>
Next, let's update the StaticSiteServer.csproj
to generate static site on both during build & publish.
<ItemGroup>
<Content Remove="$(DocFXRoot)**" />
<None Remove="$(DocFXRoot)**" />
<None Include="$(DocFXRoot)**" Exclude="$(DocFXRoot)_site\**" />
</ItemGroup>
<Target Name="DebugEnsureDocFXEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(DocFXRoot)node_modules') ">
<Exec Command="docfx --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="DocFX is required to build and run this project. To continue, please install DocFX from https://github.com/dotnet/docfx/releases, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Generate static sites from Markdown and code files. This may take several minutes..." />
<Exec WorkingDirectory="$(DocFXRoot)" Command="docfx build"/>
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec WorkingDirectory="$(DocFXRoot)" Command="docfx build"/>
<ItemGroup>
<DistFiles Include="$(DocFXRoot)_site\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
Now, with these changes, the build will generate the static site. However, the StaticSiteSeserved
web API is not serving the static site. To server the static site, let's update the ConfigureServices
method in the Startup.cs
file. Moreover, we will add authorization:
public void ConfigureServices(IServiceCollection services) {
.
.
.
services.AddAuthorization(options => {
options.FallbackPolicy = new
AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
.
.
.
}
Again in the Startup.cs
file let's update the Configure
method to serve static files.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
.
.
.
app.UseFileServer(new FileServerOptions {
FileProvider = new
PhysicalFileProvider(
Path.Combine(env.ContentRootPath,
"Docs", "_site")),
});
.
.
.
}
NOTE
app.UseFileServer*
must be added afterapp.UseAuthentication()
andapp.UseAuthorization()
.
That's it. When accessing the StaticSiteServer,
the user is redirected for authentication. After authenticating, the user can access the static site, as shown below:
Complete source code can be found on Github
Thanks for reading, cheers!
Top comments (6)
Thanks for this article. Exactly my scenario and very good description. Do you think, the StaticSiteServer could be deployed as Azure Static Web App? They require an index.html in the root. How could this be achieved with your project?
I don't have a lot of experience with Azure Static Web App specifically but generally speaking: StaticSiteServer serves the static files which is also what Azure Static Web App does, so I don't see a way of using both in conjunction as both do somewhat similar task. having said that this is what I would suggest
Azure static web app provide a way of adding authorization so I would recommend checking if they fit your specific use case. here are some links that might help Authenticate users with Azure Static Web Apps, Authentication and authorization for Azure Static Web Apps
If the first solution doesn't fit your scenario and you want to use StaticSiteServer. one way to go about it would be to deploy StaticSiteServer as webapi so that it will handle serving the static files while adding your custom authentication.
All Good, is it possible to implement logout button, I mean once user is logged in if he wants to logout, how user will logout.
Yes, there are different ways you can do that. One straight forward approach is to add logout button to your static site (in this case the documentation documentation) header that calls the logout endpoint on your backend.
Hi! @bazenteklehaymanot, I did not found the license into the git [ github.com/bazen-teklehaymanot/ser... ]. Can I consider this source code as a MIT one?
Hi @ogameleira, I've added license(MIT) into the sample code. feel free to use it.