html get请求_写个简单的wsgi应用实现解析get&post请求

fbca180e543a426c7715d3d7034bcbde.png

请求解析

解析get请求

前言:说学习曲线陡峭,意思是说从零基础入门到实战,每一步都必须有前一步的基础,后面才会学懂,并且达到最后的 目标,所需步数极其多。编程零基础入门后,可以沿着学习曲线从下往上学,这是惯常方法,但是这样有个缺点,就是每天都会遇到新知识,新概念,当时学了还不一定真正掌握,加上中间遗漏的知识点,后面就有可能越来越难,即使这条路是由一个不错的老师,通过视频、直播教授带领,你也未必走到终点(我就有过类似经历)。我的方法是从两头往中间走,学完python ,直接学应用,可以巩固语法知识点,同时因为有了终极目标的驱动,它会带领你,以自己为主线,书、资料为自己所用,我称这种学习法为:目标导向下的,多模块儿穿插同时学习——立体学习法。使用flask实现一个网站就是这样一个目标,实现过程中,不断查漏补缺学习知识模块,这里的wsgi规范,应用就是这个缺,当了解完wsgi后,再看flask的解析,响应,即使不看源码也大致了解运行机制,知其所以然,后续还可以再补补Werkzeug,试着去实现一个类似Flask的框架。学习链接先放在这里
如果一个人能完成一个曲线,那他以后也可以使用同样的方法,去完成更多曲线。"学习知识"与"掌握学习能力"比,人更应该掌握后者(包括:想象力,指定计划的能力,实操力,明确化具体化知识的能力)

WSGI应用是如何对这个请求进行解析的呢?

运行上篇的environment.py,并浏览器输入:http://192.168.0.105:8051/?age=10&hobbies=software&hobbies=tunning

可以在网页上中可以看到许多environ字典里的变量和值,包括 QUERY_STRING: age=10&hobbies=software&hobbies=tunning REQUEST_METHOD: GET

  • GET 请求是访问、获取页面的请求
  • GET请求是如何被解析的
  1. 当请求(request)方法为GET,表单变量将通过查询字符串传递给服务器(由URL里?号后面部分,即:查询字符串)
  2. 注意这里的hobbies出现了两次,因为在表单里有checkbox多选,或用户在URL里多次输入hobbies
  3. 代码示例: 下面代码是最简单的wsgi应用,其提供一个页面,当访问它(直接访问页面,并在Url里提交查询字符串),此时请求是GET方法,产生简单的响应(即,服务器调用application函数,当客户端使用GET方法访问该页面时,该应用使用parse_qs对URL里的查询字符串进行处理,将其放入一个字典处理后,调用回调函数start_response返回HTML响应状态码及响应头部,并返回响应BODY)
