모든 Flask 경로에 접두사 추가


98

모든 경로에 추가하려는 접두사가 있습니다. 지금은 모든 정의에서 상수를 경로에 추가합니다. 이 작업을 자동으로 수행하는 방법이 있습니까?

PREFIX = "/abc/123"

@app.route(PREFIX + "/")
def index_page():
  return "This is a website about burritos"

@app.route(PREFIX + "/about")
def about_page():
  return "This is a website about burritos"

답변:


75

대답은이 애플리케이션을 어떻게 제공하는지에 따라 다릅니다.

다른 WSGI 컨테이너 내부에 하위 마운트 됨

WSGI 컨테이너 (mod_wsgi, uwsgi, gunicorn 등) 내에서이 애플리케이션을 실행한다고 가정합니다. 실제로 해당 접두사 에서 해당 WSGI 컨테이너의 하위 부분으로 응용 프로그램을 마운트하고 (WSGI를 말하는 모든 것이 수행 할 것임) APPLICATION_ROOT구성 값을 접두사 로 설정 해야합니다.

app.config["APPLICATION_ROOT"] = "/abc/123"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /abc/123/"

APPLICATION_ROOT구성 값을 설정하면 Flask의 세션 쿠키를 해당 URL 접두사로 제한하기 만하면됩니다. 다른 모든 것은 Flask와 Werkzeug의 뛰어난 WSGI 처리 기능에 의해 자동으로 처리됩니다.

앱을 올바르게 하위 마운트하는 예

첫 번째 단락이 무엇을 의미하는지 확실하지 않은 경우 내부에 Flask가 마운트 된 다음 예제 애플리케이션을 살펴보십시오.

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/abc/123'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/abc/123': app.wsgi_app})

if __name__ == '__main__':
    app.run('localhost', 5000)

앱에 대한 요청 프록시

반면에, WSGI 컨테이너의 루트에서 Flask 애플리케이션을 실행하고 이에 대한 요청을 프록시하는 경우 (예 : FastCGI를 사용하는 경우 또는 nginx가 proxy_pass하위 엔드 포인트에 대한 요청을 -ing하는 경우) 독립 실행 형 uwsgi/ gevent서버에 다음 중 하나를 수행 할 수 있습니다.

  • Miguel이 그의 답변 에서 지적했듯이 Blueprint를 사용하십시오 .
  • 또는 사용 DispatcherMiddleware에서 werkzeug(또는 PrefixMiddleware에서 su27의 답변을 에) 서브 마운트 사용하고있는 독립 실행 형 WSGI 서버에서 응용 프로그램을. ( 사용할 코드는 위 의 앱을 올바르게 하위 마운트하는 예를 참조 하세요 .)

@jknupp -보고 flask.Flask#create_url_adapterwerkzeug.routing.Map#bind_to_environ그것처럼 보인다 해야 일 - 당신은 어떻게 코드를 실행했다? (응용 프로그램은 실제로위한 WSGI 환경에서 하위 경로에 설치 될 필요가 url_for기대 값을 반환합니다.)
숀 비에이라

당신이 작성한 그대로 실행했지만 app = Flask ( name ) 및 app.run (debug = True)
jeffknupp 2014 년

4
@jknupp-그게 문제입니다. 실제로 애플리케이션을 더 큰 애플리케이션의 하위 부분으로 마운트해야합니다 (WSGI를 말하는 모든 것이 수행됩니다). 저는 예제 요점 을 작성하고 하위 경로 요청 만 전달하는 프록시 뒤에있는 독립형 WSGI 환경이 아니라 하위 마운트 된 WSGI 환경을 가정하고 있음을 명확히하기 위해 답변을 업데이트했습니다.
Sean Vieira 2014 년

3
이것은 DispatcherMiddleware플라스크를 단독으로 실행할 때 접근 방식을 사용하여 작동 합니다. Gunicorn 뒤에서 실행할 때 제대로 작동하지 않는 것 같습니다.
Justin

