WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。
WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的接口,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。
很多框架都自带了 WSGI server ,比如 Flask,webpy,Django、CherryPy等等。当然性能都不好,自带的 web server 更多的是测试用途,发布时则使用生产环境的 WSGI server或者是联合 nginx 做 uwsgi 。
[python] view plain copy
class Flask(_PackageBoundObject):
#中间省略一些代码
#请注意函数的说明,说得非常准确,这个wsgi_app是一个真正的WSGI应用
def wsgi_app(self, environ, start_response): #他扮演的是一个中间角色
”“”The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this::
app = MyMiddleware(app)
It’s a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
:param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
”“”
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
response = self.full_dispatch_request() #full_dispatch_request起到了预处理和错误处理以及分发请求的作用
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e)) #如果有错误发生,则生成错误响应
return response(environ, start_response) #如果没有错误发生,则正常响应请求,返回响应内容
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
class Flask(_PackageBoundObject):
中间省略一些代码
#请注意函数的说明,说得非常准确,这个wsgi_app是一个真正的WSGI应用
def wsgi_app(self, environ, start_response): #他扮演的是一个中间角色
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
:param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
"""
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
response = self.full_dispatch_request() #full_dispatch_request起到了预处理和错误处理以及分发请求的作用
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e)) #如果有错误发生,则生成错误响应
return response(environ, start_response) #如果没有错误发生,则正常响应请求,返回响应内容
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)</pre>
[python] view plain copy
class Flask(_PackageBoundObject):
#此处省略一些代码
def full_dispatch_request(self):
”“”Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
.. versionadded:: 0.7
”“”
self.try_trigger_before_first_request_functions() #进行发生真实请求前的处理
try:
request_started.send(self) #socket部分的操作
rv = self.preprocess_request() #进行请求的预处理
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response
class Flask(_PackageBoundObject):
1
2
3
4
5
6
7
此处省略一些代码
def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
[python] view plain copy
class Flask(_PackageBoundObject):
#省略一些代码
def try_trigger_before_first_request_functions(self):
”“”Called before each request and will ensure that it triggers
the :attr:`before_first_request_funcs` and only exactly once per
application instance (which means process usually).
:internal:
”“”
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs:
func()
self._got_first_request = True
class Flask(_PackageBoundObject):
1
2
3
4
5
6
7
省略一些代码
def try_trigger_before_first_request_functions(self):
"""Called before each request and will ensure that it triggers
the :attr:`before_first_request_funcs` and only exactly once per
application instance (which means process usually).
:internal:
"""
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs:
func()
self._got_first_request = True</pre>
再来看看_got_first_request 的定义,他的默认值是False
他的定义中可以明显看到, if the application started,this attribute is set to True.
[python] view plain copy
class Flask(_PackageBoundObject):
#省略一些代码
@property
def got_first_request(self):
”“”This attribute is set to “True“ if the application started
handling the first request.
.. versionadded:: 0.8
”“”
return self._got_first_request
class Flask(_PackageBoundObject):
@property
def got_first_request(self):
"""This attribute is set to ``True`` if the application started
handling the first request.
[python] view plain copy
class Flask(_PackageBoundObject):
#省略一些代码
def dispatch_request(self): #看函数定义,matches the URL and returns the value of the view or error.
”“”Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
proper response object, call :func:`make_response`.
.. versionchanged:: 0.7
This no longer does the exception handling, this code was
moved to the new :meth:`full_dispatch_request`.
”“”
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if getattr(rule, ‘provide_automatic_options’, False) \
and req.method == ‘OPTIONS’:
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args) #最终进入view_functions,取出url对应的视图函数的返回值
class Flask(_PackageBoundObject):
1
2
3
4
5
6
7
省略一些代码
def dispatch_request(self): #看函数定义,matches the URL and returns the value of the view or error.
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
proper response object, call :func:`make_response`.
.. versionchanged:: 0.7
This no longer does the exception handling, this code was
moved to the new :meth:`full_dispatch_request`.
"""
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if getattr(rule, 'provide_automatic_options', False) \
and req.method == 'OPTIONS':
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args) #最终进入view_functions,取出url对应的视图函数的返回值</pre>
1
Converts the return value from a view function to a real response object that is an instance of :attr:`response_class
[python] view plain copy
class Flask(_PackageBoundObject): #注意函数说明,converts the return value from view function to a real response object
#省略一部分代码
def make_response(self, rv):
”“”Converts the return value from a view function to a real
response object that is an instance of :attr:`response_class`.
The following types are allowed for `rv`:
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
======================= ===========================================
:attr:`response_class` the object is returned unchanged
:class:`str` a response object is created with the
string as body
:class:`unicode` a response object is created with the
string encoded to utf-8 as body
a WSGI function the function is called as WSGI application
and buffered as response object
:class:`tuple` A tuple in the form “(response, status,
headers)“ or “(response, headers)“
where `response` is any of the
types defined here, `status` is a string
or an integer and `headers` is a list or
a dictionary with header values.
======================= ===========================================
:param rv: the return value from the view function
.. versionchanged:: 0.9
Previously a tuple was interpreted as the arguments for the
response object.
”“”
status_or_headers = headers = None
if isinstance(rv, tuple):
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
if rv is None:
raise ValueError(‘View function did not return a response’)
if isinstance(status_or_headers, (dict, list)):
headers, status_or_headers = status_or_headers, None
if not isinstance(rv, self.response_class):
# When we create a response object directly, we let the constructor
# set the headers and status. We do this because there can be
# some extra logic involved when creating these objects with
# specific values (like default content type selection).
if isinstance(rv, (text_type, bytes, bytearray)):
rv = self.response_class(rv, headers=headers,
status=status_or_headers)
headers = status_or_headers = None
else:
rv = self.response_class.force_type(rv, request.environ)
if status_or_headers is not None:
if isinstance(status_or_headers, string_types):
rv.status = status_or_headers
else:
rv.status_code = status_or_headers
if headers:
rv.headers.extend(headers)
return rv
class Flask(_PackageBoundObject): #注意函数说明,converts the return value from view function to a real response object
#省略一部分代码
def make_response(self, rv):
"""Converts the return value from a view function to a real
response object that is an instance of :attr:`response_class`.
The following types are allowed for `rv`:
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
======================= ===========================================
:attr:`response_class` the object is returned unchanged
:class:`str` a response object is created with the
string as body
:class:`unicode` a response object is created with the
string encoded to utf-8 as body
a WSGI function the function is called as WSGI application
and buffered as response object
:class:`tuple` A tuple in the form ``(response, status,
headers)`` or ``(response, headers)``
where `response` is any of the
types defined here, `status` is a string
or an integer and `headers` is a list or
a dictionary with header values.
======================= ===========================================
:param rv: the return value from the view function
.. versionchanged:: 0.9
Previously a tuple was interpreted as the arguments for the
response object.
"""
status_or_headers = headers = None
if isinstance(rv, tuple):
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
if rv is None:
raise ValueError('View function did not return a response')
if isinstance(status_or_headers, (dict, list)):
headers, status_or_headers = status_or_headers, None
if not isinstance(rv, self.response_class):
# When we create a response object directly, we let the constructor
# set the headers and status. We do this because there can be
# some extra logic involved when creating these objects with
# specific values (like default content type selection).
if isinstance(rv, (text_type, bytes, bytearray)):
rv = self.response_class(rv, headers=headers,
status=status_or_headers)
headers = status_or_headers = None
else:
rv = self.response_class.force_type(rv, request.environ)
if status_or_headers is not None:
if isinstance(status_or_headers, string_types):
rv.status = status_or_headers
else:
rv.status_code = status_or_headers
if headers:
rv.headers.extend(headers)