当前位置: 首页 > 后端技术 > Python

Flask0.2源码解读:Flask中HTTP消息的流程与处理

时间:2023-03-26 13:35:07 Python

〇,Flask渐进式源码解读前言解读:0.1本期指南Flask0.2提供了快速生成JSON响应的功能:jsonify,如何实现?网络中的字节流数据是如何传给Flask的,Flask又是如何生成字节流数据返回给客户端的呢?我们从服务器接收HTTP消息开始。1、服务器接收HTTP报文HTTP报文是“一问一答”的形式。客户端提出问题(请求),服务器回答(响应)。是先有客户端还是先有服务器?去小商店买冰淇淋。如果老板不在店里,在店里喊:“老板,吃冰淇淋”,??老板会回复吗?不是,因为老板没有收到消息,所以老板必须在线并且处于收到消息的状态,我们发出的“请求”才能得到“回应”。客户端和服务器也是如此。必须先在服务端监听请求,才能接收到客户端发送的请求。客户端发送的HTTP报文有一个目标地址:host:port,基于TCP/IP协议传输。套接字将TCP/IP层的复杂操作封装成几个简单的接口,供应用层调用网络中的实现程序。沟通。在UNIX操作系统中,一个套接字就是一个文件。在服务器端调用socket接口监听TCP请求,实际上是创建了一个可读文件。当数据写入文件时,它会收到来自客户端的消息。问。中提到,执行serve_forever启动Flask服务,我们具体看一下:def_eintr_retry(func,*args):whileTrue:try:returnfunc(*args)except(OSError,select.error)作为e:如果e.args[0]!=errno.EINTR:raiseclassBaseServer:defserve_forever(self,poll_interval=0.5):self.__is_shut_down.clear()try:whilenotself.__shutdown_request:r,w,e=_eintr_retry(select.select,[self],[],[],poll_interval)ifself.__shutdown_request:breakifselfinr:self._handle_request_noblock()最后:self.__shutdown_request=Falseself.__is_shut_down.set()r,w,e=_eintr_retry(select.select,[self],[],[],poll_interval)相当于执行select.select([self],[],[],poll_),这是一个系统调用,返回值是三个包含就绪对象的列表。如果返回值r非空,说明可读套接字文件创建成功,即启动套接字服务,开始请求。ifselfinr:self._handle_request_noblock()如果selfinr为True,说明socket监听服务已经启动,服务启动后会继续执行self._handle_request_noblock()。def_handle_request_noblock(self):try:request,client_address=self.get_request()exceptsocket.error:returnifself.verify_request(request,client_address):try:self.process_request(request,client_address)除了:selfer_stler(,client_address)self.shutdown_request(请求)else:self.shutdown_request(request)classTCPServer(BaseServer):defget_request(self):returnself.socket.accept()defprocess_request(self,request,client_address):self.finish_request(request,client_address)self.shutdown_request(request)deffinish_request(self,request,client_address):self.RequestHandlerClass(request,client_address,self)self._handle_request_noblock()调用self.get_request(),self.get_request()调用socket.accept(),该函数被动接受来自TCP客户端的连接,等待连接到达(阻塞型,如果没有连接到达,程序会停留在这个地方,直到请求到达才会执行)。2、Flask中HTTP消息的流程收到请求后,调用self.process_request()->self.finish_request()->self.RequestHandlerClass(request,client_address,self),在Flask中渐进式源码解读:0.1、2,Flask是如何接收请求的?分析进去,调用self.RequestHandlerClass(request,client_address,self),然后调用WSGIRequestHandler.__init__()->BaseRequestHandler.__init__()->self.handle()->Flask.__call__(),会传递请求烧瓶处理。在BaseRequsetHandler.__init__()中调用self.setup(),该方法被StreamRequestHandler覆盖:classStreamRequestHandler(BaseRequestHandler):rbufsize=-1wbufsize=0timeout=Nonedisable_nagle_algorithm=Falsedefsetup(self):self.connection=self.requestifself.timeoutisnotNone:self.connection.settimeout(self.timeout)ifself.disable_nagle_algorithm:self.connection.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,True)self.rfile=self.connection.makefile('rb',self.rbufsize)self.wfile=self.connection.makefile('wb',self.wbufsize)创建两个io流:用于读取的self.rfile和用于写入的self.wfile。socket接收到的请求数据可以从self.rfile中读取,socket要返回给客户端的响应数据保存在self.wfile中。调用self.wfile.flush()会将数据发送到客户端。类WSGIRequestHandler(BaseHTTPRequestHandler,object):defrun_wsgi(self):app=self.server.appenviron=self.make_environ()defwrite(data):...self.wfile.write(data)self.wfile.flush()defexecute(app):application_iter=app(environ,start_response)try:fordatainapplication_iter:write(data)#确保headers被发送ifnotheaders_sent:write('')finally:ifhasattr(application_iter,'close'):application_iter.close()application_iter=NoneFlask.__call__()返回一个可迭代对象,后续迭代,将数据写入io流(write(data))并发送给客户端。3、Flask生成一个JSON格式的响应在响应中,Content-Type头告诉客户端实际返回的内容的内容类型。要生成JSON格式的响应,只需要做两件事:设置标头:Content-Type:application/json将数据转换为JSON格式的字符串defjsonify(*args,**kwargs):returncurrent_app.response_class(json.dumps(dict(*args,**kwargs),indent=Noneifrequest.is_xhrelse2),mimetype='application/json')jsonify函数做这两件事。设置header:mimetype='application/json'转换格式:`json.dumps(dict(args,*kwargs),indent=Noneifrequest.is_xhrelse2)`下面的例子,在路由函数中,传入数据jsonify并返回以生成JSON格式的响应。@app.route('/_get_current_user')defget_current_user():returnjsonify(username=g.user.username,email=g.user.email,id=g.user.id)"""返回JSON格式响应{"username":"admin","email":"admin@localhost","id":42}"""四、更快的JSON数据JSON是前后端数据最流行的方式之一交互,如果整个Flask项目的API返回的数据都是JSON格式的,最后每个视图函数都调用一次jsonify函数是非常多余的。是否可以不调用jsonify直接返回Python原生数据类型/自定义数据类型(如SQLAlchemy的Model类型)生成JSON化响应?让我们尝试对Flask进行一些框架级别的修改。要实现上述目标,只需在Flask生成响应之前添加以下两个操作:设置标头:Content-Type:application/json将数据转换为JSON格式的字符串4.1设置标头:Content-Type:当application/json生成response,headerContent-Type的值默认为Flask.response_class中default_mimetype属性的值,所以只需要继承response基类,重写default_mimetype='application/json'即可替换Flaskresponse班级。实现如下:fromwerkzeugimportResponseasResponseBaseclassJSONResponse(ResponseBase):default_mimetype='application/json'Flask.response_class=JSONResponse4.2将数据转换成JSON字符串视图函数返回值放入响应体对象中烧瓶。在make_response方法中实现,重写Flask.make_response判断接收到的参数是否需要格式化为JSON格式,必要时转为JSON格式。例如自动将dict、list格式的数据转换成JSON格式的response,如下:fromflaskimportFlaskasFlaskBaseclassFlask(FlaskBase):defmake_response(self,rv):ifisinstance(rv,(dict,list)):rv=json.dumps(rv)returnFlaskBase.make_response(self,rv)视图函数直接返回dict和list生成JSON格式的响应,无需调用jsonify进行处理。示例:@app.route('/json/list')deftest_json():#returnjsonify([1,2,3])return[1,2,3]@app.route('/json/dict')deftest_json():#returnjsonify({'hello':'world','name':'huaiyue'})return{'hello':'world','name':'huaiyue'}见https://github.com/yyywang/fl...Version:python2.7,werkzeug==0.6.1,Flask==0.2参考资料:[1]Flask改动。(日期不详)。2023年2月19日检索自https://flask.palletsprojects...[2]MDN网络文档。(日期不详)。2023年2月20日检索自https://developer.mozilla.org...本文源代码:https://github.com/yyywang/fl...