Score:0

Strip path prefix using AspNetCoreModuleV2 (or httpPlatformHandler) on IIS

in flag

As a company using IIS for deployment of several web sites and services, we somehow struggle to deploy a Python web service based on FastAPI, however this problem may not be limited to Python. We are trying to use either the httpPlatformHandler module or its successor, the AspNetCoreModuleV2 module.

The configuration for the httpPlattformHandler module looks like this right now:

<configuration>
  <system.webServer>
    <handlers>
      <add name="httpPlatform"
           path="*"
           verb="*"
           modules="httpPlatformHandler"
           resourceType="Unspecified"/>
    </handlers>
    <httpPlatform processPath=".\venv\Scripts\python.exe"
                arguments="-m uvicorn test:app --port %HTTP_PLATFORM_PORT%"
                stdoutLogEnabled="true"
                stdoutLogFile=".\python.log"/>
  </system.webServer>
</configuration>

The configuration for the AspNetCoreModuleV2 module is very similar as it seems to work pretty much the same way:

<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore"
           path="*"
           verb="*"
           modules="AspNetCoreModuleV2"
           resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath=".\venv\Scripts\python.exe"
                arguments="-m uvicorn test:app --port %ASPNETCORE_PORT%"
                stdoutLogEnabled="true"
                stdoutLogFile=".\python.log"/>
  </system.webServer>
</configuration>

Both modules work fine and forward the requests to the ASGI server uvicorn, as long as the web service runs directly under the root path / (site-level). However, we now want to deploy one of the web services under a specific path, e.g. /api (application-level). This is where the actual problem starts, because the FastAPI application is not aware of the path /api, so it answers all requests as if the /api part of the path belongs to the actual request.

The documentation of FastAPI contains a chapter on running behind a proxy:

Having a proxy with a stripped path prefix, in this case, means that you could declare a path at /app in your code, but then, you add a layer on top (the proxy) that would put your FastAPI application under a path like /api/v1.

In this case, the original path /app would actually be served at /api/v1/app.

Even though all your code is written assuming there's just /app.

And the proxy would be "stripping" the path prefix on the fly before transmitting the request to Uvicorn, keep your application convinced that it is serving at /app, so that you don't have to update all your code to include the prefix /api/v1.

The question is now how to configure IIS to apply this path stripping before routing the request to the httpPlatformHandler or the AspNetCoreModuleV2 module.

We tried to use the the URL Rewrite module like in the example below, but the rewrite seems to take place before routing to the application, causing the requests to not be routed to the FastAPI application anymore:

<rewrite>
    <rules>
        <rule name="Proxy" stopProcessing="true">
            <match url="^api/(.*)" />
            <action type="Rewrite" url="/{R:1}" />
        </rule>
    </rewrite>
</rules>

We took a look at the Application Request Routing module, too, but it seems to be a massive overkill for such a simple use case.

As a side note, we recently also used the FastCgiModule in combination with Microsofts wfastcgi library for Python, even if we had to convert the ASGI application to a WSGI application first (with the help of a2wsgi). The FastCgiModule successfully stripped the path after configuring the properties path and the somehow sketchy allowPathInfo:

<configuration>
  <system.webServer>
    <handlers>
      <add name="FastCGI"
           path="api/"
           allowPathInfo="true"
           verb="*"
           modules="FastCgiModule"
           scriptProcessor="<path-created-by-wfastcgi>"
           resourceType="Unspecified"
           requireAccess="Script" />
    </handlers>
  </system.webServer>
  <appSettings>
    <add key="WSGI_HANDLER" value="my_api.wsgi_app" />
  </appSettings>
</configuration>

Sadly, we cannot continue to use this approach, especially because of the conversion from ASGI to WSGI.

Lex Li avatar
vn flag
"as long as the web service runs directly under the root path / (site-level)", then keep that site untouched. Use ARR to set up reverse proxy rules to forward `/api` to that site at `/`. When you said "it seems to be a massive overkill", you should be aware that quite a lot of users are on that route.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.