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

xiaoxiao2021-02-27  256

第8章 开发web接口

因为要对web接口进行测试,虫师在前面大致讲解了web开发的过程,接下来就是web接口开发。所以,了解了什么是web接口开发,也就很容易进行web接口测试了。

8.1 为何要开发Web接口

总的来说,就是当开发大型B/S架构的程序时,前后端分离,当前端想要调用后台程序,只需要调用接口即可。这样带来的好处就是:

后端不必精通前端技术(HTML5/JavaScript/CSS),只专注于数据的处理并提供Web接口即可;

前端的专业性越来越高,通过调用Web接口获取数据,从而专注于数据展示和页面交互设计;

Web接口的应用范围更加广泛,由后端开发的接口既可以提供给Web页面调用,也可以提供移动APP调用等;

8.2 什么是Web接口

通过数据传输协议(eg:HTTP/SOAP……)传输一定格式(eg:XML/JSON/CSV……)数据的接口。

在当前Web接口中,HTTP协议+JSON数据格式,是目前最流行的两个接口技术。

8.2.1 HTTP

HTTP(Hyper Text Transfer Protocol,超文本传输协议)

主要特点:

无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接,采用这种方式可以节省传输时间;

媒体独立:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送,客户端和服务器指定使用适合的MIME-type内容类型;

无状态:无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大,如果服务器不需要先前的信息,那么它的应答就比较快。

HTTP请求方法:

请求方法说明(V1.1)GET请求指定的页面信息,并返回实体主体POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中,POST请求可能会导致新的资源的建立或已有资源的修改HEAD类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头PUT从客户端向服务器传送的数据取代指定文档的内容DELETE请求服务器删除指定的页面TRACE请求服务器返回收到的请求信息,主要用于测试或诊断CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器OPTIONS请求查询服务器的性能,或者查询与资源相关的选项和需求

状态响应码:

当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(Server Header)用以响应浏览器的请求。

HTTP状态码分为五类:

1**:信息,服务器收到请求,需要请求者继续执行操作;

2**:成功,操作被成功接收并处理;

3**:重定向,需要进一步的操作以完成请求;

4**:客户端错误,请求包含语法错误或无法完成请求;

5**:服务器错误,服务器在处理请求的过程中发生了错误。

常见状态代码和状态说明:

200 OK: 请求成功,一般用于GET或POST请求。

302 Fund: 临时移动,资源只是临时被移动,客户端应继续使用原有URI。

400 Bad Request: 客户端请求有语法错误,不能被服务器所理解。

401 Unauthorized: 请求要求用户的身份认证。

403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求。

404 Not Found: 服务器无法根据客户端的请求找到资源。

500 Internal Server Error: 服务器内部错误,无法完成请求。

503 Server Unavailable: 由于超载或系统维护,服务器暂时无法处理客户端请求。

请求头信息与响应头信息:

请求头信息:请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息,常用的请求报头如下:

Accept:浏览器可接受的MIME类型。

Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。

Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时会用到。

Connection:表示是否需要持久连接,从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性。

Host:初始URL中的主机和端口,它通常是从HTTP URL中提取出来的。

User-Agent:请求报头域允许客户端将它的操作系统、浏览器和其他属性告诉服务器。

响应头信息:响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息,常用的响应报头如下:

Content-Type:表示后面的文档属于哪种MIME类型。

Date:当前的GMT(国际时间)。

Server:包含了服务器用来处理请求的软件信息。

X-Frame-Options:用来给浏览器指示允许一个页面可否在<frame>、<iframe>或<object>中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌到别人的网站中去,从而避免了点击劫持(click jacking)的攻击。

8.2.2 JSON格式

JSON:JavaScript Object Notation,即JavaScript对象表示法。

JSON解析器和JSON库支持不同的编程语言,JSON具有自我描述性,很容易理解。

JSON语法是JavaScript对象表示法语法的子集:

数据在名称/值对中。

数据由逗号分隔。

花括号保存对象。

方括号保存数组。

8.3 开发系统Web接口

继续开发之前的发布会签到系统的Web接口。

8.3.1 配置接口路径

