wsgi python

tech2023-02-26  113

wsgi python

Beneath Django, Flask, Bottle, and every other Python web framework, lies the Web Server Gateway Interface, or WSGI for short. WSGI is to Python what Servlets are to Java — a common specification for web servers that allows different web servers and application frameworks to interact based on a common API. However, as with most things, the Python version is considerably simpler.

在Django , Flask , Bottle和所有其他Python Web框架之下,是Web服务器网关接口(简称WSGI)。 WSGI对Python而言就像Servlet对Java一样-Web服务器的通用规范,该规范允许不同的Web服务器和应用程序框架基于通用API进行交互。 但是,与大多数情况一样,Python版本要简单得多。

WSGI is defined in PEP 3333, which I encourage you to read as a reference if you want more information after this quick intro.

WSGI在PEP 3333中定义,如果您希望在此快速入门后有更多信息,建议您阅读WSGI作为参考。

This article will introduce you to the WSGI spec from an application developer’s perspective, and show you how to work directly with WSGI to create applications (if you so desire).

本文将从应用程序开发人员的角度向您介绍WSGI规范,并向您展示如何直接与WSGI一起创建应用程序(如果您愿意的话)。

您的第一个WSGI应用程序 (Your First WSGI App)

Here’s the most basic Python web app possible:

这是最基本的Python网络应用程序:

def app(environ, start_fn): start_fn('200 OK', [('Content-Type', 'text/plain')]) return ["Hello World!\n"]

That’s it! The whole file. Call it app.py and run it with any WSGI-compatible server and you’ll get a Hello World response with a 200 status. You can use gunicorn for this; just install it via pip (pip install gunicorn) and run it with gunicorn app:app. This command tells gunicorn to get the WSGI callable from the app variable in the app module.

而已! 整个文件。 将其命名为app.py,并在任何与WSGI兼容的服务器上运行它,您将收到状态为200的Hello World响应。 您可以为此使用gunicorn; 只需通过pip( pip install gunicorn )进行pip install gunicorn ,然后使用gunicorn app:app运行它gunicorn app:app 。 此命令告诉gunicorn从app模块中的app变量获取可调用的WSGI。

Right now, you should be pretty excited. Just 3 lines for a running application? That must be some sort of record (barring PHP, because mod_php is cheating). I bet you’re just raring to know more.

现在,您应该会非常兴奋。 运行的应用程序只有3行? 那一定是某种记录(除非PHP,因为mod_php在作弊)。 我敢打赌,您只是想知道更多。

So what are the essential parts of a WSGI application?

那么WSGI应用程序的基本部分是什么?

A WSGI application is a Python callable, such as a function, a class, or a class instance with a __call__ method

WSGI应用程序是Python 可调用的 ,例如具有__call__方法的函数,类或类实例。

The application callable must accept two arguments: the environ, which is a Python dict containing the request data, and start_fn, itself a callable.

可调用的应用程序必须接受两个参数: environ (它是包含请求数据的Python字典)和start_fn (本身是可调用的)。

The application must call the start_fn with two arguments: the status code (as a string), and a list of headers expressed as 2-tuples.

应用程序必须使用两个参数调用start_fn :状态码(作为字符串)和以2元组表示的标头列表。

The application returns an iterable containing the bytes in the response body in handy, streamable chunks — in this case, a list of strings containing just "Hello, World!". (If app is a class, this can be accomplished in the __iter__ method.)

该应用程序返回一个可迭代的内容,该内容包含响应主体中方便的可传输块中的字节-在这种情况下,该字符串列表仅包含"Hello, World!" 。 (如果app是一个类,则可以通过__iter__方法来完成。)

By way of example, these next two examples are equivalent to the first:

作为示例,以下两个示例与第一个示例等效:

class app(object): def __init__(self, environ, start_fn): self.environ = environ self.start_fn = start_fn def __iter__(self): self.start_fn('200 OK', [('Content-Type', 'text/plain')]) yield "Hello World!\n" class Application(object): def __call__(self, environ, start_fn): start_fn('200 OK', [('Content-Type', 'text/plain')]) yield "Hello World!\n" app = Application()

You might already be thinking of ways that you can use this information, but probably the most relevant one is writing middlewares.

您可能已经在思考可以使用这些信息的方式,但是可能最相关的一种是编写中间件。

爵士乐 (Jazzing It Up)

Middlewares are an easy way to extend the functionality of WSGI apps. Since you need only provide a callable, you can wrap it up in other functions however you please.

中间件是扩展WSGI应用程序功能的简便方法。 由于您只需要提供可调用对象,因此您可以根据需要将其包装在其他函数中。

For example, say we want to examine the contents of environ. We can easily create a middleware to do so, as in this example:

例如,假设我们要检查environ的内容。 我们可以轻松地创建一个中间件来做到这一点,如以下示例所示:

import pprint def handler(environ, start_fn): start_fn('200 OK', [('Content-Type', 'text/plain')]) return ["Hello World!\n"] def log_environ(handler): def _inner(environ, start_fn): pprint.pprint(environ) return handler(environ, start_fn) return _inner app = log_environ(handler)

Here, log_environ is a function that returns a function, which pretty-prints the environ argument before deferring to the original callback.

在这里, log_environ是一个返回函数的函数,该函数在推迟到原始回调之前漂亮地打印environ参数。

