Running FastAPI under IIS - python

currently I have a simple API running with Flask under a subdirectory of some internal IIS site. Now I thought it might be some idea to rewrite that API using FastAPI instead. Running the API on IIS isn't a hard thing, you have to create a web.config and so some stuff inside the IIS configuration. I know that this is using WSGI, but is there a possibility to use ASGI as well (maybe in combination with uvicorn and gunicorn)?
One thing which is important is that it must run under a certain subdirectory, call it <iis_internal_company_server>/myapi. In Flask I have included a well-known prefix middleware which works as expected. I was looking for something similar for FastAPI, is this may a case to use app.include_router(router, prefix='/myapi')?
Have done some research but did not find a solution. Maybe one of you may have some experience with it. If so, please share. Many thanks in advance.
Regards, Thomas

In my case for deploy on IIS used wfastcgi.py
First of all please install a2wsgi
pip install a2wsgi
in main.py
from fastapi import FastAPI
from a2wsgi import ASGIMiddleware
app = FastAPI()
#app.get("/")
def read_main():
return {"message": "Hello World"}
wsgi_app = ASGIMiddleware(app)
in web.config add this key in appSettings
<add key="WSGI_HANDLER" value="main.wsgi_app" />

My answer is too long for a single comment, but it's not a definitive answer.
I did not understand completely the WSGI/ASGI stuff? You want to still run the Flask api together with Fastapi? Then Fastapi can do it. Check the docs https://fastapi.tiangolo.com/advanced/wsgi/. Hereby I report the example
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
flask_app = Flask(__name__)
#flask_app.route("/")
def flask_main():
name = request.args.get("name", "World")
return f"Hello, {escape(name)} from Flask!"
app = FastAPI()
#app.get("/v2")
def read_main():
return {"message": "Hello World"}
app.mount("/v1", WSGIMiddleware(flask_app))
What do you mean by subdirectory? You can put the code in whatever directory you want and then run it from there. Instead, if you want to have two apps running on the same IP address, as far as I know, your only chance is to run a gateway that will forward requests that start with /api to the Fastapi server, all other requests to the other server
EDIT based on the comment
How can I fix FastAPI application error on Apache WSGI?. The server needs to be compatible with ASGI workers. I don't know which web server you're running, so I can't say anything.
This point should already answer your question, doesn't it?

Related

How to change the Redirect URI from HTTP to HTTPS within a FastAPI web app using fastapi_msal?

I am trying to setup Azure AD authentication for a web application using FastAPI. I am using the fastapi_msal python package to do this. The problem I am having is that when I go to the web app, I am able to login, but once i am authenticated, it says the redirect URI that the application is using begins with HTTP. However, Azure requires the redirect uri begin with HTTPS unless running the app locally. Does anyone know how I can change the redirect uri to begin with https instead?
The code for my project pretty much exactly resembles the code from this example project here. However, I have found a similar project using Flask instead of FastAPI. And there is a specific portion of the code that addresses this redirect uri problem:
# This section is needed for url_for("foo", _external=True) to automatically
# generate http scheme when this sample is running on localhost,
# and to generate https scheme when it is deployed behind reversed proxy.
# See also https://flask.palletsprojects.com/en/1.0.x/deploying/wsgi-standalone/#proxy-setups
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
Does anyone know how I can do something like this for a web app using FastAPI instead?
The full source code for the Flask app can be found here

Can't serve swagger on remotely hosted server with different url

I'm trying to serve some simple service using flask and flask_restx (a forked project of flask-restplus, that would be eventually served on AWS.
When it is served, I want to generate swagger page for others to test it easily.
from flask import Flask
from flask_restx import Api
from my_service import service_namespace
app = Flask(__name__)
api = Api(app, version='1.0')
api.add_namespace(service_namespace)
if __name__ == '__main__':
app.run(debug=True)
When I test it locally (e.g. localhost:5000), it works just fine. Problem is, when it is hosted on AWS, because it has a specific domain (gets redirected?) (e.g. my-company.com/chris-service to a container), the document page is unable to find its required files like css and so:
What I've looked and tried
Python (Flask + Swagger) Flasgger throwing 404 error
flask python creating swagger document error
404 error in Flask
Also tried adding Blueprint (albeit without knowing exactly what it does):
app = Flask(__name__)
blueprint = Blueprint("api", __name__,
root_path="/chris-service",
# url_prefix="/chris-service", # doesn't work
)
api = Api(blueprint)
app.register_blueprint(blueprint)
...
And still no luck.
Update
So here's more information as per the comments (pseudo, but technically identical)
Access point for the swagger is my-company.com/chris (with or without http:// or https:// doesn't make difference)
When connecting to the above address, the request URL for the assets are my-company.com/swaggerui/swagger-ui.css
You can access the asset in my-company.com/chris/swaggerui/swagger-ui.css
So I my resolution (which didn't work) was to somehow change the root_path (not even sure if it's the correct wording), as shown in What I've looked and tried.
I've spent about a week to solve this but can't find a way.
Any help will be greatful :) Thanks
Swagger parameters defined at apidoc.py file. Default apidoc object also created in this file. So if you want to customize it you have change it before app and api initialization.
In your case url_prefix should be changed (I recommend to use environment variables to be able set url_prefix flexibly):
$ export URL_PREFIX='/chris'
from os import environ
from flask import Flask
from flask_restx import Api, apidoc
if (url_prefix := environ.get('URL_PREFIX', None)) is not None:
apidoc.apidoc.url_prefix = url_prefix
app = Flask(__name__)
api = Api(app)
...
if __name__ == '__main__':
app.run()
Always very frustrating when stuff is working locally but not when deployed to AWS. Reading this github issue, these 404 errors on swagger assets are probably caused by:
Missing javascript swagger packages
Probably not the case, since flask-restx does this for you. And running it locally should also not work in this case.
Missing gunicorn settings
Make sure that you are also setting gunicorn up correctly as well with
--forwarded-allow-ips if deploying with it (you should be). If you are in a kubernetes cluster you can set this to *
https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
According to this post, you also have to explicitly set
settings.FLASK_SERVER_NAME to something like http://ec2-10-221-200-56.us-west-2.compute.amazonaws.com:5000
If that does not work, try to deploy a flask-restx example, that should definetely work. This rules out any errors on your end.