1
uwsgi의 하위 경로에 마운트하는 방법 uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app. 자세한 내용은 (uwsgi 문서) [ flask.pocoo.org/docs/1.0/deploying/uwsgi/]
todaynowork

94

경로를 청사진에 넣을 수 있습니다.

bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route("/")
def index_page():
  return "This is a website about burritos"

@bp.route("/about")
def about_page():
  return "This is a website about burritos"

그런 다음 접두사를 사용하여 애플리케이션에 Blueprint를 등록합니다.

app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/abc/123')

2
안녕하세요 Miguel; 아래에서했던 것처럼 블루 프린트에 대한 url_prefix를 등록하는 것과 app.register_blueprint위의 블루 프린트 객체를 인스턴스화 할 때 등록하는 것의 차이점을 알고 url_prefix='/abc/123있습니까? 감사합니다!
aralar

4
차이점은 register_blueprint호출에 URL 접두사가 있으면 애플리케이션이 원하는 위치에 Blueprint를 "마운트"하거나 다른 URL에 동일한 Blueprint를 여러 번 마운트 할 수 있다는 것입니다. Blueprint 자체에 접두사를 넣으면 응용 프로그램이 더 쉬워 지지만 유연성이 떨어집니다.
Miguel

감사합니다!! 그것은 매우 도움이됩니다. 나는 명백한 중복성에 혼란 스러웠지만 두 옵션 사이의 절충안을 봅니다.
aralar

그리고 실제로 저는 이것을 시도한 적이 없지만 블루 프린트와 앱 모두에서 URL 접두사를 앱의 접두사 fist와 결합한 다음 블루 프린트 접두사를 결합 할 수 있습니다.
Miguel

4
blueprint.route 데코 레이팅 기능 이후 에 청사진 등록이 필요 합니다.
Quint 2017 년

53

APPLICATION_ROOT이 목적을위한 것이 아님을 유의해야합니다 .

다음과 같이 변경하기 위해 미들웨어를 작성하기 만하면됩니다.

  1. PATH_INFO접두사가 붙은 URL을 처리하도록 수정 하십시오.
  2. SCRIPT_NAME접두사가 붙은 URL을 생성하도록 수정 하십시오.

이렇게 :

class PrefixMiddleware(object):

    def __init__(self, app, prefix=''):
        self.app = app
        self.prefix = prefix

    def __call__(self, environ, start_response):

        if environ['PATH_INFO'].startswith(self.prefix):
            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
            environ['SCRIPT_NAME'] = self.prefix
            return self.app(environ, start_response)
        else:
            start_response('404', [('Content-Type', 'text/plain')])
            return ["This url does not belong to the app.".encode()]

다음과 같이 미들웨어로 앱을 래핑합니다.

from flask import Flask, url_for

app = Flask(__name__)
app.debug = True
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/foo')


@app.route('/bar')
def bar():
    return "The URL for this page is {}".format(url_for('bar'))


if __name__ == '__main__':
    app.run('0.0.0.0', 9010)

방문 http://localhost:9010/foo/bar,

올바른 결과를 얻을 수 있습니다. The URL for this page is /foo/bar

필요한 경우 쿠키 도메인을 설정하는 것을 잊지 마십시오.

이 솔루션은 Larivact의 요점에 의해 제공됩니다 . 는 APPLICATION_ROOT그것을 할 수처럼 보이지만,이 작업이 아니다. 정말 혼란 스럽습니다.


4
이 답변을 추가해 주셔서 감사합니다. 여기에 게시 된 다른 솔루션을 시도했지만 이것이 나를 위해 일한 유일한 솔루션입니다. +++ 나는 wfastcgi.py 사용하여 IIS에 배포하고있어
sytech을

