WEB开发基础知识+WSGI
1、WEB开发
CS 即客户端,服务端编程
客户端,服务端之间需要使用Socket ,约定协议,版本(往往使用的协议是TCP或UDP),指定地址和端口,就可以通信了。
客户端,服务端传输数据,数据就可以有一定的格式双方必须先约定好
BS编程,即Browser Server 开发
Browser 浏览器,一种特殊的客户端,支持HTTP(s)协议,能够通过URL 向服务器发起请求,等待服务器返回HTML等数据,并在浏览器内可视化展示的程序
Server 支持HTTP(s)协议,能够接受众多的客户端发起的http协议请求,经过处理,将HTML 等数据返回给浏览器
本质上说,BS 是一种特殊的CS,即客户端必须是一种支持HTTP的协议的且能够解析并渲染HTML的软件,服务端必须是能够接受多客服端HTTP访问的服务器软件。
HTTP协议底层基于TCP 协议实现。
- BS开发分为两端
- 客服端开发,或称为前端开发,HTML,CSS, JavaScript
- 服务端开发,Python 有WSGI,Django Flask, Tornado等
2、HTTP协议
Http协议是无状态协议
同一个客户端的两次请求之间没有任何关系,从服务器端角度来说,它不知道这两个请求来自同一个客户端
cookie
键值对信息
是一种客服端,服务端传递数据的技术
一般来说,cookie信息是在服务器端产生的,返回给客服端的。
浏览器发起每一请求时,都会把cookie 信息发给服务器端
服务器端收到浏览器发来的cookie信息,处理这些信息,可以用来判断这次请求是否和之前的请求有关联。
客户端可以自己设置cookie信息
URL组成
url 可以说是地址, uniform resource locator 同一资源定位符,每一个链接指向一个资源供客户端访问
schema://host【:psot】/path/...../[;url-params] [?query-string][#anchor]
例如:通过以下的URL 访问网页
http://www.baidu.com/python/index.html?id=5&name=python
访问静态资源是,通过上面的这个url访问的是网站的某个路径下的index.html 文件,而这个文件对应磁盘上的真实的访问路径,就会从磁盘上读取这个文件并把文件的内容发回浏览器端。
schema:协议 -----http https ftp ftps mailto等
一般都是默认80 端口,80 可以不写,否则都需要指明,域名会使用DNS 解析,域名会解析成IP才能使用,实际上会对解析后返回的TCP的80 端口发起访问。
query string (如:?key1=value1&key2=value2)查询字符串,问号用来和路径分开,后面key=value形式,且使用&符号分割开
HTTP消息:
消息分为 Request, Response
Request:浏览器向服务器发起的请求
Response:服务器对客服端请求的响应
请求和响应消息都是由请求行,Header消息报头,Body消息正文组成。
请求:
请求消息行:请求方法Method 请求路径 协议版本 CRLF
请求方法Method:
GET: 请求获取URL 对应的资源
POST:提交数据至服务器端
HEAD:和GET类似,不过不返回消息正文
常见传递消息的方式:
1、GET方法使用Query String ,搜索栏直接输入:
http://www.nagedu.com/python/index.html?id=5&name=python
2、POST 方法提交数据
表单:
使用post 提交表单后:
3、URL中本身就包含着信息
http://www.magedu.com/python/istudent/001
响应:
响应消息行:协议版本 状态码 描述信息CRLF
status code状态码:
重定向:( 访问两次)
B <---------> S (301,302)
------------->
无状态,有连接, 短连接:
无状态:指的是服务器无法知道2次请求之间的联系,即使是前后两次同一个浏览器也没有任何数据能够判断出是同一个浏览器的请求。后来可以通过cookie session 来判断
有连接:是因为基于TCP协议,是面向连接的,需要3次握手,四次断开
短连接:Http 1.1 之前都是一个请求一个连接,而TCP 的链接创建销毁成本高,对服务器有很大的影响,所以之后,支持keep-alive 默认也开启,一个连接打开后,会保持一段时间(可设置),浏览器在访问该服务器就是用这个TCp 连接,减轻服务器压力,提高效率。
3、WSGI:
WSGI 服务器----wsgiref
wsgiref 是Python提供的一个WSGI 参考实现库(只能自己测试用,不能用于生产环境)
sdgiref.simple_server 模块实现一个简单的WSGI Http 服务器
wsgiref.simple_server.make_server(host, port , app, server_class=WSGIServer,handler_class=WSGIRequestHandler) 启动一个WSGI 服务器
wsgiref.simple_server.demo_app(environ, start_response) 一个 两个参数的 函数,小巧完整的WSGI的应用程序的实现。
测试:
结果:
WSGI服务器作用:
-
-
- 监听HTTP 服务端口(TCPserver 默认80)
- 接受浏览器端的http请求并解析封装成environ 环境数据
- 负责调用应用程序,将environ数据和start_response 方法 两个参数传入给Application
- 将应用程序响应的正文封装成HTTP响应 报文返回浏览器
-
WSGI APP 应用程序端
- 应用程序应该是一个可调用对象,Python中应该是函数,类,实现了 __call__方法的类的实例
- 这个可调用对象应该接受两个参数
demo_app:源代码:
测试:可调用对象的实现,都必须返回一个可迭代对象
1 from wsgiref.simple_server import make_server, demo_app 2 ip = '127.0.0.1' 3 port = 8080 4 5 # 原码:return [stdout.getvalue().encode("utf-8")] 6 rest_str = b'www.jerry.com' 7 8 # # 函数实现 application 9 # def application(environ, start_response): 10 # start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) 11 # return [rest_str] 12 13 # 类实现 14 class Application: 15 def __init__(self,environ, start_response): 16 self.env = environ 17 # self.resp = start_response 18 # 必须且只能执行一次 19 start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) 20 21 def __iter__(self): 22 # self.resp("200 OK", [('Content-Type','text/plain; charset=utf-8')]) 23 yield rest_str 24 25 server = make_server(ip, port, Application) # demo_app 应用程序,可调用 26 27 28 # 类实现 可调用对象 29 class Application: 30 def __call__(self,environ, start_response): 31 start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) 32 return [rest_str] 33 34 35 server = make_server(ip, port, Application()) # demo_app 应用程序,可调用 36 37 38 server.serve_forever() # server.handle_request() 执行一次
environ:
environ 是包含HTTP请求信息的dict字典对象( Header 信息,除了首行,都是key: value)
1 ('ALLUSERSPROFILE', 'C:\\ProgramData') 2 ('APPDATA', 'C:\\Users\\Administrator\\AppData\\Roaming') 3 ('COMMONPROGRAMFILES', 'C:\\Program Files\\Common Files') 4 ('COMMONPROGRAMFILES(X86)', 'C:\\Program Files (x86)\\Common Files') 5 ('COMMONPROGRAMW6432', 'C:\\Program Files\\Common Files') 6 ('COMPUTERNAME', 'USER-20180710VW') 7 ('COMSPEC', 'C:\\Windows\\system32\\cmd.exe') 8 ('FP_NO_HOST_CHECK', 'NO') 9 ('GOPATH', 'F:\\goproject') 10 ('GOROOT', 'D:\\go_sdk\\go') 11 ('HOMEDRIVE', 'C:') 12 ('HOMEPATH', '\\Users\\Administrator') 13 ('LOCALAPPDATA', 'C:\\Users\\Administrator\\AppData\\Local') 14 ('LOGONSERVER', '\\\\USER-20180710VW') 15 ('NUMBER_OF_PROCESSORS', '4') 16 ('OS', 'Windows_NT') 17 ('PATH', 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;D:\\Git\\cmd;D:\\nodejs\\;D:\\python3.7\\Scripts\\;D:\\python3.7\\;C:\\Users\\Administrator\\AppData\\Roaming\\npm;D:\\Microsoft VS Code\\bin;D:\\go_sdk\\go\\bin') 18 ('PATHEXT', '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC') 19 ('PROCESSOR_ARCHITECTURE', 'AMD64') 20 ('PROCESSOR_IDENTIFIER', 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel') 21 ('PROCESSOR_LEVEL', '6') 22 ('PROCESSOR_REVISION', '3c03') 23 ('PROGRAMDATA', 'C:\\ProgramData') 24 ('PROGRAMFILES', 'C:\\Program Files') 25 ('PROGRAMFILES(X86)', 'C:\\Program Files (x86)') 26 ('PROGRAMW6432', 'C:\\Program Files') 27 ('PSMODULEPATH', 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\') 28 ('PUBLIC', 'C:\\Users\\Public') 29 ('PYCHARM_HOSTED', '1') 30 ('PYTHONIOENCODING', 'UTF-8') 31 ('PYTHONPATH', 'E:\\code_pycharm') 32 ('PYTHONUNBUFFERED', '1') 33 ('SESSIONNAME', 'Console') 34 ('SYSTEMDRIVE', 'C:') 35 ('SYSTEMROOT', 'C:\\Windows') 36 ('TEMP', 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp') 37 ('TMP', 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp') 38 ('USERDOMAIN', 'USER-20180710VW') 39 ('USERNAME', 'Administrator') 40 ('USERPROFILE', 'C:\\Users\\Administrator') 41 ('WINDIR', 'C:\\Windows') 42 ('WINDOWS_TRACING_FLAGS', '3') 43 ('WINDOWS_TRACING_LOGFILE', 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log') 44 ('_DFX_INSTALL_UNSIGNED_DRIVER', '1') 45 ('SERVER_NAME', 'USER-20180710VW') 46 ('GATEWAY_INTERFACE', 'CGI/1.1') 47 ('SERVER_PORT', '8080') 48 ('REMOTE_HOST', '') 49 ('CONTENT_LENGTH', '') 50 ('SCRIPT_NAME', '') 51 ('SERVER_PROTOCOL', 'HTTP/1.1') 52 ('SERVER_SOFTWARE', 'WSGIServer/0.2') 53 ('REQUEST_METHOD', 'GET') 54 ('PATH_INFO', '/') 55 ('QUERY_STRING', '') 56 ('REMOTE_ADDR', '127.0.0.1') 57 ('CONTENT_TYPE', 'text/plain') 58 ('HTTP_HOST', '127.0.0.1:8080') 59 ('HTTP_CONNECTION', 'keep-alive') 60 ('HTTP_CACHE_CONTROL', 'max-age=0') 61 ('HTTP_UPGRADE_INSECURE_REQUESTS', '1') 62 ('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Maxthon/5.2.4.3000') 63 ('HTTP_ACCEPT', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8') 64 ('HTTP_DNT', '1') 65 ('HTTP_ACCEPT_ENCODING', 'gzip, deflate') 66 ('HTTP_ACCEPT_LANGUAGE', 'zh-CN') 67 ('wsgi.input', <_io.BufferedReader name=384>) 68 ('wsgi.errors', <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>) 69 ('wsgi.version', (1, 0)) 70 ('wsgi.run_once', False) 71 ('wsgi.url_scheme', 'http') 72 ('wsgi.multithread', True) 73 ('wsgi.multiprocess', False) 74 ('wsgi.file_wrapper', <class 'wsgiref.util.FileWrapper'>)
start_response
它是一个可调用对象, 有3个参数,定义如下:
start_response( status, response_headers, exc_info=None), header部分,(‘ key ’, ' value ')
start_response应该在返回可迭代对象之前嗲用,因为他是 header,return返回的是一个可迭代对象,不一定是一个列表。
服务器端
服务器程序需要调用符合上述定义的可调用对象APP,传入 environ, start_response
APP处理后,返回响应头和可迭代对象正文,由服务器的封装成 http报文,返回给浏览器
测试:
WSGI WEB 服务器:
本质上就是一个TCP 服务器,监听在特点的端口上
支持HTTP 协议,能够将HTTP请求报文进行解析,能够把响应数据进行HTTP协议的报文封装并返回浏览器
实现了WSGI 协议,该协议约定了和应用恒旭之间的接口,https://www.python.org/dev/peps/pep-0333/
WSGI APP 应用程序:
遵从WSGI协议
本身是一个可调用对象
调用start_response,返回响应头部
返回包含正文的可迭代对象
WSGI 框架库,往往 可以看做增强的更加复杂的Application
WSGI是Web Server Gateway Interface的缩写。以层的角度来看,WSGI所在层的位置低于CGI。但与CGI不同的是WSGI具有很强的伸缩性且能运行于多线程或多进程的环境下,这是因为WSGI只是一份标准并没有定义如何去实现。实际上WSGI并非CGI,因为其位于web应用程序与web服务器之间,而web服务器可以是CGI,直接以模块编译进去,mod_python(注:现通常使用mod_wsgi代替),FastCGI或者是一个定义了WSGI标准的web服务器就像python标准库提供的独立WSGI服务器称为wsgiref。