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.