Publish a script online

I have a Python script written up and the output of this script is a list.
Right now I need to get it online and make it accessible to others. I looked at Django , but then I realized that it may be kind of hard to create the UI. Is there any simple way to create a UI in Django and map it to an existing Python script.
Right now I using nltk, numpy, sqlite3 and things like that. Or is there a simpler way by which I can proceed?
In your case, Django is redundant.
You can use something smaller, Flask or maybe Aiohttp.
For example, all you need in aiohttp:
basic hmtl template
handler for one url (here you will call your script)
aiohttp webserver
The main idea:
Your server catch some url (for example /),
start your script, receives result,
respond with your html template (also render script result in it).
You can try creating a flask App.
Just do a pip install Flask and try the code below
from flask import Flask
import flask
import json
from flask import Response
app = Flask(__name__)
#app.route('/test',methods=['GET'])
def test():
'''
GET: Receives the request in /test route and returns a response containing {"response": [1,2,3]}
'''
my_list = [1,2,3]
resp = Response(response=json.dumps({"response": my_list}), status=200, mimetype='application/json')
return resp
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8082)
Then to test your app from your browser accessing
localhost:8082/test
or also through some app like postman
I would suggest looking into something like React for creating the UI. This way your UI would only make calls to your Flask server.

AngularJS with python flask test fails

i'm learning to use python flask to complement angularJS. This is what i have on flask:
#!flask/bin/python
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/hola', methods=['GET'])
def get_tasks():
x="pepepepep"
return jsonify({'msj': x})
if __name__ == '__main__':
app.run(debug=True)
when i open in my browser:
http://127.0.0.1:5000/hola
i do indeed see the json object with that random message.
Now, when i go to angularJS, this is what i have on the controller:
function ControladorPrueba($scope,$http) {
$scope.mensaje="i";
$scope.ObtenerMsj= function(){
$http.get('http://localhost:5000/hola')
.success(function(data){
$scope.mensaje=data.msj;
})
.error(function(data,status){
$scope.mensaje = status;
});
};
}
The problem is that when this function is executed, the get always go on .error(...), any ideas of why this happens even when the service in flask works well when opened on the browser? am i missing something?
Any help appreciated. Thanks!
I suspect that this may be due to the Same-origin policy. If you're running your Flask app on localhost, and if you're serving your Angularjs files separately (for example, using the file:/// protocol), then the browser will actually forbid your Angular code from talking to the webservice for security reasons.
If this is the case, you have two solutions. You can either start serving your HTML/CSS/Javascript files from your Flask app, or by using something like Flask-Cors to explicitly set your server to allow external requests.

Routes With Custom Domains Using Flask

My web app assigns a subdomain to users and optionally allows them to use a custom domain. This works except when the user visits their custom domain for a route without including a trailing slash.
GET requests to this url works as expected: http://user.example.com:5000/book/12345/
GET requests to this url works as expected: http://custom.com:5000/book/12345/
GET requests to this url attempt to redirect, but fail: http://custom.com:5000/book/12345
Flask ends up redirecting the browser to this url which, of course, doesn't work: http://<invalid>.example.com:5000/book/12345/
Is there a different way that I should handle custom domains? Here's a complete minimal example to reproduce this. I have set custom.com, example.com. and user.example.com to point to 127.0.0.1 in my /etc/hosts file in my development environment so that Flask receives the request.
from flask import Flask
app = Flask(__name__)
server = app.config['SERVER_NAME'] = 'example.com:5000'
#app.route('/', subdomain="<subdomain>")
#app.route('/')
def index(subdomain=None):
return ("index")
#app.route('/book/<book_id>/', subdomain="<subdomain>")
#app.route('/book/<book_id>/')
def posts(post_id, subdomain=None):
return (book_id)
if __name__ == '__main__':
app.run(host='example.com', debug=True)
I'm not sure that's possible. host matching and subdomain matching are mutually exclusive (look at host matching parameter).
I'd love to be wrong though.
One way around this issue that I can think of is to use something in front of Flask (say nginx) that points custom.com to custom.com._custom.example.com or something like that. In your code you could create a custom url_for function that would recognize this as a custom domain. I would ask on the Flask mailing list as they would be able to give you a solid answer.

Categories