运维开发网

49web开发1_http_wsgiref

运维开发网 https://www.qedev.com 2020-03-20 10:44 出处:51CTO 作者:chaijowin
目录cs和bs:... 1http协议:... 2http消息:... 3Request:... 3常见传递信息的方式:... 3Response:... 4wsgi:... 5wsgi app应用程序端:... 6wsgi server服务器端:... 7测试用命令:... 9QUERY_STRING查询字符串解析:... 9   web开发: cs和bs:c和s之间需使用socket,约定协

 

目录

cs和bs:... 1

http协议:... 2

http消息:... 3

Request:... 3

常见传递信息的方式:... 3

Response:... 4

wsgi:... 5

wsgi app应用程序端:... 6

wsgi server服务器端:... 7

测试用命令:... 9

QUERY_STRING查询字符串解析:... 9

 

 

 

web开发:

 

cs和bs:

c和s之间需使用socket,约定协议、版本(往往使用tcp、udp),指定地址和端口,就可以通信;

c和s传输数据,数据可以有一定的格式,双方必须先约定好;

 

b和s:

b,一种特殊client,支持http(s)协议,能通过url向服务器发起请求,等待服务器端返回html等数据,并在browser内可视化展示的程序;

s,支持http(s),能接受众多客户端发起的http协议请求,经过处理,将html等数据返回给b;

 

本质上,bs是一种特殊的cs:

即客户端必须是一种支持http协议且能解析并渲染html的软件;

服务端必须是能接收多客户端的http访问的服务器软件;

 

客户端开发或前端开发;

服务端开发,py可学wsgi、django、flask、tornado等;

 

py,web框架:

早期cgi;

wsgi,web server gateway interface,可看作是一种底层协议,它规定了服务器程序和应用程序各自实现什么接口,py的实现为wsgiref库;

flask,基于wsgi,微框架;

django,基于wsgi,开源的web框架,巨无霸;

 

 

http协议:

是无状态的、面向连接的协议;

 

无状态,同一个客户端的两次请求,从服务端角度来说,它并不知道这两个请求来自同一个客户端;

解决:通过cookie(session或token)来判断;

 

面向连接:

有连接,基于tcp是面向连接,需3次握手4次断开;

短连接:http1.0是一个请求一个连接,而tcp创建销毁成本高,对server有很大影响;http1.1,支持keep-alive,默认开启,一个连接打开后会保持一段时间(可设置),b再访问该s就使用这个tcp连接,减轻了服务器压力,提高了效率;

 

cookie:

为解决http的无状态,用到cookie(sessionID或token);

一般,cookie信息是服务端生成,返回给客户端的;

客户端可自己设置cookie信息;

键值对信息,有时效;

browser发起每一个请求时,都会把cookie信息发给服务端;

是一种客户端、服务端传递数据的技术;

服务端可通过判断这些信息,来确定这次请求是否和之前的请求有关联,服务端为了认识同一个client,用sessionID或token,sessionID、token可理解为是一种标识,这些标识要么通过cookie带,要么通过url带,要么通过request body带;

 

注:

sessionID,保存在服务端内存中,随着会话关闭而消亡;

token,替代sessionID,server发给client的令牌,server通过此标识来认client;

 

 

url组成:

uniform resource locator统一资源定位符,每一个链接指向一个资源供客户端访问;

schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]

schema,模式、协议,http、ftp、file、mailto、mysql等;

host:port,80默认可不写,有域名解析,实际上应该是IP;

/path/to/resource,path指向资源的路径;

?key1=value1&key2=value2,query string查询字符串,问号分割,后面key=value形式且用&分割;

 

 

http消息:

分为Request、Response,请求和响应都由请求消息行(请求行)、header消息报头、body消息正文组成;

 

Request:

request method请求方法:

GET,请求获取url对应的资源;

POST,提交数据至服务器端;

HEAD,和GET类似,不过不返回消息正文;

 

例:

   

49web开发1_http_wsgiref

GET / HTTP/1.1   #请求行,分别为:请求方法 请求路径 协议版本CRLF,CRLF为回车换行

Host:

User-Agent:

Accept:

Accept-Language:

Accept-Encoding:

Cookie:

Connection:

Upgrade-Insecure-Requests:   #header消息报头

 

