《Web接口开发与自动化测试基于Python语言》--第13章

第13章 REST

REST同样属于Web Services技术范围。REST定义了一组体系框架原则,根据这些原则设计以系统资源为中心的Web 服务,包括使用不同语言编写的客户端如果通过HTTP处理和传输资源状态。REST可以说是近年来最主要的Web服务设计模型,基本取代了SOAP和WSDL。

13.1 RPD与REST

  • RPC

Remote Procedure Call,远程过程调用。RPC风格的开发关注于服务器/客户端之间的方法调用,而并不关注基于哪个网络层的哪种协议。

RPC风格的代表是:XML-RPC、大Web 服务

XML-RPC

XML-RPC是一种使用XML格式封装的方法的调用,并使用HTTP协议作为传送机制的RPC风格的实现。

XML-RPC的请求方法是:HTTP协议的POST方法,请求和响应的数据格式均为XML。

测试用例管理系统TestLink的对外接口就是PHP开发的XML-RPC。

大Web服务

即Big Web Services,是基于SOAP+WSDL+UDDI等技术实现RPC风格的大型Web服务的统称。

  • REST

Representational State Transfer,即表现层状态转化。

REST具有跨语言、跨平台的特点,它是一种遵循REST风格的Web Services。

如果一个架构符合REST原则,就称它为RESTful架构。

资源(Resources)

表现层其实指的是“资源”,所谓“资源”就是网络的一个实体,或者说是一个具体信息。可以用一个URI(统一资源定位符)来指向它,要想获取这个资源,只需要访问它的URI即可。

表现层(Representation)

“资源”是一种信息实体,它可以有多种外在表现形式,这种把“资源”具体呈现出来的形式,叫做它的“表现层”。

eg:文本既可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式、二进制格式来表现。

URI只代表资源的实体,不代表它的形式。

状态转化(State Transfer)

HTTP协议是一个无状态协议,所有的状态都保存在服务器端,客户端如果想要操作服务器,就必须通过某种手段,让服务器端发生“状态转化”,而这种转化是建立在表现层之上的,所以是“表现层状态转化”。

客户端用到的手段,只能是HTTP协议,具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT和DELETE。

GET:用来获取资源;

POST:用来新建资源(也可用于更新资源)

PUT:用来更新资源

DELETE:用来删除资源

综合以上内容,RESTful架构可以总结为:

  • 每一个URI代表一种资源

  • 客户端和服务器之间,传递这种资源的某种表现层

  • 客户端通过四个HTTP动词,对服务器端资源进行操作,实现“表现层状态转化”

以上这些概念的一个包含关系:

SOA--Web Services--RPC--XML-RPC
       |            |----Big Web Services--SOAP
       |                            |------WSDL
       |                            |------UDDI
       |-----------REST

13.2 Django REST Framework

顾名思义,是一套基于Django的REST风格的架构。

它具有以下特点:

  • 功能强大、灵活,可以帮助你快速开发Web API

  • 支持认证策略,包括OAuth 1a和OAuth 2

  • 支持ORM和非ORM数据源的序列化

  • 丰富的文档以及良好的社区支持

官方网址:http://www.django-rest-framework.org/

13.2.1 创建简单的API

安装Django REST Framework:

pip install djangorestframework
pip install markdown
pip install django-filter

安装好之后,创建一个新的项目django_rest,在项目下创建“api”应用:

django-admin startproject django_rest
cd django_rest
python manage.py startapp api

进入django_rest,修改settings.py文件:

# Application definition

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',
    )
}

rest_framework:为Django REST Framework应用;
api:为我们自己创建的应用;
DEFAULT_PERMISSION_CLASSES:默认的权限策略可以设置在全局范围内;

执行数据库迁移:python manage.py migrate

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

Ps:这里我自己创建的是,admin、admin@mail.com、那啥+0

创建数据序列化,在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')

Serializers用于定义API的表现形式,如返回哪些字段、返回怎样的格式等,这里序列化Django自带的User和Group。

继续编辑视图文件:api/views.py

#! /usr/bin/python
# -*- coding:utf-8 -*-

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

# Create your views here.

# ViewSets定义视图的展现形式
class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

class GroupViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

其中,ViewSets用于定义视图的展现形式,eg:返回哪些内容、需要做哪些权限处理。
在URL中会定义相应的规则到ViewSet,ViewSets则通过serializer_class找到对应的Serializers。这里将User和Group的所有对象赋予queryset,并返回这些值。在UserSerializer和GroupSerializer中定义要返回的字段。

继续编辑url文件:django_rest/urls.py

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

# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

因为使用的是ViewSets,所以可以使用routers类自动生成URL conf。

启动该服务:python manage.py runserver,启动成功后,访问:http://127.0.0.1:8000的效果如下:

这里写图片描述

13.2.2 添加接口数据

通过前面创建的超级管理员账号登录,然后:

13.2.3 测试接口

针对我们上面添加的用户、用户组的接口查询,使用Requests库编写测试用例:

#! /usr/bin/python
# -*- coding:utf-8 -*-

import unittest
import requests