The advantage of writing middlewares this way is that the middleware and the handler don’t have to know or care about each other. You could easily bolt log_environ onto a Flask application, for example, since Flask apps are WSGI apps.

用这种方式编写中间件的好处是中间件和处理程序不必彼此了解或关心。 例如,由于Flask应用程序是WSGI应用程序,因此您可以轻松地将log_environ连接到Flask应用程序。

A few other useful middleware ideas:

其他一些有用的中间件思想:

import pprint def handle_error(handler): def _inner(environ, start_fn): try: return handler(environ, start_fn) except Exception as e: print e # Log error start_fn('500 Server Error', [('Content-Type', 'text/plain')]) return ['500 Server Error'] return _inner def wrap_query_params(handler): def _inner(environ, start_fn): qs = environ.get('QUERY_STRING') environ['QUERY_PARAMS'] = urlparse.parse_qs(qs) return handler(environ, start_fn) return _inner

You can use reduce to apply a bunch of middleware at once if you don’t want to make a big pyramid a the bottom of your file:

如果您不想在文件底部做一个大金字塔,则可以使用reduce一次应用一堆中间件:

# Applied from bottom to top on the way in, then top to bottom on the way out MIDDLEWARES = [wrap_query_params, log_environ, handle_error] app = reduce(lambda h, m: m(h), MIDDLEWARES, handler)

You can also write middleware that modifies the response, by taking advantage of the start_fn argument. Here’s a middleware that reverses the output if the Content-Type header is text/plain:

您还可以编写利用start_fn参数修改响应的中间件。 如果Content-Type标头是text/plain那么这是一个中间件,可以反转输出:

def reverser(handler): # A reverse function rev = lambda it: it[::-1] def _inner(environ, start_fn): do_reverse = [] # Must be a reference type such as a list # Override start_fn to check the content type and set a flag def start_reverser(status, headers): for name, value in headers: if (name.lower() == 'content-type' and value.lower() == 'text/plain'): do_reverse.append(True) break # Remember to call `start_fn` start_fn(status, headers) response = handler(environ, start_reverser) try: if do_reverse: return list(rev(map(rev, response))) return response finally: if hasattr(response, 'close'): response.close() return _inner

It’s a little more tangled thanks to the separation of start_fn and response, but still perfectly workable.

由于start_fn和response的分离,它有些纠结,但仍然可以正常使用。

Also note that, to be strictly spec-compliant with WSGI, we must check for a close method on the response and call it if present. Legacy WSGI applications may also return a write function instead of an iterable upon calling handler; if you want your middleware to support older applications, you may need to handle this case.

还要注意,为了严格符合WSGI规范,我们必须检查响应中的close方法,并在存在时调用它。 旧版WSGI应用程序还可能在调用handler时返回write函数而不是可迭代的handler ; 如果您希望中间件支持较旧的应用程序,则可能需要处理这种情况。

Once you start playing with raw WSGI a little bit, you start to understand why Python has literally dozens of web frameworks. WSGI makes it pretty simple to build something up starting from scratch. For example, you might be considering the problem of routing:

一旦开始使用原始WSGI,您就会开始理解为什么Python实际上有数十个Web框架。 WSGI使从头开始构建内容变得非常简单。 例如,您可能正在考虑路由问题:

routes = { '/': home_handler, '/about': about_handler, } class Application(object): def __init__(self, routes): self.routes = routes def not_found(self, environ, start_fn): start_fn('404 Not Found', [('Content-Type', 'text/plain')]) return ['404 Not Found'] def __call__(self, environ, start_fn): handler = self.routes.get(environ.get('PATH_INFO')) or self.not_found return handler(environ, start_fn)

Working with WSGI directly can be nice if you enjoy the flexibility of assembling libraries over

如果您喜欢在上面组装库的灵活性,那么直接使用WSGI可能会很好。

Template libraries: just drop in any template library you like (e.g. Jinja2, Pystashe) and return the rendered template from your handler!

模板库:只需放入您喜欢的任何模板库(例如Jinja2 , Pystashe ),然后从处理程序中返回渲染的模板!

Help your routing with a library like Routes or perhaps Werkzeug’s routing. Actually, take a look at Werkzeug if you want to use an ever-so-slight abstraction over WSGI.

使用诸如Routes之类的库或也许Werkzeug的routing帮助您的路由 。 实际上,如果您想对WSGI使用非常轻微的抽象,请看一下Werkzeug。

Use any database/migration libraries as you would with Flask or similar.

像使用Flask或类似工具一样使用任何数据库/迁移库。

Of course, for non-specialized applications, you’ll probably still want to use a framework just so that edge cases are properly handled and whatnot.

当然,对于非专业的应用程序,您可能仍想使用一个框架,以便正确处理边缘情况,而不会进行其他处理。

但是服务器呢? (But What About Servers?)

There are a bunch of ways to serve WSGI apps. We already talked about Gunicorn, which is a decent option. uWSGI is another great option. Just make sure you set up something like nginx in front of these to serve static assets and you should have a solid starting point.

有很多服务WSGI应用程序的方法。 我们已经讨论过Gunicorn ,这是一个不错的选择。 uWSGI是另一个不错的选择。 只要确保您在它们前面设置了类似nginx之类的内容来服务静态资产,就应该有一个坚实的起点。

And that’s all there is to it!

这就是全部!

翻译自: https://www.sitepoint.com/python-web-applications-the-basics-of-wsgi/

wsgi python

最新回复(0)