Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I tell IIS to serve static files from the web root with an underlying catch-all route in my .NET application?

I have a web application that is built with .NET 5 beta-4 and running on IIS 8.5.

I want IIS to serve all static files, so I removed the .NET 5 static file middleware. This generally works as expected. /img/image.png will be served correctly via IIS, for example.

However, in my .NET application I have a catch-all route ("/{param?}") that serves business requirement purposes. But I also have a few static files in my webroot folder that need to be served (robots.txt, favicon.ico, etc).

The problem I'm running into: IIS routes web root static file requests to the .NET application, that will then run the catch-all route, which in turn will result in a 404. Ideally, I want IIS to serve the static file in the web root directory if it finds one. If it doesn't find one, send the request to the .NET catch-all route.

How do I tell IIS 8.5 to serve static files from web root with an underlying catch-all route in my .NET 5 application?

like image 623
Stephen Watkins Avatar asked Sep 28 '22 01:09

Stephen Watkins


2 Answers

Because we use IIS to serve our ASP.NET Core 1.0 (previously called ASP.NET 5) application, we wanted to use the IIS ability compress and cache static files.

It can make it more efficient and static assets are not affected when your app recycles. In any case, it is recommended to put app.UseStaticFiles(); as early as possible in your application's request pipeline so that it short-circuits the request. But there is an IIS server that can serve the static files already with compression and caching built-in. If on Linux, Nginx is typically set up to serve static files itself without ever hitting the ASP.NET Core pipeline. So we wanted to do this on IIS (if not, then why use IIS?).

In order to configure IIS to serve static files instead of our ASP.NET Core (Kestrel) server, we configured the StaticFileModule to run for specific file extensions before the AspNetCoreModule handler, as the order is important. (The rewrite method in the above answer did not work). There could be a better way to define the StaticFileModule handler to serve any existing file in wwwroot. We still used app.UseStaticFiles(); in our application to catch any obscure static files that haven't been defined in the StaticFileModule handlers.

Here is the web.config (system.webServer section):

<system.webServer>
<handlers>
  <add name="StaticFiles_Js" path="*.js" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Css" path="*.css" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Img" path="*.jpg" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Png" path="*.png" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Gif" path="*.gif" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Fnt" path="*.eot" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Ttf" path="*.ttf" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Ico" path="*.ico" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="StaticFiles_Pdf" path="*.pdf" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
  <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="..\MySite.exe" arguments="" stdoutLogEnabled="false" stdoutLogFile="..\logs\stdout" forwardWindowsAuthToken="true" />
<!-- Adding Gzip compression for static files -->
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files" minFileSizeForComp="1024">
  <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
  <staticTypes>
    <add mimeType="text/*" enabled="true" />
    <add mimeType="application/javascript" enabled="true" />
    <add mimeType="application/json" enabled="true" />
    <add mimeType="*/*" enabled="false" />
  </staticTypes>
</httpCompression>
</system.webServer>

To confirm that IIS is being used for the static files/assets, just use F12 developer tools to see the Response Headers. The html page would have response headers showing that it was served by the .NET application (Server: Kestrel) but the .css or .js assets would have Server: Microsoft-IIS and Content-Encoding:gzip if you enabled compression.

like image 136
truemedia Avatar answered Oct 01 '22 01:10

truemedia


While I agree with the idea of using middleware, if you absolutely want to rely on your webserver to serve static files without ever touching you application you could setup a file-dependent rewrite:

<rule name="Static Files" stopProcessing="true">
    <match url="(.*)" />
    <conditions logicalGrouping="MatchAny">
        <add input="{APPL_PHYSICAL_PATH}wwwroot\{R:1}" matchType="IsFile" />
    </conditions>
    <action type="Rewrite" url="/{R:1}" />
</rule>

That should check if your request is a file on the filesystem, and if it is rewrite your request directly to it and stop-processing the request.

like image 28
Brandon Martinez Avatar answered Sep 30 '22 23:09

Brandon Martinez