" APPLICATION_ROOT이 일은이 일을위한 것이 아닙니다"-이것이 내가 잘못하고 있었던 곳입니다. 나는 희망 Blueprinturl_prefix매개 변수와 APPLICATION_ROOT내가 가질 수 그래서, 기본적으로 결합 된 APPLICATION_ROOT전체 응용 프로그램에 대한 범위 URL 및 url_prefix내 범위 URL을 APPLICATION_ROOT단지 개인의 청사진합니다. Sigh
Monkpit

을 사용하여하려는 작업의 예는 이 요점 을 참조하십시오 APPLICATION_ROOT.
Monkpit

2
gunicorn을 사용하는 경우 SCRIPT_NAME이 이미 지원됩니다. 환경 변수로 설정하거나 http 헤더로 전달 : docs.gunicorn.org/en/stable/faq.html
blurrcat

1
코드가 저에게 효과가 없었습니다. 몇 가지 조사를 마친 후 __call__방법 에서 다음과 같은 방법을 response = Response('That url is not correct for this application', status=404) return response(environ, start_response)from werkzeug.wrappers import BaseResponse as Response
Louis Becker 19

10

이것은 Flask / werkzeug 답변보다 파이썬 답변에 가깝습니다. 그러나 그것은 간단하고 작동합니다.

저처럼 애플리케이션 설정 ( .ini파일 에서로드 됨 )에 Flask 애플리케이션의 접두사도 포함하도록하려면 (따라서 배포 중에 값을 설정하지 않고 런타임 중에) 다음을 선택할 수 있습니다.

def prefix_route(route_function, prefix='', mask='{0}{1}'):
  '''
    Defines a new route function with a prefix.
    The mask argument is a `format string` formatted with, in that order:
      prefix, route
  '''
  def newroute(route, *args, **kwargs):
    '''New function to prefix the route'''
    return route_function(mask.format(prefix, route), *args, **kwargs)
  return newroute

틀림없이, 이것은 다소 hackish하고 플라스크 경로 기능이 있다는 사실에 의존 필요route 첫 번째 위치 인수로합니다.

다음과 같이 사용할 수 있습니다.

app = Flask(__name__)
app.route = prefix_route(app.route, '/your_prefix')

NB : 접두사에 변수를 사용하고 (예를 들어으로 설정하여 /<prefix>) @app.route(...). 그렇게한다면 prefix데코 레이팅 된 함수에 매개 변수 를 선언해야합니다 . 또한 제출 된 접두사를 일부 규칙에 대해 확인하고 확인에 실패하면 404를 반환 할 수 있습니다. 404 사용자 정의 재 구현을 방지하기 위해, 제발 from werkzeug.exceptions import NotFound다음 raise NotFound()검사가 실패 할 경우.


사용하는 것보다 간단하고 효율적 Blueprint입니다. 공유해 주셔서 감사합니다!
HK boy

5

따라서 이에 대한 올바른 대답은 개발이 완료 될 때 사용하는 실제 서버 응용 프로그램에서 접두사를 구성해야한다는 것입니다. Apache, nginx 등

그러나 디버그에서 Flask 앱을 ​​실행하는 동안 개발 중에이 작업을 수행하려면 이 요점을 살펴보십시오 .

플라스크가 DispatcherMiddleware구출되었습니다!

후손을 위해 여기에 코드를 복사하겠습니다.

"Serve a Flask app on a sub-url during localhost development."

from flask import Flask


APPLICATION_ROOT = '/spam'


app = Flask(__name__)
app.config.from_object(__name__)  # I think this adds APPLICATION_ROOT
                                  # to the config - I'm not exactly sure how!
# alternatively:
# app.config['APPLICATION_ROOT'] = APPLICATION_ROOT


@app.route('/')
def index():
    return 'Hello, world!'


if __name__ == '__main__':
    # Relevant documents:
    # http://werkzeug.pocoo.org/docs/middlewares/
    # http://flask.pocoo.org/docs/patterns/appdispatch/
    from werkzeug.serving import run_simple
    from werkzeug.wsgi import DispatcherMiddleware
    app.config['DEBUG'] = True
    # Load a dummy app at the root URL to give 404 errors.
    # Serve app at APPLICATION_ROOT for localhost development.
    application = DispatcherMiddleware(Flask('dummy_app'), {
        app.config['APPLICATION_ROOT']: app,
    })
    run_simple('localhost', 5000, application, use_reloader=True)