class UserTest(unittest.TestCase):
    '''用户查询测试'''

    def setUp(self):
        self.base_url = 'http://127.0.0.1:8000/users'
        self.auth = ('admin', 'admin123456')

    def test_user1(self):
        '''test user admin'''
        r = requests.get(self.base_url+'/1/', auth=self.auth)
        result = r.json()
        self.assertEqual(result['username'], 'admin')
        self.assertEqual(result['email'], 'admin@mail.com')

    def test_user2(self):
        '''test user tom'''
        r = requests.get(self.base_url+'/2/', auth=self.auth)
        result = r.json()
        self.assertEqual(result['username'], 'tom')
        self.assertEqual(result['email'], 'tom@mail.com')

    def test_user3(self):
        '''test user jack'''
        r = requests.get(self.base_url+'/3/', auth=self.auth)
        result = r.json()
        self.assertEqual(result['username'], 'jack')
        self.assertEqual(result['email'], 'jack@mail.com')

class GroupsTest(unittest.TestCase):
    '''用户组查询测试'''

    def setUp(self):
        self.base_url = 'http://127.0.0.1:8000/groups'
        self.auth = ('admin', 'admin123456')

    def test_groups1(self):
        '''test groups test'''
        r = requests.get(self.base_url+'/1/', auth=self.auth)
        result = r.json()
        self.assertEqual(result['name'], 'test')

    def test_groups2(self):
        '''test groups developer'''
        r = requests.get(self.base_url+'/2/', auth=self.auth)
        result = r.json()
        self.assertEqual(result['username'], 'developer')

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

注意:

  1. 请求接口的资源不是通过接口参数(?user=1)访问,而是通过URI路径(/1/)访问;
  2. 接口的访问需要用户签名,在发送get()请求时需要指定auth参数。

13.3 集成发布会系统API

通过Django REST Framework来实现发布会签到系统接口更简单。

13.3.1 添加发布会API

在我们创建的django_rest项目的基础上增加发布会和嘉宾的相关接口。

首先,创建模型,修改文件:api/models.py

#! /usr/bin/python
# -*- coding:utf-8 -*-

from __future__ import unicode_literals

from django.db import models

# Create your models here.
# 发布会
class Event(models.Model):
    name = models.CharField(max_length=100)
    limit = models.IntegerField()
    status = models.BooleanField()
    address = models.CharField(max_length=200)
    start_time = models.DateTimeField('events time')
    create_time = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

# 嘉宾
class Guest(models.Model):
    event = models.ForeignKey(Event)
    realname = models.CharField(max_length=64)
    email = models.EmailField()
    sign = models.BooleanField()
    create_time = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ('phone', 'event')

    def __str__(self):
        return self.realname

执行数据库迁移:

python manage.py makemigrations api

python manage.py migrate

继续添加发布会数据序列化,编辑文件:api/serializers.py

#!/usr/bin/python
# -*- coding:utf-8 -*-

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

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

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

class EventSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Event
        fields = ('url', 'name', 'address', 'start_time', 'limit', 'status')

class GuestSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Guest
        fields = ('url', 'realname', 'phone', 'email', 'sign', 'event')

继续定义发布会和嘉宾视图,编辑文件:api/views.py

#! /usr/bin/python
# -*- coding:utf-8 -*-

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

# Create your views here.

# ViewSets定义视图的展现形式
class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

class GroupViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

class EventViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows events to be viewed or edited
    """
    queryset = Event.objects.all()
    serializer_class = EventSerializer

class GuestViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows guests to be viewed or edited
    """
    queryset = Guest.objects.all()
    serializer_class = GuestSerializer

继续添加URL配置,编辑文件:django_rest/urls.py

"""django_rest URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from api import views

# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
router.register(r'events', views.EventViewSet)
router.register(r'guests', views.GuestViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

重启项目,访问后的效果如下:

这里写图片描述

13.3.2 测试接口

使用Django REST Framework开发的接口,除了可以使用GET方法查询接口数据外,还可以调用接口添加数据,并不需要关心接口插入数据的细节,只需要将接口请求改为POST即可。

使用Postman添加一条发布会接口,额,最近无法访问谷歌,这个工具也用不了,就使用了另外一个类似工具做测试:

这里写图片描述

13.4 soapUI测试工具

soapUI是一款针对REST和SOAP的功能和性能测试工具:https://www.soapui.org/

13.4.1 创建SOAP测试项目

Projects-New SOAP Project:

Project Name:MobileCodeWS

Initial WSDL:http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl

依次展开:MobileCodeWS–MobileCodeWSSoap–getMobileCodeInfo

双击:Request1,填写接口查询的手机号,点击运行按钮,即可得到结果。

注意: 新版本中要求填写userID,免费用户的userID是空白,默认为“?”问号,不修改的话会报错。

这里写图片描述

Ps:我已经把手机号去掉显示了。

13.4.2 创建REST测试项目

继续创建一个REST项目。

认证选项里各选项的含义:

  • Username:用于填写基本认证的用户名;

  • Password:用于填写基本认证的密码;

  • Domain:域名是基本认证的可选项,设置为空;

  • Pre-emptive auth:设置定义认证的行为;

  • Use global preference:用于定义HTTP设置为全局首选项;

  • Authenticate pre-emptively:仅适用于此请求,不需要等待身份验证时才发送凭据。

soapUI的官方文档:https://www.soapui.org/siapui-projects/soapui-projects.html

总结

Djanog REST Framework在实际使用中还是比较多的,针对于这块的测试方法,除了工具,就是代码测试的办法了,建议大家还是多对此部分内容进行深入学习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值