http协议的9种请求方法

在http/1.1协议中,定义了8种访问指定资源的方法,他们分别为

  1. OPTIONS
  2. GET
  3. HEAD
  4. POST
  5. PUT
  6. PATCH
  7. DELETE
  8. TRACE
  9. CONNECT

为了更好的讲解和演示这9种http请求的方法,我用flask写了一个简单的服务程序

import json
from flask import Flask, request, make_response, jsonify


app = Flask(__name__)


@app.route("/book", methods=['HEAD', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE'])
def book():
    method = request.method
    if method == 'HEAD':
        return book_head()
    elif method == 'GET':
        return book_get()
    elif method == 'POST':
        #return book_post_form()
        return book_post_json()
    elif method == 'PUT':
        return book_put()
    elif method == 'DELETE':
        return book_delete()

def book_head():
    return jsonify({'name': 'python进阶教程', 'price': 35.5})


def book_get():
    return jsonify({'name': 'python进阶教程', 'price': 35.5})


def book_post_form():
    name = request.form['name']
    price = float(request.form['price'])
    print(name, price)

    return jsonify({'status': 1, 'msg': '新增成功'})


def book_post_json():
    data = json.loads(request.get_data())
    print(data)
    return jsonify({'status': 1, 'msg': '新增成功'})


def book_put():
    data = json.loads(request.get_data())
    print(data)
    return jsonify({'status': 1, 'msg': '修改成功'})


def book_delete():
    data = json.loads(request.get_data())
    print(data)
    return jsonify({'status': 1, 'msg': '删除成功'})



if __name__ == '__main__':
    app.run(debug=True)

这个服务只提供了一个URI资源,下面逐个介绍这8种请求资源的方法

1. OPTIONS

向服务器发送options方法,可以测试服务器功能是否正常,服务器会返回这个资源所支持的HTTP请求方法,在javascript中,使用XMLHttpRequest对象进行CORS跨域资源共享时,会先使用options方法进行嗅探,以此判断对指定资源是否具有访问权限。

flask框架会自动处理OPTIONS和HEAD请求,我在指定'/book'所支持的methods中并没有写OPTIONS,但使用requests发送OPTIONS请求,可以得到正常响应

import requests

url = 'http://127.0.0.1:5000/book'


def test_options():
    res = requests.options(url)
    print(res.headers)

test_options()

response 的headers里,会返回Allow 首部,其内容为"TRACE, GET, HEAD, PATCH, POST, DELETE, OPTIONS, PUT",这表示,请求'/book'时,服务器支持这么多的请求方法。

2. GET

GET方法用于显示的请求指定资源,通常,GET方法只用于读取数据,这在restful接口中尤为严格,GET方法绝不应该应用于会产生副作用的非幂等操作中。

所谓幂等,是一个数学与计算机概念,在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

本示例中,服务端收到GET请求后,返回一段json数据,发送请求的客户端程序如下

import requests

url = 'http://127.0.0.1:5000/book'


def test_get():
    params = {'name': 'python'}
    res = requests.get(url, params=params)
    print(res.text)

test_get()

你应该已经注意到,发送get请求也是可以携带参数的,而不只是post可以.

不同之处在于,post请求的参数放在dody体中,而get请求的参数放在url中,服务端收到的请求的完整url是

http://127.0.0.1:5000/book?name=python

GET请求的URL在浏览器里是极容易暴露的,因此用户的敏感信息不可以明文传输。

3. HEAD

HEAD方法在各个方面均与GET方法相同,唯一不同之处在于,服务器收到HEAD请求,不会将body里的内容返回给客户端,在本实例中,虽然响应HEAD请求时,明确的使用return语句返回了数据

return jsonify({'name': 'python进阶教程', 'price': 35.5})

但客户端不会收到这些数据,既然收不到数据,HEAD方法还有什么用处呢?

HEAD方法通常用来获取资源的信息,这些信息都蕴藏在响应头信息中,比如Content-Length, 客户端只是想知道自己将要请求的资源有多大,而并不是真的想要获取这份资源,那么就可以使用HEAD方法进行查看,虽然返回的response里没有body数据,但header里的部首和使用GET请求时返回的完全一样。

一个比较实用的场景如下,有一个很大的文件需要下载,单线程下载会很慢,那么就可以考虑使用多线程,每个线程只下载资源的一段,这个利用Range部首很容易就做到,那么整件时间最关键的就是获得资源的大小,你可以使用GET请求,但更便捷的方法是用HEAD请求,毕竟GET请求会返回整个文件。多线程下载大文件,可以参考我的一篇文章https://blog.csdn.net/KWSY2008/article/details/49204047

4. POST

post用于向指定资源提交数据,在restful风格的接口里,post用于新增数据。

post请求提交的数据放在请求的body体中,post提交的数据一般有两种格式,一种是form表单,一种是json数据

4.1 form表单

我们在登录某个网站的后台时,在网页上填写用户名和密码,点击回车后的提交过程就是post请求,请求的Content-Type值为 application/x-www-form-urlencoded 下面是模拟客户端提交form表单的一段代码

import requests

url = 'http://127.0.0.1:5000/book'


def test_form_post():
    data = {'name': 'python', 'price': 45.6}
    res = requests.post(url, data=data)
    print(res.text)

test_form_post()

服务端处理这种post请求要使用book_post_form函数,收到的请求body体中,数据为

name=python&price=45.6

flask框架会帮助我们解析这里的内容,我们通过request.form对象即可获得name和price

上面的代码是通过requests库直接提交,如果是通过网页上的form表单进行提交,则form表单应该是如下形式

<form action="/book" method="POST">
      <p>name: <input type="text" name="name" /></p>
      <p>price: <input type="text" name="price" /></p>
      <input type="submit" value="提交" />
    </form>

application/x-www-form-urlencoded 是表单默认的提交方式,你也可以通过enctype 来指定提交方式

<form action="/book" method="POST" enctype='application/x-www-form-urlencoded'>

如果你的表单还要上传文件,那么enctype则要设置成multipart/form-data

提交json数据

另一种较为常用的post数据格式是json,下面是一个具体示例

import requests

url = 'http://127.0.0.1:5000/book'


def test_json_post():
    data = {'name': 'python', 'price': 45.6}
    res = requests.post(url, json=data)
    print(res.text)

test_json_post()

服务端收到的请求的Content-Type 为application/json,body体中的数据为

{"name": "python", "price": 45.6}

不同于form表单数据,json数据需要我们自己写代码从body中取出并loads成字典来使用,具体方法参见flask 服务端程序里的book_post_json函数

5. PUT

put请求方法用于向指定资源上传最新数据,用于更新操作,该方法是幂等操作。同post一样,提交数据时,可以使用form表单或者json格式数据,下面是具体示例

import requests

url = 'http://127.0.0.1:5000/book'


def test_put():
    data = {'name': 'python', 'price': 55.6}
    res = requests.put(url, json=data)
    print(res.text)

test_put()

6. PATCH

PATCH方法是HTTP/1.1标准制定之后扩展的方法,和PUT一样,也用于资源的更新,不同之处有两点

  1. PATCH用于资源的部分更新,PUT多用于资源的整体更新
  2. 如果资源不存在,使用PATCH时应创建一个新的资源,而PUT则不要求创建新资源

下面是使用patch请求的示例代码

import requests

url = 'http://127.0.0.1:5000/book'


def test_patch():
    data = {'name': 'python'}
    res = requests.request('trace', url, json=data)
    print(res.text)

test_patch()

7. DELETE

delete方法用于删除URI所标识的资源,同样是幂等操作

import requests

url = 'http://127.0.0.1:5000/book'


def test_delete():
    data = {'name': 'python', 'price': 55.6}
    res = requests.delete(url, json=data)
    print(res.text)


test_delete()

8.TRACE

trace请求方法用于http请求的测试和诊断,根据协议,服务器在收到trace请求后,应回显所收到的数据,即服务器返回自己所收到的数据。

下面是客户端发送trace请求的一个例子

import requests

url = 'http://127.0.0.1:5000/book'


def test_trace():
    data = {'name': 'python'}
    res = requests.request('trace', url, json=data)
    print(res.text)


test_trace()

requests模块目前还没有提供专门用来发送trace请求的方法,因此我这里使用request方法直接进行发送

9. CONNECT

connect请求方法在我们日常的开发工作中是不会遇到的,因为它的作用是让服务器作为代理,让服务器代替用户访问其他网页,其实就是代理服务器。

鉴于connect在日常工作中很难有所应用,因此该请求不提供参考示例,感兴趣的同学可以阅读以下几篇博文

  1. https://www.jianshu.com/p/54357cdd4736
  2. https://blog.csdn.net/aotony_1988/article/details/42005835
  3. https://www.joji.me/zh-cn/blog/the-http-connect-tunnel/

10. 最后啰嗦几句

虽然http协议提供了这么多种方法,但使用的最为频繁的却只有get和post的,甚至是滥用。

restful风格的接口提倡使用http方法来标识区分一个请求操作意图,比如获取数据用get,新增用post,修改用put,删除用delete。

然而在实际工作中,我看到的最多的代码都没有遵守这些规则,而是大量的使用post请求,区分请求意图的方法是使用不同的uri,比如获取数据用get_xx,新增数据用add_xx, 修改数据用modify_xx,删除数据用del_xx, 一个post请求,实现了对同一个资源的四种操作,个人很不喜欢这种风格的接口。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案