zoukankan      html  css  js  c++  java
  • Requests

     
     
     
     
     
     
     
     
     
     
     
     
     

    requests使用

    requests的安装

    1. pip的安装方式    pip install requests
    2. 克隆公共版本库    git clone git://github.com/kennethreitz/requests.git
      1. 到下载的指定目录    cd requests
      2. 再次使用pip下载    pip install .

    快速上手

    发送请求

    >>> r = requests.get('https://api.github.com/events')
    >>> r = requests.post('http://httpbin.org/post', data = {'key':'value'})
    >>> r = requests.put('http://httpbin.org/put', data = {'key':'value'})
    >>> r = requests.delete('http://httpbin.org/delete')
    >>> r = requests.head('http://httpbin.org/get')
    >>> r = requests.options('http://httpbin.org/get')

    传递url参数

    经常会为URL的查询字符串传递某种数据,如果是手工构建URL,那么数据会以键值对的形式放在URL中,跟在一个问号的后面,requests允许使用params关键字参数,一个字符串字典来提供这些参数

    >>> payload = {'key1': 'value1', 'key2': 'value2'}
    >>> r = requests.get("http://httpbin.org/get", params=payload)

    通过打印输出该URL,可以看到URL已被正确编码

    需要注意的是字典里面的值为None的键都不会被添加到URL的查询字符串里,也可以将一个列表作为值传入

    >>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
    
    >>> r = requests.get('http://httpbin.org/get', params=payload)
    >>> print(r.url)
    http://httpbin.org/get?key1=value1&key2=value2&key2=value3

    响应内容

    我们能够服务服务器响应的内容

    >>> import requests
    >>> r = requests.get('https://api.github.com/events')
    >>> r.text
    u'[{"repository":{"open_issues":0,"url":"https://github.com/...

    request会自动解码来自服务器的内容,大多数unicode字符集都能被无缝解码,请求发出后,Requests会使用其推测的文本编码,可以找出Requests使用了什么编码,并且能够使用r.encoding属性来改变它

    >>> r.encoding
    'utf-8'
    >>> r.encoding = 'ISO-8859-1'

    二进制响应内容

    可以以字节的方式访问请求响应体,对于非文本请求

    >>> r.content
    b'[{"repository":{"open_issues":0,"url":"https://github.com/...

    Requests会自动解码gzip和deflate传输编码的响应数据,例如,以请求返回的二进制数据创建了一张图片,可以使用如下代码:

    >>> from PIL import Image
    >>> from io import BytesIO
    
    >>> i = Image.open(BytesIO(r.content))

    json响应内容

    Requests中有一个内置的json解码器,用于处理json数据

    >>> import requests
    
    >>> r = requests.get('https://api.github.com/events')
    >>> r.json()
    [{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...

    如果json解码失败,r.json()会抛出一个异常,例如,响应内容是401,尝试访问r.json将会抛出ValueError:No JSON object could be decoded异常,需要注意的是,成功调用r.json并不意味着响应的成功,有的服务器会在失败的响应中包含一个json对象(比如HTTP 500的错误细节),这种json会被解码返回,要检查请求是否成功,请使用r.raise_for_status()或者检查r.status_code是否和你的期望相同

    原始响应内容

    在罕见的情况下,可能想获取来自服务器的原始套接字响应,那么可以访问r.raw.如果确实像这么干,确保在初始请求中设置了stream=True,具体可以这么做

    >>> r = requests.get('https://api.github.com/events', stream=True)
    >>> r.raw
    <requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
    >>> r.raw.read(10)
    'x1fx8bx08x00x00x00x00x00x00x03'    # 读取10个

    一般情况下,应该以下面的模式将文本流保存到文件

    with open(filename, 'wb') as fd:
        for chunk in r.iter_content(chunk_size):
            fd.write(chunk)

    定制请求头

    如果想为请求添加HTTP头部,值需要传递一个dict给headers参数就可以了

    >>> url = 'https://api.github.com/some/endpoint'
    >>> headers = {'user-agent': 'my-app/0.0.1'}
    
    >>> r = requests.get(url, headers=headers)

    注意:定制header的优先级低于某些特定的信息源

    1. 如果在.netrc中设置了用户认证信息,使用headers=设置的授权就不会生效,而如果设置了auth=参数,.netrc的设置就无效了
    2. 如果内重定向到别的主机,授权header就会被删掉
    3. 的代理授权header会被URL中提供的代理身份覆盖掉
    4. 在我们能判断内容长度的情况下,header的Content-Length会被改写

    准确的说,Requests不会基于定制的header的具体情况改变自己的行为,只不过会在最后的请求中,所有的header信息都会被传递进去.所有的header值必须是string、bytestring或者unicode,尽管传递unicode header也是允许的,但是不建议这么做

    post请求

    通常,想要发送一些编码为表单形式的数据,非常像一个HTML表单,要实现这个,只需要简单传递一个字典给data参数,你的数据字典在发出请求时会自动编码为表单形式:

    >>> payload = {'key1': 'value1', 'key2': 'value2'}
    
    >>> r = requests.post("http://httpbin.org/post", data=payload)
    >>> print(r.text)
    {
      ...
      "form": {
        "key2": "value2",
        "key1": "value1"
      },
      ...
    }

    还可以为data参数传入一个元祖列表,在表单中多个元素使用同一key的时候,这种方式尤其有效

    >>> payload = (('key1', 'value1'), ('key1', 'value2'))
    >>> r = requests.post('http://httpbin.org/post', data=payload)
    >>> print(r.text)
    {
      ...
      "form": {
        "key1": [
          "value1",
          "value2"
        ]
      },
      ...
    }

    很多时候你想要发送的数据并非编码为表单形式的,如果你传递一个string而不是一个dict,那么数据会被直接发布出去(在2.24版后新增的功能)

    >>> import json
    
    >>> url = 'https://api.github.com/some/endpoint'
    >>> payload = {'some': 'data'}
    
    >>> r = requests.post(url, data=json.dumps(payload))

    此处除了自行对dict进行编码,还可以使用json参数直接传递,然后就会被自动编码,这是2.24新加功能

    >>> url = 'https://api.github.com/some/endpoint'
    >>> payload = {'some': 'data'}
    
    >>> r = requests.post(url, json=payload)

    post多部分编码的文件

    requests使得上传多部分编码文件变得简单

    >>> url = 'http://httpbin.org/post'
    >>> files = {'file': open('report.xls', 'rb')}
    
    >>> r = requests.post(url, files=files)
    >>> r.text
    {
      ...
      "files": {
        "file": "<censored...binary...data>"
      },
      ...
    }

    可以设置文件名、文件类型和请求头

    >>> url = 'http://httpbin.org/post'
    >>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
    
    >>> r = requests.post(url, files=files)
    >>> r.text
    {
      ...
      "files": {
        "file": "<censored...binary...data>"
      },
      ...
    }

    响应状态码

    我们可以检测响应状态码

    >>> r = requests.get('http://httpbin.org/get')
    >>> r.status_code
    200

    为方便引用,requests还附带一个内置的状态码查询对象

    >>> r.status_code == requests.codes.ok    # 返回一个boolearn值

    如果发送了一个错误请求(一个4XX客户端错误或者5XX服务器错误响应),我们可以通过Response.raise_for_status()来抛出异常

    >>> bad_r = requests.get('http://httpbin.org/status/404')
    >>> bad_r.status_code
    404
    
    >>> bad_r.raise_for_status()
    Traceback (most recent call last):
      File "requests/models.py", line 832, in raise_for_status
        raise http_error
    requests.exceptions.HTTPError: 404 Client Error

    响应头

    我们可以查看以一个Python字典形式展示的服务器响应头

    >>> r.headers
    {
        'content-encoding': 'gzip',
        'transfer-encoding': 'chunked',
        'connection': 'close',
        'server': 'nginx/1.0.4',
        'x-runtime': '148ms',
        'etag': '"e1ca502697e5c9317743dc078f67693f"',
        'content-type': 'application/json'
    }

    上面字典比较特殊,仅为HTTP头部而生的,HTTP头部都是大小写不敏感的,因此可以使用任意大写形式来访问这些响应头字段

    >>> r.headers['Content-Type']
    'application/json'
    
    >>> r.headers.get('content-type')
    'application/json'

    Cookie

    如果某个响应中包含一些cookie,可以快速访问他们:

    >>> url = 'http://example.com/some/cookie/setting/url'
    >>> r = requests.get(url)
    
    >>> r.cookies['example_cookie_name']
    'example_cookie_value'

    要想发送cookies到服务器,可以使用cookies参数

    >>> url = 'http://httpbin.org/cookies'
    >>> cookies = dict(cookies_are='working')
    
    >>> r = requests.get(url, cookies=cookies)
    >>> r.text
    '{"cookies": {"cookies_are": "working"}}'

    Cookie的返回对象为RequestsCookieJar,它的行为和字典类似,但接口更为完整,适合跨域名跨路径使用,还可以把Cookie Jar传到Requests中

    >>> jar = requests.cookies.RequestsCookieJar()
    >>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
    >>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
    >>> url = 'http://httpbin.org/cookies'
    >>> r = requests.get(url, cookies=jar)
    >>> r.text
    '{"cookies": {"tasty_cookie": "yum"}}'

    重定向与请求历史

    默认情况下,除了HEAD,Requests会自动处理所有重定向,可以使用响应对象的history方法来追踪重定向,Response.history是一个Response对象的列表,为了完成请求而创建了这些对象,这个对象列表按照从最老到最近的请求进行排序

    将所有HTTP请求重定向到HTTPS

    >>> r = requests.get('http://github.com')
    
    >>> r.url
    'https://github.com/'
    
    >>> r.status_code
    200
    
    >>> r.history
    [<Response [301]>]

    如果使用的是GET、OPTIONS、POST、PUT、PATCH或者DELETE,那么可以通过allow_redirects参数禁用重定向处理

    >>> r = requests.get('http://github.com', allow_redirects=False)
    >>> r.status_code
    301
    >>> r.history
    []

    如果使用了HEAD,也可以启用重定向

    >>> r = requests.head('http://github.com', allow_redirects=True)
    >>> r.url
    'https://github.com/'
    >>> r.history
    [<Response [301]>]

    超时

     可以告诉requests在经过以timeout参数设定的秒数时间之后停止等待响应,基本上所有的生产代码都应该使用这个参数,如果不适用,程序可能会永远失去响应

    >>> requests.get('http://github.com', timeout=0.001)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

     注意的是,timeout仅对连接过程有效,与响应体的下载有关,timeout并不是整个下载响应的时间限制,而是如果服务器在timeout秒内没有应答,将会引发一个异常

     错误与异常

    1. 遇到网络问题(如:DNS查询失败、拒绝连接等)时,Requests会抛出一个ConnectionError异常
    2. 如果HTTP请求返回了不成功的状态码,Response.raise_for_status()会抛出一个HTTPerror异常
    3. 若请求超时,则抛出一个Timeout异常
    4. 若请求超过了设定的最大重定向次数,则会抛出一个TooManyRedirects异常
    5. 所有Requests显示抛出的异常都继承自requests.exceptions.RequestException

     高阶用法

    会话对象

     会话对象能够跨请求保持某些参数,会在同一个Session实例发出的所有请求之间保持cookie,期间使用urllib3的connection pooling功能.所以如果想同一主机发送多个请求,底层的TCP连接将会被重用,从而带来显著的性能提升

    跨请求保持一些cookie

    s = requests.Session()
    
    s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
    r = s.get("http://httpbin.org/cookies")
    
    print(r.text)
    # '{"cookies": {"sessioncookie": "123456789"}}'

    会话也可以用来为请求方法提供缺省数据,这是通过为会话对象的属性提供数据来实现的

    s = requests.Session()
    s.auth = ('user', 'pass')
    s.headers.update({'x-test': 'true'})
    
    # both 'x-test' and 'x-test2' are sent
    s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

    任何你传递请求方法的字典都会与已设置会话层数据合并,方法层的参数覆盖会话的参数,不过需要注意,就算使用了会话,方法级别的参数也不会被夸请求保持

    s = requests.Session()
    
    r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
    print(r.text)
    # '{"cookies": {"from-my": "browser"}}'
    
    r = s.get('http://httpbin.org/cookies')
    print(r.text)
    # '{"cookies": {}}'

     会话还可以用作前后文管理器,能确保with区块退出后会话能被关闭,即使发生了异常也是一样

    with requests.Session() as s:
        s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

     有时候你会想省略字典参数中一些会话层的键,要做到这一点,只需简单的在方法层参数中将那个键的值设置为None,那个键就会被自动省略掉

     请求与响应对象

    任何时候进行了类似requests.get()的调用,你都在做两件主要的事情

    1. 在构建一个Request对象,该对象将被发送到某个服务器请求或查询一些资源
    2. 一旦requests得到一个从服务器返回的相应就会产生一个Response对象,该相应对象包含服务器返回的所有信息,也包含你原来创建的Request对象

     如果想访问服务器返回给我们的响应头部信息,可以在调用一下headers方法

    >>> r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')
    >>> r.headers

    然而,如果仅仅是想得到发送到服务器的请求头部,我们可以简单的访问该请求

    >>> r.request.headers

    准备请求

    当你从API或者会话调用中收到一个Response对象时,request属性其实是使用了PreparedRequest,有时候在发送请求之前,需要对body或者header做一些额外处理

    from requests import Request, Session
    
    s = Session()
    req = Request('GET', url,
        data=data,
        headers=header
    )
    prepped = req.prepare()
    
    # do something with prepped.body
    # do something with prepped.headers
    
    resp = s.send(prepped,
        stream=stream,
        verify=verify,
        proxies=proxies,
        cert=cert,
        timeout=timeout
    )
    
    print(resp.status_code)

    由于没有对Request对象做什么特殊事情,需要准备和修改Preparedequests对象,然后把它和别的参数一起发送到requests.*或者Session.*

    然而,上述代码会失去Requests Session对象的一些优势,尤其Session级别的状态,例如:cookie就不会被应用到你的请求上去,要获取一个带有状态的PreparedRequest,用Session.prepare_request()取代Request.prepare()的调用

    from requests import Request, Session
    
    s = Session()
    req = Request('GET',  url,
        data=data
        headers=headers
    )
    
    prepped = s.prepare_request(req)
    
    # do something with prepped.body
    # do something with prepped.headers
    
    resp = s.send(prepped,
        stream=stream,
        verify=verify,
        proxies=proxies,
        cert=cert,
        timeout=timeout
    )
    
    print(resp.status_code)

     SSL证书验证

    Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样.SSL验证默认是开启的,如果证书验证失败,Requests会抛出SSLError

    >>> requests.get('https://requestb.in')
    requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'

     在该域名上我没有设置SSL,所以失败了,但Github设置了SSL

    >>> requests.get('https://github.com', verify=True)
    <Response [200]>

     也可以为verify传入CA_BUNDLE文件的路径,或者包含可信任CA证书我呢间的文件夹路径

    >>> requests.get('https://github.com', verify='/path/to/certfile')

     或者将其保持在会话中

    s = requests.Session()
    s.verify = '/path/to/certfile'

     如果verify设为文件夹路径,文件夹必须通过OpenSSL提供的c_rehash工具处理

    还可以通过REQUESTS_CA_BUNDLE环境变量定义可信任CA列表

    如果将verify设置为False,Requests也能忽略对SSL证书的验证

    >>> requests.get('https://kennethreitz.org', verify=False)
    <Response [200]>

     默认情况下,verify是设置为True的,选项verify仅应用于主机证书

    对于私有证书,你也可以传递一个CA_BUNDLE文件的路径给verify,也可以设置REQUEST_CA_BUNDLE环境变量

    客户端证书

    可以指定一个本地证书用作客户端证书,可以是单个文件(包含秘钥和证书)或一个包含两个文件路径的元祖

    >>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
    <Response [200]>

    或者包含在会话中

    s = requests.Session()
    s.cert = '/path/client.cert'

    requests的简单应用

    测试百度接口

    import requests

    base_url = "https://www.baidu.com"

    result = requests.get(base_url + "/get")
    print(result.status_code)

    requests的参数传递

    get请求

    一般在GET请求中我们使用查询字符串来进行参数传递,在requests库中使用方法如下:

    import requests

    base_url = "http://192.168.1.105:8000/v1/cards/"

    result = requests.get(base_url +"900000")
    print(result.json())
    print(result.url)

    post请求

    import requests

    base_url = "http://192.168.1.105:8000/v1/cards"
    result = requests.post(base_url, data={"cardno": "100106", "type": "1"})
    print(result.status_code)
    print(result.url)
    print(result.json())

    requests请求头设置

     使用headers对请求头做设置

    import requests

    base_url = "http://192.168.1.105:8000/v1/cards"
    result = requests.post(base_url, data={"cardno": "100106", "type": "1"}, headers={"User-Agent": "Mozilla/8.0"})
    print(result.status_code)
    print(result.url)
    print(result.json())

    requests响应内容

    # 获取状态码
    print(result.status_code)
    # 获取地址
    print(result.url)
    # 获取json格式的响应数据
    print(result.json())
    # 获取请求头
    print(result.headers)

    request中cookie设置

    设置cookies

    import requests

    base_url = "http://192.168.1.105:8000/v1/cards"
    result = requests.post(base_url, data={"cardno": "100107", "type": "1"}, cookies={"username":"wupeng"})
    # 获取状态码
    print(result.json())

    获取cookies

    import requests

    base_url = "https://www.baidu.com"
    result = requests.get(base_url)
    # 获取cookies
    print(result.cookies)
    # 输出cookies
    for key, value in result.cookies.items():
    print(key + ":" + value)

    调用了cookies属性成功得到Cookies,可以发现它是一个RequestCookiesJar类型,然后我们用items()方法转化为元祖组成的列表,遍历输出每一个Cookie的名和值,实现Cookies的遍历解析

    request中超时设置和文件上传

    超时

    可以让requests在经过以timeout参数设定的秒数之后停止等待响应,防止某些请求没有响应而一直处于等待状态,如果超时就会抛出异常

    import requests

    base_url = "https://www.baidu.com"
    result = requests.get(base_url,timeout=0.00002)
    # 获取cookies
    print(result.cookies)
    # 输出cookies
    for key, value in result.cookies.items():
    print(key + ":" + value)

    文件上传

    import requests

    base_url = "https://www.baidu.com"
    # rb表示二进制读取
    result = requests.get(base_url,files={"file":open("wupeng.png","rb")})
    print(result.text)

    request中会话对象

    在计算机中,尤其是在网络应用中,称为会话控制,session对象存储特定用户会话所需的属性及配置信息,这样当用户在应用程序的web页跳转时,存储在session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去.

    比如你先进行了登录的操作,然后打开个人中心详情页面,个人中心详情页面如何知道展示的是刚刚登录的这个用户的信息,那么这里就需要使用session来存储相关信息

    在接口测试过程中接口之间经常有依赖关系,比如两个请求一个是设置Cookie,灵感衣蛾是获取cookie,在没有session保存机制的情况下,第二个接口无法获取第一个接口设置的cookies值

    import requests

    base_url = "http://192.168.1.105:8000/v1/cards/"
    session=requests.session()
    result = session.get(base_url +"/cookies/set/user/51zxw")
    print(result.json())
    result2 = session.get(base_url +"/cookies/")
    print(result2.json())

    requests中SSL验证和代理设置

    证书验证(12306的证书以前是自己颁发给自己的,现在已经被修复)

    import requests

    base_url = "https://www.12306.cn"
    # 关于证书问题,如果不做证书验证可以关闭
    result = requests.get(base_url,verify=False)
    print(result.text)

    代理设置

    1. 网络代理,一种特殊的网络服务,允许一个网络终端(一般为客户端),通过这个服务与另一个网络终端进行非直接的链接
    2. 代理服务器位于客户端和访问互联网之间,服务器接收客户端的请求,然后代替客户端向目标网站发出请求,所有的流量均来自代理服务器的ip地址,从而获取到一些不能直接获取的资源
    3. 对于有些接口,在测试的时候请求几次,能正常获取内容,但是一旦开始大规模频繁请求服务器的时候就会开启验证,甚至会直接将ip封掉,那么防止这种情况发生,需要设置代理来解决这个问题,在Request中需要用到proxies这个参数,在爬虫会常用到代理
    # 西刺免费代理(可以使用免费代理ip)
    import requests
    base_url="https://www.baidu.com"
    proxies={"http":"http://14.115.104.133:9797"}
    result=requests.get(base_url,proxies=proxies)
    print(result.text)

    requests身份验证

    身份认证

    很多借口都需要身份认证,Requests支持多种身份认证

    import requests
    from requests.auth import HTTPBasicAuth
    from requests.auth import HTTPDigestAuth

    base_url="http://httpbin.org"
    r=requests.get(base_url+"/basic-auth/wupeng/8888",auth=HTTPBasicAuth("wupeng","8888"))
    print(r.text)
    r1=requests.get(base_url+"/digest-auth/auth/wupeng1/8888",auth=HTTPDigestAuth("wupeng1","8888"))
    print(r1.text)

    requests流式请求

    import json
    import requests

    base_url = "http://httpbin.org"
    r = requests.get(base_url, "/stream/10", stream=True)
    if r.encoding is None:
    r.encoding = "utf-8"
    for line in r.iter_lines(decode_unicode=True):
    if line:
    data=json.loads(line)
    print(data["id"])

    停车场案例

    接口自动化的框架可以结合功能自动化

    可以使用功能自动化的pytest配置文件来操作pytest命令,实现框架执行

    可以参考功能自动化中的allure来实现接口自动化的测试报告

    可以植入jenkins实现集成持续性测试

    Restful接口开发与测试

    rest概述

    1. 简介
      1. 表现层状态转化,rest是web服务的一种架构风格
      2. 使用HTTP、URL、XML、JSON、HTML等广泛流行的标准和协议:轻量级,跨平台、跨语言的架构设计
      3. 是一种设计风格,不是一种标准,是一种思想
    2. rest原则
      1. 网络上的所有事物都可以被抽象为资源
      2. 每一个资源都有唯一的资源标识,对资源的操作不会改变这些标识
      3. 所有的操作都是无状态的
    3. 设计思想
      1. 遵循CRUD原则,对资源只需要四种行为:创建、获取、更新和删除就可以完成相关操作和处理
      2. 通过统一资源标识符来识别和定位资源,并且针对这些资源而执行的操作是通过HTTP规范定义的,核心的操作只有GET、POST、PUT、DELETE

    HTTP方法幂等性与安全性

    1. GET    SELECT    是幂等    是安全的
    2. POST    INSERT    不是幂等    不是安全的
    3. PUT    UPDATE    是幂等    不是安全的
    4. DELETE    DELETE    是幂等的    不是安全的

    幂等:对同一个rest接口多次请求,得到的资源状态是相同的

    安全:对该rest接口请求,不会使服务器资源状态发生改变.

    rest的优势

    由于rest强制所有的操作都必须是无状态的,这就没有上下文的限制,如果做分布式,集群在不需要考虑上下文和会话保持的问题,极大的提高系统的可伸缩性

    前后端分离,前端拿到的数据值负责展示和渲染,不对数据做任何处理,后端处理数据并以json格式传输出去,定义这样一套统一的接口,在web,ios,android三端都可以用相同的接口

    Django安装

    1. 输入命令    pip install django(需要提前配置好python环境)
    2. 检验安装    django-admin

    Django REST Framework

    1. 是一套基于Django的REST风格的框架
    2. 特点
      1. 功能强大灵活,可以帮助快速开发web api
      2. 支持认证策略,包括OAuth1和OAuth2
      3. 自持ORM和非ORM数据源的序列化
      4. 丰富的文档以及良好的社区支持
    3. 安装
      1. pip install djangorestframework
      2. pip install markdown
      3. pip install django-filter

    创建API

    1. 当Django REST Framework安装好之后,创建一个新的项目django_restful,如下命令所示创建在D盘根目录,在项目下创建api应用    django-admin startproject django_restful
    2. 进入项目django_restful创建api应用,创建完成之后可以看到项目文件夹下多了一个api文件夹    cd django_restful并执行命令python manage.py startapp api
    3. 利用PyCharm打开settings.py添加api和rest_framework
    4. rest_framework权限配置(默认设置在全局范围内,通过DEFAULT_PERMISSION_CLASSES设置,在该文件末尾添加内容)
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'api'
    ]

    REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.IsAuthenticated',
    ]
    }

    数据库迁移

    在原来的django_restful文件下输入命令    python manage.py migrate

    创建超级管理员    python manage.py createsuperuser

    启动Server    python manage.py runserver

    自定义host和port    python manage.py runserver 127.0.0.1:8001

    登录超级管理员账户    输入地址:http://127.0.0.1:8000/admin

    数据序列化

    Serializers用于定位API的表现形式,如返回哪些字段、返回怎样的格式等,这里序列化Django自带的User和Group.创建数据序列化,在api应用下创建serializers.py文件

    序列化代码如下:

    from django.contrib.auth.models import User, Group
    from rest_framework import serializers


    class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
    model = User
    fields = ('url', 'username', 'email', 'groups')


    class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
    model = Group
    fields = ('url', 'name')

     视图

    创建视图views.py

    视图用于如何向用户展示数据,比如用户查询User信息或查询Group信息,那么程序内部要定义好怎么去查询,再Django REST framework种,ViewSets用于定义视图的展现形式,例如返回哪些内容,需要做哪些权限处理

    from django.shortcuts import render
    from django.contrib.auth.models import User, Group
    from rest_framework import viewsets
    from api.serializers import UserSerializer, GroupSerializer


    # create your views here.
    class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer


    class GroupViewSet(viewsets.ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

    在url中会定义相应的规则ViewSets,ViewSets则通过serializer_class找到对应的Serializers

    这里将User和Group的所有对象赋予queryset,并返回这些值,再UserSerializer和GroupSerializer中定义要返回的字段

    URL路由配置

    urls.py

    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import include
    from rest_framework import routers
    from api import views

    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)
    router.register(r'groups', views.GroupViewSet)
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls)),
    path('api-auth', include('rest_framework.urls', namespace='rest_framework')),
    ]

    Swagger接口文档生成

    接口开发完之后,接下来需要编写接口文档,传统的接口文档编写都是使用Word或者其他一些接口文档管理平台,这种形式接口文档维护更新比较麻烦,每次接口有变动时得手动修改文档,因此针对这种情况,这里推荐使用Swagger来管理接口文档

    Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,总体目标是使客户端和文件系统作为服务器以同样的速度来更新,每当接口有变动时,对应的接口文档也会自动更新.

    为什么受欢迎?

    1. Swagger可以生成一个具有互动性的API控制台,开发者可以用来快速学习和尝试API
    2. Swagger可以生成客户端SDK代码用于各种不同的平台上实现
    3. Swagger文件可以在许多不同的平台上从代码注释中自动生成
    4. Swagger有一个强大的社区,里面有许多强悍的贡献

    Django接入Swagger

    安装    pip install django-rest-swagger

    进入到settings.py文件,添加django-rest-swagger应用

    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'api',
    'rest_framework_swagger'
    ]

    进入到views.py将之前定义的UserViewSet和GroupViewSet补充注释

    """
    retrieve:
    Return a user instance.
    list:
    Return all users,odered by most recent joined.
    create:
    Create a new user.
    delete:
    Remove a existing user.
    partial_update:
    Update one or more fields on a existing user.
    update:
    Update a user.
    """

    """
    retrieve:
    Return a group instance.
    list:
    Return all groups,odered by most recent joined.
    create:
    Create a new group.
    delete:
    Remove a existing group.
    partial_update:
    Update one or more fields on a existing group.
    update:
    Update a group.
    """

    在url.py添加get_schema_view辅助函数(启动失败,使用drf-yasg代替)

    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import include
    from rest_framework import routers
    from api import views
    from rest_framework.schemas import get_schema_view
    from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer

    schema_view = get_schema_view(title="API", renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer])


    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)
    router.register(r'groups', views.GroupViewSet)

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls)),
    path('api-auth', include('rest_framework.urls', namespace='rest_framework')),
    path('docs/', schema_view, name='docs')
    ]

     drf-yasg的使用

    安装    pip install drf-yasg

    settings.py添加路由等内容

    ...
    from drf_yasg.views import get_schema_view
    from drf_yasg import openapi
    
    ...
    
    
    schema_view = get_schema_view(
    openapi.Info(
    title="Lemon API接口文档平台", # 必传
    default_version='v1', # 必传
    description="接口文档",
    contact=openapi.Contact(email="17327767735@163.com"),
    license=openapi.License(name="BSD License"),
    ),
    public=True,
    # permission_classes=(permissions.AllowAny,), # 权限类
    )
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls)),
    path('api-auth', include('rest_framework.urls', namespace='rest_framework')),
    path('redoc', schema_view.with_ui('redoc', cache_timeout=None), name='schema-redoc'),
    ]

     Restful接口测试-postman

    将开发好的接口使用postman进行测试

     这种情况出现,是因为没有进行授权,在Authorization选择Basic Auth进行授权

     Restful接口测试-python

    将开发好的接口使用python与pytest结合进行测试

    import unittest
    import requests
    import pytest


    class TestRestFul(unittest.TestCase):
    def setUp(self) -> None:
    self.base_url = "http://localhost:8000/users"
    self.auth = ("wupeng", "bb961202")

    # 获取操作与删除操作类似
    def test_get_user(self):
    r = requests.get(self.base_url + "/1/", auth=self.auth)
    result = r.json()
    self.assertEqual(result['username'], "wupeng")

    # 添加操作与修改操作类似
    def test_add_user(self):
    r = requests.post(self.base_url + "/", data={'username': 'wupeng1', 'email': '1059457506@qq.com'},
    auth=self.auth)
    result1 = r.json()
    self.assertEqual(result1['username'], "wupeng1")

    # 判断是否授权
    def test_no_auth(self):
    r = requests.get(self.base_url)
    result = r.json()
    self.assertEqual(result['detail'], "Authentication credentials were not provided.")


    if __name__ == '__main__':
    pytest.main()

     Restful接口测试-MySQL配置

    在接口测试过程中,由于有些接口类型并不是安全的,比如DELETE类型,上一次请求之后下一次在请求结果就不一样了,甚至有时接口之间的数据还会相互干扰,导致接口断言失败时不能断定到底是接口程序引起的错误,还是测试数据变化引起的错误,通过测试数据库,每轮测试执勤啊将数据初始化,这样避免数据干扰.

    django支持如下几种数据库:

    1. PostgreSQL
    2. MySQL
    3. Oracle

    Django迁移MySQL

    修改settings.py文件下的DATABASES

    DATABASES = {
    'default': {
    # 'ENGINE': 'django.db.backends.sqlite3',
    # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    'ENGINE': 'django.db.backends.mysql',
    'HOST': '127.0.0.1',
    'PORT': '3306',
    'NAME': 'django_restful',
    'USER': 'root',
    'PASSWORD': 'bb961202',
    'OPTIONS': {
    'isolation_level': None,
    'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
    }
    }
    }

    安装MySQLdb驱动

    打开django_restful中的__init__.py,添加如下代码:

    import pymysql
    pymysql.install_as_MySQLdb()

     Django数据管理-数据库迁移

     创建models

    Django提供了完善的模型(model)层来创建和存取数据,包含所储存数据的必要字段和行为.通常,每个模型对应数据库中的唯一的一张表.

    打开api中的models.py创建如下代码:

    from django.db import models


    # Create your models here.
    class User(models.Model):
    username = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    groups = models.CharField(max_length=100)

    def __str__(self):
    return self.username


    class Group(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
    return self.name

    Django模型字段常用类型

    导入Models

    创建好Model后需要分别在serializers.py和views.py来导入,同时注释掉django默认的数据库

    from api.models import User,Group

    数据库迁移

    1. python manage.py makemigrations api
    2. python manage.py migrate

    执行1出错,raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

    解决方案

    第一步:找到报错位置G:pythonlibsite-packagesdjangodbackendsmysqlase.py    并打开base.py,注释掉如下两行

    version = Database.version_info

    # if version < (1, 3, 13):
    #    raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)
     
    第二步:再次执行python manage.py makemigrations   报错(如果不报错忽略)
    # 报错路径 <br>File "G:pythonlibsite-packagesdjangodbackendsmysqloperations.py", line 146, in last_executed_query
        query = query.decode(errors='replace')<br><br># 报错问题
    AttributeError: 'str' object has no attribute 'decode'
    修改:将代码里面query.decode改成:query.encode
    if query is not None:
        query = query.encode(errors='replace')  # 修改后的
    return query
     
    第三步:报警告Warning: (3090, "Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It will be removed in a future release.")
    注释掉setting.py下的DATABASES的'OPTIONS'    这一步可以忽略
     
    数据库迁移之后,在重新创建超级用户    python manage.py createsuperuser

    Django数据管理-封装初始化操作

    封装初始化操作

    数据初始化操作主要包括:数据库连接、数据清除、数据插入、关闭数据库,在api项目虾米那新建一个目录test_project,然后创建文件:mysql_action.py

    from pymysql import connect
    import yaml


    class DB():
    def __init__(self):
    print('connect db...')
    self.conn = connect(host='127.0.0.1', user='root', password='bb961202', db='django_restful')

    # 清除表
    def clear(self, table_name):
    print('clear db...')
    clear_sql = 'truncate' + ' ' + table_name + ';'
    with self.conn.cursor() as cursor:
    cursor.execute('set foreign_key_checks=0;')
    cursor.execute(clear_sql)
    self.conn.commit()

    # 添加表
    def insert(self, table_name, table_data):
    for key in table_data:
    table_data[key] = "'" + str(table_data[key]) + "'"
    key = ','.join(table_data.keys())
    value = ','.join(table_data.values())
    print(key)
    print(value)
    insert_sql = 'insert into' + ' ' + table_name + '(' + key + ')' + 'values' + '(' + value + ')'
    print(insert_sql)
    with self.conn.cursor() as cursor:
    cursor.execute(insert_sql)
    self.conn.commit()

    # 关闭数据库
    def close(self):
    print('close db')
    self.conn.close()

    if __name__ == '__main__':
    db = DB()
    # db.clear('api_user')
    # user_data = {'id': 1, 'username': 'wulei', 'email': '1059457506@qq.com','groups':'team'}
    # db.insert('api_user', user_data)
    db.close()

    Django数据管理-封装初始化数据

    mysql_action.py改写

    from pymysql import connect
    import json
    import yaml


    class DB():
    def __init__(self):
    print('connect db...')
    self.conn = connect(host='127.0.0.1', user='root', password='bb961202', db='django_restful')

    # 清除表
    def clear(self, table_name):
    print('clear db...')
    clear_sql = 'truncate' + ' ' + table_name + ';'
    with self.conn.cursor() as cursor:
    self.conn.ping(reconnect=True)
    cursor.execute('set foreign_key_checks=0;')
    cursor.execute(clear_sql)
    self.conn.commit()

    # 添加表
    def insert(self, table_name, table_data):
    for key in table_data:
    table_data[key] = "'" + str(table_data[key]) + "'"
    key = ','.join(table_data.keys())
    value = ','.join(table_data.values())
    print(key)
    print(value)
    insert_sql = 'insert into' + ' ' + table_name + '(' + key + ')' + 'values' + '(' + value + ')'
    print(insert_sql)
    with self.conn.cursor() as cursor:
    self.conn.ping(reconnect=True)
    cursor.execute(insert_sql)
    self.conn.commit()

    # 关闭数据库
    def close(self):
    print('close db')
    self.conn.close()

    # 初始化数据
    def init_data(self, datas):
    for table, data in datas.items():
    self.clear(table)
    for d in data:
    self.insert(table, d)
    self.close()


    if __name__ == '__main__':
    db = DB()
    db.close()
    file = open("datas.json", "r")
    datas = json.load(file)
    db.init_data(datas)
    # file = open("datas.yaml", "r")
    # datas = yaml.load(file, Loader=yaml.FullLoader)
    # db.init_data(datas)

    使用json初始化数据

    {
    "api_user": [
    {
    "id": 10,
    "username": "kangraobin",
    "email": "1059457506@qq.com",
    "groups": "http://127.0.0.1:8000/groups/10/"
    },
    {
    "id": 11,
    "username": "kangraobin",
    "email": "1059457506@qq.com",
    "groups": "http://127.0.0.1:8000/groups/10/"
    }
    ],
    "api_group": [
    {
    "id": 11,
    "name": "team11"
    },
    {
    "id": 12,
    "name": "team12"
    }
    ]
    }

    使用yaml初始化数据

    api_user:
    - id: 11
    username: wupeng1
    email: 1059@qq.com
    groups: http://127.0.0.1:8000/groups/11/
    - id: 12
    username: wupeng2
    email: 1059@qq.com
    groups: http://127.0.0.1:8000/groups/12/

    api_group:
    - id: 11
    name: team11
    - id: 12
    name: team12

    Django数据管理-测试用例封装

    test_project文件夹下创建test_django_restful.py文件,用于测试接口

    import unittest
    import requests
    from api.test_project.mysql_action import DB


    class TestRestFul(unittest.TestCase):
    def setUp(self) -> None:
    self.base_url = "http://localhost:8000/users"
    self.auth = ("wupeng", "bb961202")

    # 获取操作与删除操作类似
    def test_get_user(self):
    r = requests.get(self.base_url + "/11/", auth=self.auth)
    result = r.json()
    self.assertEqual(result['username'], "kangraobin")

    # # 添加操作与修改操作类似
    # def test_add_user(self):
    # r = requests.post(self.base_url + "/", data={'username': 'wupeng1', 'email': '1059457506@qq.com'},
    # auth=self.auth)
    # result1 = r.json()
    # self.assertEqual(result1['username'], "wupeng1")
    #
    # # 判断是否授权
    # def test_no_auth(self):
    # r = requests.get(self.base_url)
    # result = r.json()
    # self.assertEqual(result['detail'], "Authentication credentials were not provided.")


    if __name__ == '__main__':
    db=DB()
    file=open("datas.yaml","r")
    db.init_data(file)
    unittest.main()

     待续...

    s = requests.Session()
    
    r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
    print(r.text)
    # '{"cookies": {"from-my": "browser"}}'
    
    r = s.get('http://httpbin.org/cookies')
    print(r.text)
    # '{"cookies": {}}'

    Requests Session 对象的一些优势, 尤其 Session 级别的状态,例如 cookie 就不会被应用到你的请求上去。要获取一个带有状态的 PreparedRequest, 请用 Session.prepare_request() 取代 Request.prepare() 的调用

  • 相关阅读:
    亲历dataguard的一些经验问答题
    [转]ORA-38500: USING CURRENT LOGFILE option not available without stand
    修改npm全局安装模式的路径
    Vue 环境搭建
    Linux下查看系统版本号信息的方法
    每天一个Linux命令(12):su命令
    Ubuntu 首次给root用户设置密码
    适用于Linux的windows子系统
    IDEA的terminal设置成Linux的终端一样
    Windows模拟linux终端工具Cmder+Gow
  • 原文地址:https://www.cnblogs.com/wp950416/p/13159940.html
Copyright © 2011-2022 走看看