pythonpython-3.xwindows-servicesfastapipywin32

Unable to access FastApi app that is running as windows service created with pywin32


i have a basic fastapi main.py as following:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()
origins = ["*"]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"Hello": "World"}

and a win_service.py as following:

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import os
from threading import Thread
import uvicorn

class AppServerSvc(win32serviceutil.ServiceFramework):
    _svc_name_ = "ABCD"
    _svc_display_name_ = "ABCD Windows Service"
    _svc_description_ = "A FastAPI application running as a Windows Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(
            servicemanager.EVENTLOG_INFORMATION_TYPE,
            servicemanager.PYS_SERVICE_STARTED,
            (self._svc_name_, "")
        )
        self.main()

    def main(self):
        def start_server():
            os.chdir(os.path.dirname(os.path.abspath(__file__)))
            uvicorn.run("main:app", host="0.0.0.0", port=8000)

        server_thread = Thread(target=start_server)
        server_thread.start()
        win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
        server_thread.join()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

using windows 11 & python version 3.9. when i try to install (python win_service.py install) using admin cmd and debug (python win_service.py debug) it works & i can access it on http://localhost:8000

when i start (python win_service.py start), it gets started and in windows event viewer the log shows The ABCD service has started. but it's not accessable at http://localhost:8000

how can i access it, when it's running as a service?


Solution

  • finally created a work around, that starts fastapi as a sub-process:

    import os
    import win32serviceutil
    import win32service
    import win32event
    import socket
    import subprocess
    
    
    class AppServerSvc(win32serviceutil.ServiceFramework):
        _svc_name_ = "ABCD"
        _svc_display_name_ = "ABCD Windows Service"
        _svc_description_ = "A FastAPI application running as a Windows Service"
    
        def __init__(self, args):
            win32serviceutil.ServiceFramework.__init__(self, args)
            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
            socket.setdefaulttimeout(60)
            self.is_running = True
            self.process = None
    
        def SvcDoRun(self):
            self.ReportServiceStatus(win32service.SERVICE_RUNNING)
            self.main()
    
        def SvcStop(self):
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
            self.is_running = False
            if self.process:
                self.process.terminate()
                self.process.wait()
            win32event.SetEvent(self.hWaitStop)
    
        def main(self):
            uvicorn_location = r"C:\Users\username\AppData\Local\Programs\Python\Python39\Scripts\uvicorn.exe"
            command = [
                uvicorn_location,
                "main:app",
                "--host",
                "0.0.0.0",
                "--port",
                "8000"
            ]
            working_directory = r"E:\path-to-fastapi-backend"
    
            os.chdir(working_directory)
            self.process = subprocess.Popen(command)
            
            while self.is_running:
                rc = win32event.WaitForSingleObject(self.hWaitStop, 5000)
                if rc == win32event.WAIT_OBJECT_0:
                    break
    
    if __name__ == "__main__":
        win32serviceutil.HandleCommandLine(AppServerSvc)