body:   #两个CRLF后是body

 

 

常见传递信息的方式:

1、GET方法使用query string:

例:

http://www.magedu.com/python/index.html?id=5&name=python

 

2、POST方法提交数据:

例:

http://127.0.0.1:9999/xxx/yyy?id=5&name=python

使用表单提交数据,文本框name为age、weigth、height

请求消息如下:

POST /xxx/yyy?id=5name=python HTTP/1.1

HOST: 127.0.0.1:9999

content-length: 26

content-type: application/x-www-form-urlencoded

 

age=5&wegith=80&height=170

 

3、url中本身就包含着信息:

http://www.magedu.com/python/student/001

 

注:

ajax就是用以上3种方式传输的,不过是异步方式;

 

客户端传参的几种方式:

1、通过URL路径传递,如http://ip:port/news/1/2,两个参数,新闻类型id和页码;

2、通过query string传递,如http://ip:port/news?category=1&page=2;

3、通过body请求体传递,又可根据数据格式分为k-v对、form数据、非form数据(json|xml);

4、通过http header传递;

 

 

Response:

例:

49web开发1_http_wsgiref

HTTP/1.1 200 OK   #响应消息行,依次为:协议版本 状态码 Message

 

status code状态码:

1XX,提示信息,表示请求已被成功接收,继续处理;

2XX,表示正常响应;

         200,正常返回了网页内容;

         201,Created;

3XX,重定向;

         301,页面永久性移走,永久重定向,返回新的url,b会根据返回的url发起新的request请求;

         302,临时重定向;

         304,资源未修改,b使用本地缓存;

4XX,客户端请求错误;

         404,Not Found,网页找不到;

         400,请求语法错误;

         401,请求要求身份验证;

         403,服务器拒绝请求;

5XX,服务器端错误;

         500,服务器内部错误;

         502,上游服务器错误,如nginx反向代理时;

 

 

 

例,编写类flask框架:

使用wsgi开发框架;

目的:

学习web框架的工作机制,了解众多框架背后的技术;

学习API封装,学习框架封装的思想,并提供友好的编程接口;

 

注:

一般公司会直接使用类似于django这样的框架,但一旦代码规模到了一定阶段,就需要对框架作二次开发,定制改版,所以了解框架背后的技术非常重要;

 

wsgi:

49web开发1_http_wsgiref

wsgi,规定了服务器端和应用程序之间的接口;

请求、响应的数据都要经wsgi server转发;

wsgi app看不到是哪个socket建立的连接(除非改wsgi接口);

 

wsgi app应用程序端:

1、应是一个可调用对象,py中应是函数、类、实现了__call__方法的类的实例;

2、这个可调用对象应接收2个参数,如

def application(environ,start_response):   #函数实现

         return [res_str]

class Application:   #类实现

         def __init__(self,environ,start_response):

                   pass

         def __iter__(self):

                   yield res_str

class Application:   #类实现

         def __call__(self,environ,start_response):

                   return [res_str]

3、以上三种实现(三种都有用,框架简单用函数,框架复杂用类多些,更复杂都用),必须返回一个可迭代对象;

 

environ、start_response参数:

这两个参数可以是任何合法名,一般默认用这两个名字;

 

environ,是包含http请求信息的dict对象:

REQUEST_METHOD   #请求方法,GET、POST等

PATH_INFO   #url中的路径部分

QUERY_STRING   #查询字符串

SERVER_NAME,SERVER_PORT

HTTP_HOST   #IP:PORT

SERVER_PROTOCOL   #协议

HTTP_USER_AGENT   #UserAgent信息,对互联网公司非常重要,可对用户作精准分析

 

start_response是一个可调用对象,有3个参数:

         start_response(status,response_headers,exc_info=None)   #start_response应在返回可迭代对象前调用,因为它返回的是response header,而application返回的可迭代对象response body

status   #状态码

response_headers   #是一个元素为二元组列表,如[('Content-Type','text/plain;charset=utf-8')],用二元组模拟字典

exc_info   #在错误处理时用

 

注:

'text/plain;charset=utf-8'

'text/html;charset=utf-8'   #对中文开发,此处要通知browser使用的编码,browser只认此处的通知,以让b达到自动检测编码;chrome中工具-->编码

'text/application;charset=utf8-8'

 

 