开发Web接口的访问方式与开发系统的访问方式相同,为了进行区分,这里设置Web接口的根目录为“/api/”,通过二级目录表示实现具体功能的接口。

eg:

http://127.0.0.1:8000/api/add_event/ 表示添加发布会接口 http://127.0.0.1:8000/api/get_event_list/ 表示查询发布会接口

修改/home/test/guest/guest/urls.py文件,添加接口根路径“/api/”:

from django.conf.urls import url, include urlpatterns = [ …… url(r'^api/', include('sign.urls', namespace="sign")), ]

在sign应用下创建urls.py文件,即/home/test/guest/sign/urls.py,用来配置具体接口的二级目录:

from django.conf.urls import url from sign import views_if urlpatterns = [ # sign system interface: # ex: /api/add_event/ url(r'^add_event/', views_if.add_event, name='add_event'), # ex: /api/add_guest/ url(r'^add_guest/', views_if.add_guest, name='add_guest'), # ex: /api/get_event_list/ url(r'^get_event_list', views_if.get_event_list, name='get_event_list'), # ex: /api/get_event_list/ url(r'^get_event_list', views_if.get_event_list, name='get_event_list'), # ex: /api/get_guest_list/ url(r'^get_guest_list', views_if.get_guest_list, name='get_guest_list'), # ex: /api/user_sign/ url(r'^user_sign', views_if.user_sign, name='user_sign'), ]

这里看到我们从sign导入了一个views_if,这是为了避免Web接口代码与views.py文件中的系统功能代码混在一起,因此需要重新在sign应用下创建views_if.py文件,即/home/test/guest/sign/views_if.py文件,用于Web接口的开发。

8.3.2 添加发布会接口

修改文件/home/test/guest/sign/views_if.py,增加发布会接口:

#! /usr/bin python # -*- coding:utf-8 -*- from django.http import JsonResponse from sign.models import Event from django.core.exceptions import ValidationError # 添加发布会接口 def add_event(request): eid = request.POST.get('eid', '') name = request.POST.get('name', '') limit = request.POST.get('limit', '') status = request.POST.get('status', '') address = request.POST.get('address', '') start_time = request.POST.get('start_time', '') if eid == '' or name == '' or limit == '' or address == '' or start_time == '': return JsonResponse({'status':10021, 'message':'parameter error'}) result = Event.objects.filter(id=eid) if result: return JsonResponse({'status':10022, 'message':'event id already exists'}) result = Event.objects.filter(name=name) if result: return JsonResponse({'status':10023, 'message':'event name already exists'}) if status == '': status = 1 try: Event.objects.create(id=eid, name=name, limit=limit, address=address, status=int(status), start_time=start_time) except ValidationError as e: error = 'start_time format error. It must be in YYYY-MM-DD HH:MM:SS format.' return JsonResponse({'status':10024, 'message':error}) return JsonResponse({'status':200, 'message':'add event success'})

对上面代码进行分析:

首先,通过POST请求接收发布会参数,包括发布会id、名称、人数、状态、地址和时间;

然后,判断eid、name、limit、address、start_time是否为空,如果为空则返回参数错误;

然后,判断eid、name是否存在,如果存在说明添加数据重复,返回对应状态码和提示信息;

然后,判断status,状态不是必传字段,如果判断为空,则置为1=True=开启状态;

最后,将数据插入Event表,如果插入数据日期格式错误,则抛出ValidationError异常,异常处理接收该异常并返回响应的状态码和提示,否则插入成功,返回200状态码。

注意:

JsonResponse()是一个非常有用的类,它可以将字典转化成JSON格式返回给客户端。

8.3.3 查询发布会接口

修改文件/home/test/guest/sign/views_if.py,增加发布会查询接口:

# 查询发布会接口 def get_event_list(request): eid = request.GET.get('eid', '') name = request.GET.get('name', '') if eid == '' and name == '': return JsonResponse({'status':10021, 'message':'parameter error'}) if eid != '': event = {} try: result = = Event.objects.get(id=eid) except ObjectDoesNotExist: return JsonResponse({'status':10022, 'message':'query result is empty'}) else: event['name'] = result.name event['limit'] = result.limit event['status'] = result.status event['address'] = result.address event['start_time'] = result.start_time return JsonResponse({'status':200, 'message':'success', 'data':event}) if name != '': datas = [] results = Event.objects.filter(name__contains=name) if results: for r in results: event = {} event['name'] = r.name event['limit'] = r.limit event['status'] = r.status event['address'] = r.address event['start_time'] = r.start_time datas.append(event) return JsonResponse({'status':200, 'message':'success', 'data':datas}) else: return JsonResponse({'status':10022, 'message':'query result is empty'})

对上述代码进行分析:

通过GET请求接收发布会id(eid)和发布会名称(name),两个参数都为可选项,但不能同时为空,否则接口代码返回状态码10021和parameter error错误提示;

如果发布会id不为空,则优先使用发布会id查询,因为id具有唯一性,所以,查询结果只会有一条。将查询结果以字典的形式存放到定义的event中,并将event作为借口返回字典中data对应的值;

发布会名称name为模糊查询,查询数据可能会有多条,返回的数据格式会稍显复杂,首先将查询的每一条数据放到一个event字典中,再把每个event字典放到datas数组中,最后将整个datas数组作为借口返回字典中的data对应的值。

8.3.4 添加嘉宾接口

修改文件/home/test/guest/sign/views_if.py,增加添加嘉宾接口:

from sign.models import Event, Guest from django.db.utils import IntegrityError import time # 添加嘉宾接口 def add_guest(request): eid = request.POST.get('eid', '') realname = request.POST.get('realname', '') phone = request.POST.get('phone', '') email = request.POST.get('email', '') if eid == '' or realname == '' or phone == '': return JsonResponse({'status':10021, 'message':'parameter error'}) result = Event.objects.filter(id=eid) if not result: return JsonResponse({'status':10022, 'message':'event id null'}) result = Event.objects.get(id=eid).status if not result: return JsonResponse({'status':10023, 'message':'event status is not available'}) event_limit = Event.objects.get(id=eid).limit guest_limit = Guest.objects.filter(event_id=eid) if len(guest_limit) >= event_limit: return JsonResponse({'status':10024, 'message':'event number is full'}) event_time = Event.objects.get(id=eid).start_time etime = str(event_time).split(".")[0] timeArray = time.strptime(etime, "%Y-%m-%d %H:%M:%S") e_time = int(time.mktime(timeArray)) now_time = str(time.time()) ntime = now_time.split(".")[0] n_time = int(ntime) if n_time >= e_time: return JsonResponse({'status':10025, 'message':'event has started'}) try: Guest.objects.create(realname=realname, phone=int(phone), email=email, sign=0, event_id=int(eid)) except IntegrityError: return JsonResponse({'status':10026, 'message':'the event guest phone number repeat'}) return JsonResponse({'stauts':200, 'message':'add guest success'})

对上述代码进行分析:

通过POST请求接受嘉宾参数:关联发布会id(eid)、姓名(realname)、手机号(phone)和邮箱(mail)等参数;

判断eid、realname、phone等参数均不为空;

判断嘉宾管理的发布会id(eid)是否存在,以及关联的发布会状态是否为True(即1),如果不为True,则说明当前为关闭状态,返回相应的状态码和提示信息;

判断当前时间是否大于发布会时间,如果大于则说明发布会已经开始,或者早已经结束。那么该发布会就不能再添加嘉宾了;

插入嘉宾数据,如果手机号(phone)已存在,则抛出IntegrityError异常,接收该异常并返回相应的状态码和提示信息,如果插入成功,则返回状态码200和提示信息。

8.3.5 查询嘉宾接口

修改文件/home/test/guest/sign/views_if.py,增加查询嘉宾接口:

# 查询嘉宾接口 def get_guest_list(request): eid = request.POST.get('eid', '') phone = request.POST.get('phone', '') if eid == '': return JsonResponse({'status':10021, 'message':'eid cannot be empty'}) if eid != '' and phone == '': datas = [] results = Guest.objects.filter(event_id=eid) if results: for r in results: guest = {} guest['realname'] = r.realname guest['phone'] = r.phone guest['email'] = r.email guest['sign'] = r.sign datas.append(guest) return JsonResponse({'status':200, 'message':'success', 'data':datas}) else: return JsonResponse({'status':10022, 'message':'query result is empty'}) if eid != '' and phone != '': guest = {} try: result = Guest.objects.get(phone=phone, event_id=eid) except ObjectDoesNotExist: return JsonResponse({'status':10022, 'message':'query result is empty'}) else: guest['realname'] = result.realname guest['phone'] = result.phone guest['email'] = result.email guest['sign'] = result.sign return JsonResponse({'status':200, 'message':'success', 'data':guest})

对上述代码进行分析:

查询嘉宾接口与查询发布会接口相似,只是参数和查询条件的判断有所不同,这里不再赘述。

8.3.6 发布会签到接口

修改文件/home/test/guest/sign/views_if.py,增加发布会签到接口:

# 嘉宾签到接口 def user_sign(request): eid = request.POST.get('eid', '') phone = request.POST.get('phone', '') if eid == '' or phone == '': return JsonResponse({'stauts':10021, 'message':'parameter error'}) result = Event.objects.filter(id=eid) if not result: return JsonResponse({'stauts':10022, 'message':'event id null'}) result = Event.objects.get(id=eid).stauts if not result: return JsonResponse({'stauts':10023, 'message':'event status is not available'}) event_time = Event.objects.get(id=eid).start_time etime = str(event_time).split(".")[0] timeArray = time.strptime(etime, "%Y-%m-%d %H:%M:%S") e_time = int(time.mktime(timeArray)) now_time = str(time.time()) ntime = now_time.split(".")[0] n_time = int(ntime) if n_time >= e_time: return JsonResponse({'status':10024, 'message':'event has started'}) result = Guest.objects.filter(phone=phone) if not result: return JsonResponse({'status':10025, 'message':'user phone null'}) result = Guest.objects.filter(event_id=eid, phone=phone) if not result: return JsonResponse({'status':10026, 'message':'user did not participate in the conference'}) result = Guest.objects.get(event_id=eid, phone=phone).sign if not result: return JsonResponse({'status':10027, 'message':'user has sign in'}) else: Guest.objects.filter(event_id=eid, phone=phone).update(sign='1') return JsonResponse({'status':200, 'message':'sign success'})

对上述代码进行分析:

签到接口通过POST请求接收参数:发布会id(eid)、嘉宾手机号(phone);

判断发布会id(eid)和嘉宾手机号(phone)两个参数均不能为空;

通过查询发布会表判断发布会id(eid)是否存在,如果不存在,则返回相应的状态码和提示信息;

再判断发布会状态是否为True,如果不为True,则说明发布会状态当前未开启,返回相应的状态码和提示信息;

判断当前时间是否大于发布会时间,如果大于则说明发布会已经开始,不允许签到;

再判断嘉宾的手机号是否存在,如果不存在,则返回相应的状态码和提示信息;

判断嘉宾的手机号与发布会id是否为对应关系,如果不是对应关系,则返回相应的状态码和提示信息;

判断该嘉宾的状态是否为已签到,如果已签到,则返回相应的状态码和提示信息,如果未签到,则修改状态为已签到,并返回状态码200和提示信息。

8.4 编写Web接口文档

通过接口文档,可以方便其他人调用你的接口,接口文档必须满足以下:

信息完整

及时更新

内容准确

以下就是上述接口对应的接口文档:

发布会接口

其余接口也基本都类似,这里也就不再赘述了。

8.5 总结

本章主要是讲解了接口的概念,以及如何实现接口,以及接口文档的范例。对于如何调用接口,本章还未提及,应该是下面章节的内容了。

我们可以总结出接口代码的特点:

通过GET/POST接收参数

对传入的参数进行判断

对合法参数进行数据库的插入或查询

对每一步无论正确与否都返回状态码和提示信息

记住以上这些内容,基本就掌握接口的概念了。期待下一章的内容。

转载请注明原文地址: https://www.6miu.com/read-10163.html

最新回复(0)