독립 플라스크 응용 프로그램으로 위의 코드를 실행할 때 지금, http://localhost:5000/spam/이 표시됩니다 Hello, world!.

다른 답변에 대한 의견에서 나는 다음과 같이하고 싶다고 표현했습니다.

from flask import Flask, Blueprint

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
app.run()

# I now would like to be able to get to my route via this url:
# http://host:8080/api/some_submodule/record/1/

DispatcherMiddleware내 인위적인 예에 적용 :

from flask import Flask, Blueprint
from flask.serving import run_simple
from flask.wsgi import DispatcherMiddleware

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
application = DispatcherMiddleware(Flask('dummy_app'), {
    app.config['APPLICATION_ROOT']: app
})
run_simple('localhost', 5000, application, use_reloader=True)

# Now, this url works!
# http://host:8080/api/some_submodule/record/1/

"그래서 이것에 대한 올바른 대답은 개발이 완료 될 때 사용하는 실제 서버 응용 프로그램에서 접두사를 구성해야합니다. Apache, nginx 등입니다." 문제는 리디렉션에 있습니다. 접두사가 있고 Flask에서 설정하지 않은 경우 / yourprefix / path / to / url로 이동하는 대신 리디렉션하면 / path / to / url로 이동합니다. nginx 또는 Apache에서 접두사가 무엇인지 설정하는 방법이 있습니까?
Jordan Reiter

내가이 작업을 수행하는 방법은 puppet 또는 chef와 같은 구성 관리 도구를 사용하고 거기에 접두사를 설정 한 다음 도구가 변경 사항을 이동해야하는 구성 파일에 전파하도록하는 것입니다. 아파치 나 nginx에 대해 내가 무슨 말을하는지 아는 척도하지 않을 것입니다. 이 질문 / 답변은 파이썬에만 국한되었으므로 시나리오를 별도의 질문으로 게시하는 것이 좋습니다. 이 경우 여기에서 질문에 자유롭게 연결하십시오!
Monkpit

2

또 다른 완전히 다른 방법은 함께 마운트 지점 에서 uwsgi.

동일한 프로세스에서 여러 앱 호스팅에 대한 문서 ( permalink ).

당신에 uwsgi.ini당신은 추가

[uwsgi]
mount = /foo=main.py
manage-script-name = true

# also stuff which is not relevant for this, but included for completeness sake:    
module = main
callable = app
socket = /tmp/uwsgi.sock

당신이 당신의 파일을 호출하지 않는 경우 main.py, 당신은 변경해야하는 모두 mountmodule

귀하는 main.py다음과 같이 수 :

from flask import Flask, url_for
app = Flask(__name__)
@app.route('/bar')
def bar():
  return "The URL for this page is {}".format(url_for('bar'))
# end def

그리고 nginx 구성 (완전성을 위해 다시) :

server {
  listen 80;
  server_name example.com

  location /foo {
    include uwsgi_params;
    uwsgi_pass unix:///temp/uwsgi.sock;
  }
}

이제 호출 은 자동으로 조정되므로 flask의 반환 된대로 example.com/foo/bar표시 됩니다. 그러면 링크가 접두사 문제없이 작동합니다./foo/barurl_for('bar')


2
from flask import Flask

app = Flask(__name__)

app.register_blueprint(bp, url_prefix='/abc/123')

if __name__ == "__main__":
    app.run(debug='True', port=4444)


bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route('/')
def test():
    return "success"

1
설명을 추가해보세요.
jpp

1
내가 찾은 두 가지 멋진 설명은 exploreflask공식 문서에 있습니다.
yuriploc

1