wsgi server服务器端:

需调用符合上述定义的可调用对象,传入environ、start_response,拿到返回的可迭代对象,返回给客户端;

 

wsgiref库

是一个wsgi参考实现库,仅测试用,生产不能用;

wsgiref.simple_server,实现一个简单的wsgi http服务器;

 

from wsgiref import simple_server

simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):   #启动一个wsgi server

simple_server.demo_app(environ,start_response):   #一个函数,小巧完整的wsgi app的实现

 

例:

from wsgiref.simple_server import make_server, demo_app

 

ip = '127.0.0.1'

port = 9999

 

server = make_server(ip, port, demo_app)

server.serve_forever()   #另server.handle_request()一次,Handle one request, possibly blocking.

server.shutdown()

server.server_close()

49web开发1_http_wsgiref

 

例:

from wsgiref.simple_server import make_server

 

def application(environ:dict, start_response):

    print(type(environ))

    html = '<h1>test</h1>'

    start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])

    return [html.encode()]   #encode()同encode("utf-8")

 

ip = '127.0.0.1'

port = 9999

server = make_server(ip, port, application)

server.serve_forever()

server.shutdown()

server.server_close()

输出:

<class 'dict'>

127.0.0.1 - - [10/Sep/2018 11:20:40] "GET / HTTP/1.1" 200 13

<class 'dict'>

127.0.0.1 - - [10/Sep/2018 11:20:40] "GET /favicon.ico HTTP/1.1" 200 13

 

chrome:

工具-->编码;

工具-->开发者工具-->Network,左侧Name处点cn.bing.com,右侧查看Headers,view source或view parsed;

 

 

测试用命令:

curl -I http://ip:port/xxx?id=5   #-I,使用HEAD方法

curl -X POST http://ip:port/yyy -d '{'x':2}   #-X指定方法,-d传输数据

 

 

QUERY_STRING查询字符串解析:

1、自己解析:

例:

def application(environ:dict, start_response):

    qstr = environ.get('QUERY_STRING')

    print(qstr)   #拿到id=5&name=jowin

    for pair in qstr.split('&'):

        k,_,v = pair.partition('=')

        print(k,v)

    html = '<h1>test</h1>'

    start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])

    return [html.encode()]

输出:

id=5&name=jowin

id 5

name jowin

 

2、使用cgi模块:

def parse_qs(qs, keep_blank_values=0, strict_parsing=0):

    """Parse a query given as a string argument."""

    warn("cgi.parse_qs is deprecated, use urllib.parse.parse_qs instead",

         DeprecationWarning, 2)

    return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing)

cgi.parse_qs(qs)已过期,建议使用urllib.parse.parse_qs(qs)

例:

def application(environ:dict, start_response):

    qstr = environ.get('QUERY_STRING')

    print(qstr)

    import cgi

    print(cgi.parse_qs(qstr))   #转为dict

    html = '<h1>test</h1>'

    start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])

    return [html.encode()]

输出:

id=5&name=jowin

{'id': ['5'], 'name': ['jowin']}

 

例:

def application(environ:dict, start_response):

    qstr = environ.get('QUERY_STRING')

    print(qstr)

    from urllib.parse import parse_qs,parse_qsl

    print(parse_qs(qstr))   #dict;若query_string为?id=5&name=jowin,tom,解析结果为{'name': ['jowin,tom'], 'id': ['5']},注意['jowin,tom']不是多值;若query_string为?id=5&name=jowin&name=tom&age=&age=18,19,解析结果为{'name': ['jowin', 'tom'], 'id': ['5'], 'age': ['18,19']}

    print(parse_qsl(qstr))   #二元组列表;若query_string为?id=5&name=jowin,tom,解析结果为[('id', '5'), ('name', 'jowin,tom')];若query_string为?id=5&name=jowin&name=tom&age=&age=18,19,[('id', '5'), ('name', 'jowin'), ('name', 'tom'), ('age', '18,19')]

    html = '<h1>test</h1>'

    start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])

    return [html.encode()]

输出:

id=5&name=jowin

{'name': ['jowin'], 'id': ['5']}

[('id', '5'), ('name', 'jowin')]

 

 

 

 

0

精彩评论

暂无评论...
验证码 换一张
取 消