Tüm Flask rotalarına bir önek ekleyin


103

Her rotaya eklemek istediğim bir ön ekim var. Şu anda her tanımda rotaya bir sabit ekliyorum. Bunu otomatik olarak yapmanın bir yolu var mı?

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"

Yanıtlar:


77

Cevap, bu uygulamayı nasıl sunduğunuza bağlıdır.

Başka bir WSGI konteynerinin içine alt montajlı

Bu uygulamayı bir WSGI kapsayıcısı (mod_wsgi, uwsgi, gunicorn, vb.) İçinde çalıştıracağınızı varsayarsak; Aslında gerek bu önek olarak, montaj ve ayarlama (yapacak WSGI konuşur şeylere) o WSGI kabın bir alt bölümü olarak uygulamayı APPLICATION_ROOTsizin önek için yapılandırma değeri:

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_ROOTYapılandırma değerini ayarlamak, Flask'ın oturum çerezini bu URL önekiyle sınırlandırmanız yeterlidir. Geri kalan her şey, Flask ve Werkzeug'un mükemmel WSGI işleme yetenekleri tarafından sizin için otomatik olarak ele alınacaktır.

Uygulamanızı uygun şekilde alt montajına bir örnek

İlk paragrafın ne anlama geldiğinden emin değilseniz, içine Flask monte edilmiş bu örnek uygulamaya bakın:

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)

Uygulamaya isteklerin vekaleten verilmesi

Öte yandan, Flask uygulamanızı WSGI kapsayıcısının kök dizininde çalıştıracak ve isteklere proxy uygulayacaksanız (örneğin, FastCGI için kullanılıyorsa veya nginx proxy_passbir alt uç nokta için isteklerse) bağımsız uwsgi/ geventsunucunuz için şunlardan birini yapabilirsiniz:

  • Miguel'in cevabında belirttiği gibi bir Şablon kullanın .
  • veya kullanmak DispatcherMiddlewareden werkzeug(veya PrefixMiddlewaregelen su27 cevabı için) alt montaj kullandığınız tek başına WSGI sunucusunda başvurunuzu. ( Kullanılacak kod için yukarıdaki uygulamanızı uygun şekilde alt montajına ilişkin bir örneğe bakın ).

@jknupp - bakıyorum flask.Flask#create_url_adapterve çalışması gerekiyormuşwerkzeug.routing.Map#bind_to_environ gibi görünüyor - kodu nasıl çalıştırıyordunuz? ( Beklenen değeri döndürmek için uygulamanın bir WSGI ortamında alt yola monte edilmesi gerekiyor .)url_for
Sean Vieira

Tam olarak yazdığınız şeyi çalıştırdım, ancak app = Flask ( name ) ve app.run (debug = True)
ekledim

4
@jknupp - sorun budur - uygulamayı daha büyük bir uygulamanın bir alt parçası olarak bağlamanız gerekir (WSGI konuşan herhangi bir şey işe yarar). Ben çırpılmış ettik örnek özünü ve ben bir alt monte WSGI ortamı, sadece alt yol isteklerini yönlendiriyorsa Proxy'yle değil tek başına bir WSGI ortamı farz ediyorum o daha anlaşılabilir olması için benim cevap güncellendi.
Sean Vieira

3
Bu, DispatcherMiddlewareşişeyi kendi başına çalıştırırken yaklaşımı kullanarak çalışır . Gunicorn'un arkasında koşarken bunu çalıştıracak gibi görünmüyor.
Justin

1
Uwsgi'de alt yola bağlanmanın yolu uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app. ayrıntı (uwsgi belgesi) [ flask.pocoo.org/docs/1.0/deploying/uwsgi/]
todaynowork

101

Rotalarınızı bir plana yerleştirebilirsiniz:

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"

Ardından, planı bir önek kullanarak uygulamaya kaydedersiniz:

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

2
Merhaba Miguel; Aşağıda yaptığınız gibi bir plan için bir url_prefix kaydetme ile app.register_blueprintyukarıdaki Blueprint nesnesini geçirerek somutlaştırdığınızda onu kaydetme arasındaki farkı biliyor musunuz url_prefix='/abc/123? Teşekkür ederim!
aralar

4
Aradaki fark, register_blueprintçağrıda URL ön ekine sahip olmanın, uygulamaya planı istediği herhangi bir yere "bağlama" veya hatta aynı planı farklı URL'lere birden çok kez bağlama özgürlüğü vermesidir. Öneki planın kendisine eklerseniz, uygulama için kolaylaştırırsınız, ancak daha az esnekliğe sahip olursunuz.
Miguel

Teşekkür ederim!! Bu çok faydalıdır. Görünür fazlalıkla kafam karıştı ama iki seçenek arasındaki değiş tokuşu görüyorum.
aralar