비슷한 소위 "컨텍스트 루트"가 필요했습니다. WSGIScriptAlias를 사용하여 /etc/httpd/conf.d/ 아래의 conf 파일에서 수행했습니다.

myapp.conf :

<VirtualHost *:80>
    WSGIScriptAlias /myapp /home/<myid>/myapp/wsgi.py

    <Directory /home/<myid>/myapp>
        Order deny,allow
        Allow from all
    </Directory>

</VirtualHost>

이제 다음과 같이 내 앱에 액세스 할 수 있습니다. http : // localhost : 5000 / myapp

가이드 참조-http: //modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html


1

플라스크와 PHP 앱이 nginx와 PHP5.6이 공존하는 솔루션

루트에 플라스크 유지 및 하위 디렉토리에 PHP 유지

sudo vi /etc/php/5.6/fpm/php.ini

한 줄 추가

cgi.fix_pathinfo=0
sudo vi /etc/php/5.6/fpm/pool.d/www.conf
listen = /run/php/php5.6-fpm.sock

uwsgi

sudo vi /etc/nginx/sites-available/default

PHP에 중첩 된 위치를 사용하고 FLASK를 루트에 유지

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.php index.nginx-debian.html;

    server_name _;

    # Serve a static file (ex. favico) outside static dir.
    location = /favico.ico  {    
        root /var/www/html/favico.ico;    
    }

    # Proxying connections to application servers
    location / {
        include            uwsgi_params;
        uwsgi_pass         127.0.0.1:5000;
    }

    location /pcdp {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    location /phpmyadmin {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php7.0-cgi alone:
    #   fastcgi_pass 127.0.0.1:9000;
    #   # With php7.0-fpm:
    #   fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}

https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms를 주의 깊게 읽으십시오.

위치 일치를 이해해야합니다 (없음) : 수정자가 없으면 위치가 접두사 일치로 해석됩니다. 이는 주어진 위치가 일치를 결정하기 위해 요청 URI의 시작 부분과 일치 함을 의미합니다. = : 등호가 사용되는 경우 요청 URI가 지정된 위치와 정확히 일치하면이 블록이 일치하는 것으로 간주됩니다. ~ : 물결표 수정자가있는 경우이 위치는 대소 문자를 구분하는 정규식 일치로 해석됩니다. ~ * : 물결표 및 별표 수정자를 사용하면 위치 블록이 대소 문자를 구분하지 않는 정규식 일치로 해석됩니다. ^ ~ : 캐럿 및 물결표 수정자가 있고이 블록이 최상의 비정규 식 일치로 선택되면 정규식 일치가 발생하지 않습니다.

nginx의 "위치"설명에서 순서는 중요합니다.

주어진 요청과 일치하는 위치를 찾기 위해 nginx는 먼저 접두사 문자열 (접두사 위치)을 사용하여 정의 된 위치를 확인합니다. 그 중에서 가장 긴 접두사가 일치하는 위치를 선택하여 기억합니다. 그런 다음 구성 파일에 나타나는 순서대로 정규식을 확인합니다. 정규식 검색은 첫 번째 일치에서 종료되고 해당 구성이 사용됩니다. 정규식과 일치하는 항목이 없으면 이전에 기억 된 접두사 위치의 구성이 사용됩니다.

그 뜻은:

First =. ("longest matching prefix" match)
Then implicit ones. ("longest matching prefix" match)
Then regex. (first match)

1

여전히이 문제로 어려움을 겪고있는 사람들에게는 첫 번째 예제가 작동하지만 제어 할 수없는 Flask 앱이있는 경우 전체 예제가 여기에 있습니다.

from os import getenv
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.serving import run_simple
from custom_app import app

application = DispatcherMiddleware(
    app, {getenv("REBROW_BASEURL", "/rebrow"): app}
)

if __name__ == "__main__":
    run_simple(
        "0.0.0.0",
        int(getenv("REBROW_PORT", "5001")),
        application,
        use_debugger=False,
        threaded=True,
    )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.