0
votes

I have a WebApp with an AspNetCore WebAPI + AngularApp on Azure with IIS rewrite rules in my Web.Config based on this article: https://weblog.west-wind.com/posts/2017/Apr/27/IIS-and-ASPNET-Core-Rewrite-Rules-for-AspNetCoreModule

I would like to avoid rewriting on Swagger, so I added this rule as the first rule:

<rule name="swagger" enabled="true" stopProcessing="true">
  <match url="swagger/.*" />
  <action type="None" />
</rule>

Based on this website: https://www.regextester.com/ enter image description here

The rules is supposed to exclude swagger/ and all the subfolders and files too. But It s not the case. image

This is what I can see when I open http://[MYWEBSITE]/swagger/ image

The Angular app works correctly.

I already made some search about that:

Thanks for your help.


EDIT 1:

Just to let you know, with the default web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>

    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\[MYWEBAPP].Api.dll" stdoutLogEnabled="false" stdoutLogFile="\\?\%home%\LogFiles\stdout" />
  </system.webServer>
</configuration>

I can correctly see Swagger working: enter image description here


EDIT 2:

During my tests, I removed the rewrite rules to focus only on the Handlers and I noticed that theses rules make problem:

It s normal, Swagger is build in the .NetCore WebApi. I m looking for a way to exclude a specific folder from a handlers.

Based on this response: IIS Handlers exclude a Directory when processing htm requests

I created a web.config in 'D:\home\site\wwwroot\wwwroot\swagger' on Azure with the following configuration:

<system.webServer>
     <handlers>
       <remove name="StaticFileModuleHtml" />
       <remove name="StaticFileModuleJs" />
       <remove name="StaticFileModuleCss" />
       <remove name="StaticFileModulePng" />
     </handlers>
</system.webServer>

EDIT 3:

Clearly the problem comes from the Static handlers. If I want to use Swagger, I need to disable them. If I want to access to my angular App, I need to add them.

I saw here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcorecli

That it seems possible to extract the content from the SwaggerUi directly in a folder on built, but currently, the Nugget Package is not available: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/601

I will keep you update. If someone has an idea, it will be helpful. I m probalbly not the only to try to do that :).

But the problem is still there.

2

2 Answers

4
votes

I finally found a solution!

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="wwwroot-static" stopProcessing="true">
          <match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg))" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_URI}" pattern="swagger/" negate="true" />
          </conditions>
          <action type="Rewrite" url="wwwroot/{R:1}" />
        </rule>
        <rule name="empty-root-index" stopProcessing="true">
          <match url="^$" />
          <action type="Rewrite" url="wwwroot/index.html" />
        </rule>
        <rule name="AngularJS-Html5-Routes" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" pattern="api/" negate="true" />
            <add input="{REQUEST_URI}" pattern="swagger/" negate="true" />
          </conditions>
          <action type="Rewrite" url="wwwroot/index.html" />
        </rule>
      </rules>
    </rewrite>
    <handlers>
      <add name="StaticFileModuleHtml" path="*.htm*" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleSvg" path="*.svg" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleJs" path="*.js" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleCss" path="*.css" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleJpeg" path="*.jpeg" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleJpg" path="*.jpg" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModulePng" path="*.png" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="StaticFileModuleGif" path="*.gif" verb="*" modules="StaticFileModule" resourceType="File" requireAccess="Read" />
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\[MYWEBAPP].dll" stdoutLogEnabled="false" stdoutLogFile="\\?\%home%\LogFiles\stdout" />
  </system.webServer>
</configuration>

My mistake during the Edit 2 was to create a Swagger folder in wwwroot/wwwroot instead of wwwroot/ directly.

Here is the web.config to add in wwwroot/swagger:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <!--We need to add this web.config to avoid conflict on Azure between Angular App and Swagger -->
      <remove name="StaticFileModuleHtml" />
      <remove name="StaticFileModuleSvg" />
      <remove name="StaticFileModuleJs" />
      <remove name="StaticFileModuleCss" />
      <remove name="StaticFileModuleJpeg" />
      <remove name="StaticFileModuleJpg" />
      <remove name="StaticFileModulePng" />
      <remove name="StaticFileModuleGif" />
    </handlers>
  </system.webServer>
</configuration>

You can now access to:

  • http(s)://[MYWEBAPP]
  • http(s)://[MYWEBAPP]/[ANGULAR ROUTE]
  • http(s)://[MYWEBAPP]/Swagger

I hope it will help someone :). If you have any questions, please ask.

1
votes

First of all, because Swagger does not make a real folder, so it is not a perfect idea to make a folder in wwwroot named swagger and make that web.config inside of it. I consider that the default address for your swagger UI is: localhost:[random-port-number]/swagger/index.html. It can be different based on your swagger version and you can also change it to something else like below:

app.UseSwagger(c =>
{
    c.RouteTemplate = "SampleApi/swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/SampleApi/swagger/v1/swagger.json", "Sample API");
    c.RoutePrefix = "SampleApi/swagger";
});

For more information see this article. The version of my Swashbuckle.AspNetCore is 4.0.1 I also added API to the routes in order to be able to use your Microsoft web APIs in your project from font-end by React or Angular or anything else, you just need to add this to your main web.config in the project:

<rules>
    <rule name="wwwroot-static" stopProcessing="true">
      <match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg|ico|axd))" />
       <!--Handle static file requests and server them from the wwwroot--> 
      <conditions logicalGrouping="MatchAll">
        <add input="{HTTP_METHOD}" pattern="GET|HEAD" />
        <add input="{REQUEST_URI}" pattern="/swagger/.*" negate="true" />
      </conditions>
      <action type="Rewrite" url="wwwroot/{R:1}" />
    </rule>

    <rule name="html5-routes" stopProcessing="true">
      <match url=".*" />
      <!-- Handle all paths except /api/ and pass the route onto the wwwroot/index.html -->
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <!-- Add rule to negate file requests e.g. css/html/images-->
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        <!-- Add rule to negate directories-->
        <add input="{REQUEST_URI}" pattern="^/api/" negate="true" />
        <add input="{REQUEST_URI}" pattern="/swagger/.*" negate="true" />
        <!-- Add rule to negate paths and let them through the MVC-->
        <add input="{HTTP_METHOD}" pattern="GET|HEAD" />
      </conditions>
      <action type="Rewrite" url="wwwroot/index.html" />
    </rule>
</rules>