Ve aslında, bunu hiç denemedim, ancak muhtemelen hem plandaki hem de uygulamadaki URL öneklerini, uygulamanın önek yumruğu ve ardından plan öneki ile birleştirebilirsiniz.
Miguel

5
Blueprint.route dekore edilmiş işlevlerden sonra planın kaydedilmesi gerektiğini unutmayın .
Quint

53

Bunun APPLICATION_ROOTbu amaç için OLMADIĞINI unutmamalısınız .

Tek yapmanız gereken, aşağıdaki değişiklikleri yapmak için bir ara yazılım yazmaktır:

  1. PATH_INFOön ekli url'yi işlemek için değiştirin .
  2. SCRIPT_NAMEön ekli url oluşturmak için değiştirin .

Bunun gibi:

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()]

Uygulamanızı aşağıdaki gibi ara yazılımla sarın:

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)

Ziyaret edin http://localhost:9010/foo/bar,

Doğru sonucu alacaksınız: The URL for this page is /foo/bar

Gerekirse çerez alanını ayarlamayı da unutmayın.

Bu çözüm Larivact'in özüyle verilmektedir . APPLICATION_ROOTOlmasını benziyor rağmen, bu iş için değil. Gerçekten kafa karıştırıcı.


4
Bu cevabı eklediğiniz için teşekkürler. Burada yayınlanan diğer çözümleri denedim, ancak bu benim için çalışan tek çözüm. A +++ wfastcgi.py kullanarak IIS üzerinde konuşlandırıldım
sytech

" APPLICATION_ROOTBu iş için değil" - işte burada yanlış gidiyordum. Dilerim Blueprint'ın url_prefixparametresini ve APPLICATION_ROOTYapabilirdim böylece, varsayılan olarak birleştirildi APPLICATION_ROOTTüm uygulamanın kapsamı URL'ler ve url_prefixiçinde kapsam URL'ler APPLICATION_ROOTsadece bireysel planına. Sigh
Monkpit

Kullanmaya çalıştığım şeyin bir örneği için bu öze bakın APPLICATION_ROOT.
Monkpit

2
Gunicorn kullanıyorsanız, SCRIPT_NAME zaten desteklenmektedir. Bunu bir ortam değişkeni olarak ayarlayın veya bir http başlığı olarak iletin
blurrcat

1
Şu anki kod benim için işe yaramadı. Biraz araştırma yaptıktan sonra, __call__yöntemdeki diğer 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

10

Bu, bir Flask / werkzeug cevabından çok bir python cevabıdır; ama basit ve işe yarıyor.

Benim gibi, uygulama ayarlarınızın (bir .inidosyadan yüklenen ) Flask uygulamanızın önekini de içermesini istiyorsanız (bu nedenle, değerin dağıtım sırasında değil, çalışma zamanı sırasında ayarlanmasını), aşağıdakileri seçebilirsiniz:

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

Muhtemelen, bu biraz hackish ve Matara rota işlevi olduğu gerçeğine dayanır gerektiren bir routeilk pozisyonel argüman olarak.

Bunu şu şekilde kullanabilirsiniz:

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

Not: Önekte bir değişkeni kullanmanın (örneğin, olarak ayarlayarak) mümkün olması /<prefix>ve ardından bu öneki @app.route(...). Bunu yaparsanız, açıkça prefixdekore edilmiş işlevlerinizde parametreyi bildirmeniz gerekir. Ek olarak, gönderilen öneki bazı kurallara göre kontrol etmek ve kontrol başarısız olursa bir 404 döndürmek isteyebilirsiniz. 404 özel yeniden uygulamasından kaçınmak için, lütfen from werkzeug.exceptions import NotFoundve sonra raise NotFound()kontrol başarısız olursa.


Kullanmaktan daha basit ve daha verimli Blueprint. Paylaşım için teşekkürler!
HK boy

5

Bu nedenle, buna geçerli bir yanıt olduğuna inanıyorum: önek, geliştirme tamamlandığında kullandığınız gerçek sunucu uygulamasında yapılandırılmalıdır. Apache, nginx vb.

Ancak, hata ayıklamada Flask uygulamasını çalıştırırken bunun geliştirme sırasında çalışmasını istiyorsanız, bu özete bir göz atın .

Flask DispatcherMiddlewarekurtarmaya!

Gelecek nesil için kodu buraya kopyalayacağım:

"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)

Şimdi, yukarıdaki kodu bağımsız bir Flask uygulaması olarak çalıştırırken http://localhost:5000/spam/görüntülenecektir Hello, world!.

Başka bir cevapla ilgili bir yorumda şöyle bir şey yapmak istediğimi ifade ettim:

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/

Yapılmış DispatcherMiddlewareörneğime başvurarak :

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/