```python
from wsgiref.simple_server import make_server
from urllib import parse

def application(environ,start_response):
    d = parse.parse_qs(environ['QUERY_STRING'])
    print (d)
    start_response('200 ok', [('Content-Type','text/html')])
    content = "<h>hello web,i love you cat_%s and %s</h1>"%(environ['PATH_INFO'][1:] or 'web',environ['QUERY_STRING'])
    # environ['PATH_INFO'][1:]表示URL里的路径,即:根域名后,?前的部分
    return [content.encode('utf-8')]

httpd = make_server('',8001,application)
print ("servering on 8001") 
httpd.serve_forever()
servering on 8001
{'name': ['sam'], 'hobbies': ['softerware']}
{}


192.168.0.108 - - [22/May/2020 12:29:57] "GET /HOME?name=sam&hobbies=softerware HTTP/1.1" 200 69
192.168.0.108 - - [22/May/2020 12:29:57] "GET /favicon.ico HTTP/1.1" 200 49

在浏览器里输入:http://192.168.0.105:8001/HOME?name=sam&hobbies=softerware. 查询字符串,?开始并变量间隔用&隔开

运行结果为:

hello web,i love you cat_HOME and name=sam&hobbies=softerware

GET请求时,在Flask里,却是这样写的

不用准备响应报文首部(状态码,文件类型),也不用调用回调函数start_response(), 这些相关的请求解析,响应封装,实际 已经被werkzeug完成,而Flask子类化了,Werkzeug的请求,响应对象,并添加程序的其他特定功能,后续再学习Werkzeug究竟是如 何实现这些动作的。

解析请求,获取URL查询字符串,FLASK 是这样子做的:

from flask import Flask,request

app =  Flask(__name__)

@app.rout('/hello')
def hello():
    name = request.args.get('name','Flask')
    # agrs是来自Werkzeug,的不变一键多值字典对象,存储解析后的查询字符串
    return "<h1>hello %s!</h1>"% name

请求URL的处理

  • 在WSGI应用里,使用:(environ['PATH_INFO'][1:] 获得URL的路径
  • 在FLASK里获得路径:request.path
  • FLASK里有更完善功能,包括,request.full_path、request.host、request.host_url、request.base_url、request.url_root
参考网站链接
html语法知识点
HTML中的input标签的用法: <input> 标签用于搜集用户信息。
  • type 属性值,输入字段拥有很多种形式。输入字段可以是文本字段、复选框、掩码后的文本控件、单选按钮、按钮等等。
  • value属性是input元素规定值。对于不同类型的input元素,value属性用法也不同:
    • type 属性为:"button","reset","submit"类型的,value定义按钮上的文本
    • type 属性为:"text","password","hidden"类型,value定义输入字段的初始(默认)值
    • type属性为"checkbox", "radio", "image" 类型的,value定义与input元素相关的值。该值会发送到表单的action URL。 注释:value属性对于<input type="checkbox"><inputtype="radio">是必需的。 注释:value属性不适用于<input type="file">
  • name属性定义和用:name 属性规定 input 元素的名称。name 属性用于对提交到服务器后的表单数据进行标识,或者在客户端通过 JavaScript 引用表单数据。 注释:只有设置了 name 属性的表单元素才能在提交表单时传递它们的值。

用GET方法传递表单数据,wsgi应用如何解析,并产生响应的呢?

首先是HTML部分

html = """
<html>
<body>
   <form method="get" action="">
        <p>
           Age: <input type="text" name="age" value="%(age)s">
        </p>
        <p>
            Hobbies:
            <input
                name="hobbies" type="checkbox" value="software"
                %(checked-software)s
            > Software
            <input
                name="hobbies" type="checkbox" value="tunning"
                %(checked-tunning)s
            > Auto Tunning
        </p>
        <p>
            <input type="submit" value="Submit">
        </p>
    </form>
    <p>
        Age: %(age)s<br>
        Hobbies: %(hobbies)s
    </p>
</body>
</html>
"""
语法复习: 这里的%()s用法是:用字典来传递真实值。 print ("I'm %(c)s. I have %(l)d yuan." % {'c':'hungry','l':22}) 结果:I'm hungry.I have 22yuan

代码示例: HTML里的form标签,指明浏览器访问时使用GET方法,当使用GET方法时,When the request method is GET the form variables will be sent in the URL in the part called query string, that is, everything after the ?

from wsgiref.simple_server import make_server
from urllib import parse
html="""
<html>
<body>
   <form method="get" action="">
        <p>
            Hobbies:
            <input
                name="hobbies" type="checkbox" value="tunning"
                %(checked-tunning)s
            /> Auto Tunning
            <input
                name="hobbies" type="checkbox" value="software"
                %(checked-software)s
            /> Software
        </p>
        <p>
            <input type="submit" value="Submit">
        </p>
    </form>

</body>
</html>
"""
#%()s此处为变量,用词典传值,返回”“ 或checked,当checked 会返回打勾
#'checked-tunning':("","checked")["tunning" in hobbies],方括号里是条件为真 则选”checked“
# 如果选”checked“ ,则会返回 checked 到html,input 里
def application(environ, start_response):

    d = parse.parse_qs(environ['QUERY_STRING'])
    hobbies = d.get("hobbies",[])
    # Adding strings to the response body
    response_body = html%{
        'checked-tunning':("","checked")["tunning" in hobbies],
        'checked-software':("","checked")["software" in hobbies]
    }
    # So the content-lenght is the sum of all string's lengths
    content_length = sum([len(s) for s in response_body])
    # GET方法请求时,Conten-Length,有省略

    status = '200 OK'
    response_headers = [
        ('Content-Type', 'text/html'),
        ('Content-Length', str(content_length))
    ]

    start_response(status, response_headers)
    return [response_body.encode("utf-8")]

httpd = make_server('',8088,application)
httpd.serve_forever()

~ ^_^ ~ 如果使用FlaskGET方法传递表单变量时,Flask如何解析?直接使用request.agrs,它本身就是一个MULTIDIC对象,不用再使用复杂的解析,再生成字典,request对象的属性方法的应用将另篇记录

post 请求解析

  • When the request method is POST the query string will be sent in the HTTP request body in instead of in the URL. The request body is in the WSGI server supplied wsgi.input file like environment variable.
  • It is necessary to know the response body size as an integer to read it from wsgi.input. The PEP 3333 says that the CONTENT_LENGTH variable, which holds the body size, may be empty or missing so read it in a try/except block.
from wsgiref.simple_server import make_server
from urllib import parse
html="""
<html>
<body>
   <form method="post" action="">
        <p>
        Ages:
        <input  type="text" name="age" value="yourage"
        />
        </p>
        <p>
            Hobbies:
            <input
                name="hobbies" type="checkbox" value="tunning"
                %(checked-tunning)s
            /> Auto Tunning
            <input
                name="hobbies" type="checkbox" value="software"
                %(checked-software)s
            /> Software
        </p>
        <p>
            <input type="submit" value="Submit">
        </p>
    </form>
    <p>
    Age:%(age)s
    Hobbies:%(hobbies)s
    </p>

</body>
</html>
"""

def application(environ, start_response):
    try:
        request_body_size = int(environ.get('CONTENT_LENGTH',0))
    except(ValueError):
        request_body_size = 0 


    request_body = environ['wsgi.input'].read(request_body_size)
    print (type(environ['wsgi.input']))
    # 结果为:<class '_io.BufferedReader'>,应该是二进制码
    #最开始并不知道需要在request_body后加decode函数,因单步运行,记得 d =[].且hobbies 为字节串,
    #感觉这里应该是不对,字典里面不都应该是字符串吗?从Server传过来都应该是字节串,所以应该转一下
    d = parse.parse_qs(request_body.decode("utf-8"))

    age = d.get("age",[""])[0]
    #['']不可少,否则客户端第一次访问页面时,还没有输入数字年龄,
    #仅age=d.get("age")[0]会出现TypeError: 'NoneType' object is not subscriptable
    hobbies = d.get("hobbies",[])
    print (hobbies)

    # Adding strings to the response body
    response_body = html%{
        'checked-tunning':("","checked")["tunning" in hobbies],
        'checked-software':("","checked")["software" in hobbies],
        'hobbies':','.join(hobbies or ['no hobbies']),
        'age':age or "empty"
    }
    # So the content-lenght is the sum of all string's lengths
    content_length = sum([len(s) for s in response_body])

    status = '200 OK'
    response_headers = [
        ('Content-Type', 'text/html'),
        ('Content-Length', str(content_length))
    ]

    start_response(status, response_headers)
    return [response_body.encode("utf-8")]

httpd = make_server('',8099,application)
print ("serveing on 8099")

httpd.serve_forever()

最开始并不知道错误在哪里,在jupyter lab 记事本里,将代码贴上,先设好,断点, 断点的地方就会出现变量,可以观察变量在软件运行中的变化, 》 是 继续向前停止于断点 !步进观察 按照软件使用过程,一步步运行步进,如果变量未按需求变化,说明有错误

首先是GET方法,访问页面,包括浏览器自动发起的一个图片访问GET

然后,客户端提交POST 请求,这时,观察 各变量是否正确变化,发现d,始终为空,是不对,且hobbies 为字节串,这是不对的

回想前篇,return 到服务器时,加.encode(),这次在处理服务器发来的数据,也 应该相反地处理 所以:.decode()

~ ^ _ ^ !~ 使用Flask, 当收到POST提交的变量,request的form方法,是werkzeug的ImmutableMutiDict,包含解析后的表单数据,表单字段(变量)值是通过input标签的name属性值作为键获取。后篇将详细讨论request的各种方法,属性

文字总结

以上就是简单的WSGI应用,到这里就已经完成WEB大致过程,实际web肯定复制得多,更多功能,不可能用这里的简单程序,Werkzeug

提供一些列实现WSGI协议的工具,并封装request ,response 类,FLASK 在此基础上发展成更多功能,更复杂的wsgi应用框架,但

不管多复杂,起启点,都和文章中的代码一样,一个字典变量environ,一个回调函数start_reponse

示意图总结

from IPython.display import SVG
SVG(filename="wsgi-get.svg")

bc87a41a4fe0404f08b3467f31f8e95a.png
  • 当方法是GET时,表单变量通过URL?后面的查询字符串,传递给服务器,插入到environ 字典里,QUERY-STRING 键下
  • 当方法为POST时,表单变量被放入请求主体传给服务器,具体被放到,environ字典里, wsgi.input文件,像其他变量一样
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值