"Bu nedenle, bunun için geçerli bir yanıt olduğuna inanıyorum: önek, geliştirme tamamlandığında kullandığınız gerçek sunucu uygulamasında yapılandırılmalıdır. Apache, nginx, vb." Sorun yeniden yönlendirmelerdedir; bir önekiniz varsa ve bunu Flask'ta ayarlamazsanız, / önek / yol / / url'ye gitmek yerine yeniden yönlendirdiğinde, / yol / / url'ye gider. Nginx veya Apache'yi kurmanın bir yolu var mı, önek ne olmalı?
Jordan Reiter

Muhtemelen bunu yapmamın yolu, kukla veya şef gibi bir yapılandırma yönetim aracı kullanmak ve öneki oraya ayarlamak ve ardından aracın değişikliği gitmesi gereken yerde yapılandırma dosyalarına yaymasını sağlamaktır. Apache veya nginx için neden bahsettiğimi bildiğimi bile iddia etmeyeceğim. Bu soru / cevap python'a özgü olduğu için senaryonuzu ayrı bir soru olarak göndermenizi tavsiye ederim. Bunu yaparsanız, buradan soruya bağlantı vermekten çekinmeyin!
Monkpit

2

Tamamen farklı bir başka yol da mountpoints ile uwsgi.

Aynı işlemde birden fazla uygulamayı barındırma hakkındaki dokümandan ( kalıcı bağlantı ).

Gözlerinde farklı uwsgi.iniEklemek

[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

Dosyanızı aramazsan main.py, değiştirmek gerekir hem mountvemodule

Sizin main.pybu gibi görünebilir:

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

Ve bir nginx yapılandırması (yine bütünlük için):

server {
  listen 80;
  server_name example.com

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

Şimdi arama example.com/foo/bar, otomatik olarak adapte edildiğinden, /foo/barşişenin döndürdüğü şekilde görüntülenecektir url_for('bar'). Bu şekilde bağlantılarınız önek sorunları olmadan çalışacaktır.


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
Lütfen bir açıklama eklemeyi düşünün.
jpp

1
Bulduğum İki güzel açıklamalar vardı exploreflask ve resmi belgeler
yuriploc

1

"Bağlam-kök" denen benzer bir şeye ihtiyacım vardı. WSGIScriptAlias ​​kullanarak /etc/httpd/conf.d/ altında conf dosyasında yaptım:

myapp.conf:

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

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

</VirtualHost>

Artık uygulamama şu adresten erişebiliyorum: http: // localhost: 5000 / myapp

Kılavuza bakın - http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html


1

Flask ve PHP uygulamalarının nginx ve PHP5.6 ile bir arada bulunduğu çözümüm

Flask'ı kökte ve PHP'yi alt dizinlerde TUTUN

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

1 satır ekleyin

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 için NESTED LOCATIONS KULLANIN ve FLASK'ın kökte kalmasına izin verin

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;
    #}
}

Dikkatlice OKUYUN https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

Konum eşleştirmesini anlamamız gerekir (yok): Değiştirici yoksa, konum bir önek eşleşmesi olarak yorumlanır. Bu, bir eşleşme belirlemek için verilen konumun istek URI'sinin başlangıcıyla eşleştirileceği anlamına gelir. =: Eşittir işareti kullanılıyorsa, istek URI'si verilen konumla tam olarak eşleşiyorsa bu blok bir eşleşme olarak kabul edilecektir. ~: Tilde değiştiricisi varsa, bu konum büyük / küçük harfe duyarlı bir normal ifade eşleşmesi olarak yorumlanacaktır. ~ *: Yaklaşık işaret ve yıldız değiştiricisi kullanılırsa, konum bloğu, büyük / küçük harfe duyarlı olmayan normal ifade eşleşmesi olarak yorumlanacaktır. ^ ~: Bir kırat ve yaklaşık işareti değiştiricisi varsa ve bu blok en iyi normal olmayan ifade eşleşmesi olarak seçilirse, normal ifade eşleşmesi gerçekleşmez.

Nginx'in "konum" açıklamasına göre sıralama önemlidir:

Belirli bir istekle eşleşen konumu bulmak için, nginx önce önek dizelerini (önek konumları) kullanarak tanımlanan konumları kontrol eder. Bunlar arasında, en uzun eşleşen öneke sahip konum seçilir ve hatırlanır. Daha sonra normal ifadeler, yapılandırma dosyasındaki görünüm sırasına göre kontrol edilir. Normal ifadelerin aranması ilk eşleşmede sona erer ve ilgili konfigürasyon kullanılır. Normal ifadeyle eşleşme bulunmazsa, daha önce hatırlanan önek konumunun konfigürasyonu kullanılır.

Anlamı:

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

1

Hala bununla mücadele eden insanlar için, ilk örnek işe yarıyor, ancak kontrolünüz altında olmayan bir Flask uygulamanız varsa tam örnek burada:

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,
    )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.