RESTful
RESTful一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
REST的特点:
具体表象,请求的就是资源,比如,客户端访问服务器,获取的数据就是资源。比如文字、图片、音视频等。
表现:资源的表现形式。txt格式、html格式、json格式、jpg格式等。浏览器通过URL确定资源的位置,但是需要在HTTP请求头中,用Accept和Content-Type字段指定,这两个字段是对资源表现的描述。
状态转换:客户端和服务器交互的过程。在这个过程中,一定会有数据和状态的转化,这种转化叫做状态转换。其中,GET表示获取资源,POST表示新建资源,PUT表示更新资源,DELETE表示删除资源。
RESTful架构:
每个URL代表一种资源;
客户端通过GET,POST,PUT,DELETE,对服务器资源进行操作.
设计RESTful风格api
一、域名: 将api部署在专用域名下:
http://api.example.com
或者将api放在主域名下:
http://www.example.com/api/
二、版本
将版本号放在api后面
http://www.example.com/app/1.0/
http://www.example.com/app/1.2/
三、使用http标准方法
GET :从服务器获取资源。
POST :在服务器新建资源。
PUT :在服务器更新资源。
DELETE :从服务器删除资源。
四、返回状态码:
返回状态码,可以使用标准http状态码,也可以自定义状态码。
200 OK :服务器成功返回用户请求的数据
201 CREATED :用户新建或修改数据成功。
202 Accepted:表示请求已进入后台排队。
400 INVALID REQUEST :用户发出的请求有错误。
401 Unauthorized :用户没有权限。
403 Forbidden :访问被禁止。
404 NOT FOUND :请求针对的是不存在的记录。
406 Not Acceptable :用户请求的的格式不正确。
500 INTERNAL SERVER ERROR :服务器发生错误。
ok "4001" 成功
NODATA "4002" 数据库查询错误
DATAEXIST "4003" 无数据
DATAERR "4004" 数据已存在
SESSIONERR "4101" 用户未登录
LOGINERR "4102" 用户登录失败
等等自定义返回码
五、错误信息: 一般来说,服务器返回的错误信息,以键值对的形式返回。
{
error:'NODATA'
msg:'数据错误'
}
六、返回数据类型
返回的数据类型一般使用json格式,也有少部分使用xml格式。
七、数据分页
#指定返回数据的数量
http://www.example.com/example?limit=10
#指定返回数据的开始位置
http://www.example.com/example?offset=10
#指定第几页,以及每页数据的数量
http://www.example.com/example?page=2&per_page=20
图片验证码
项目中使用的图片验证码,直接调captcha模块,将图片验证码模块放在libs目录下,
captcha模块中直接调用 generate_captcha方法直接生成随机验证码,以及图片。
from captcha import captcha
# 生成图片验证码
# 名字, 图片验证码的真实值, 验证码图片
name, text, image_data = captcha.generate_captcha()
错误处理:
OSError: cannot open resource
如果在调用验证码模块的时候报上面错误,原因是找不到字体的资源文件,或者字体资源文件不适合当前系统。 可以在自己系统上找到字体资源放到fons文件夹中,并且修改验证码模块的资源名称,windows 系统字体资源在 c盘的 C:\Windows\Fonts , linux系统字体资源在:/usr/share/fonts
接口文档编写格式
接口名称:图片验证码
功能描述: 提供随机图片验证码
接口地址 url: /api/v1/codes/<code_id> code_id 为请求参数
请求方式 methods: GET
请求参数说明:
请求体数据的类型: url中传递
参数名 参数类型 是否必传 说明 示例
code_id 字符串类型 是 图片验证码编号 /api/v1/codes/1
返回值说明:
返回的数据类型: 图片 或者 json字符串
正常: 返回图片
错误: 返回json
参数名 参数类型 是否必传 说明
errno 整数 是 错误编码
errmsg 字符串 是 错误内容
例如:
{ "error": 4001, "errmsg": "验证码失败" }
自定义错误代码
在前端调用api接口的时候,接口视图在某些情况下无法返回正确数据,需要返回错误提示给前端,那么一般会定义一些错误代码。
在项目中我们将错误代码定义到commons.py模块中提供给整个项目使用。
class Error:
OK = "10000"
DBERR = "10001"
NODATA = "10002"
DATAEXIST = "10003"
DATAERR = "10004"
SESSIONERR = "10005"
LOGINERR = "10006"
PARAMERR = "10007"
USERERR = "10008"
ROLEERR = "10009"
PWDERR = "10010"
REQERR = "10011"
IPERR = "10012"
THIRDERR = "10013"
IOERR = "10014"
SERVERERR = "10015"
UNKOWNERR = "10016"
error_map = {
RET.OK : "成功",
RET.DBERR : "数据库查询错误",
RET.NODATA : "无数据",
RET.DATAEXIST : "数据已存在",
RET.DATAERR : "数据错误",
RET.SESSIONERR : "用户未登录",
RET.LOGINERR : "用户登录失败",
RET.PARAMERR : "参数错误",
RET.USERERR : "用户不存在或未激活",
RET.ROLEERR : "用户身份错误",
RET.PWDERR : "密码错误",
RET.REQERR : "非法请求或请求次数受限",
RET.IPERR : "IP受限",
RET.THIRDERR : "第三方系统错误",
RET.IOERR : "文件读写错误",
RET.SERVERERR : "内部错误",
RET.UNKOWNERR : "未知错误",
}
手机验证码
手机验证码在绝大多数的网站都会用到,比如注册,支付等情况下。在所有验证码中算是比较安全但是成本也是非常高的。
在项目中我们使用云通讯第三方短信验证码平台,云通讯新注册用户提供免费短信测试,并且不用实名认证,在开发阶段适合测试。
1、注册账号
2、添加测试号码。测试用户最多只能添加三个测试号码,并且只有测试号码能接收短信。
特别注意:
下载的sdk是python2版本的,我们使用的项目是python3版本,所以官网提供的sdk是不能直接使用的。
python 3版本中没有urllib2模块,需要修改为urllib模块,md5模块在hashlib包中。其他方法我们暂时不需要用,可以先不管。修改也是类似短信的修改方法。
将sdk中的 sendTemplateSMS 方法修改成下面代码:
import hashlib
import base64
import datetime
from urllib import request
import json
from shop.libs.ytx.xmltojson import xmltojson
# 发送模板短信
# @param to 必选参数 短信接收彿手机号码集合,用英文逗号分开
# @param datas 可选参数 内容数据
# @param tempId 必选参数 模板Id
def sendTemplateSMS(self, to,datas,tempId):
self.accAuth()
nowdate = datetime.datetime.now()
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
#生成sig
signature = self.AccountSid + self.AccountToken + self.Batch;
sig = hashlib.md5(signature.encode('utf-8')).hexdigest()
sig = sig.upper()
#拼接URL
url = "https://"+self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SMS/TemplateSMS?sig=" + sig
#生成auth
src = self.AccountSid + ":" + self.Batch;
auth = base64.encodestring(src.encode('utf-8')).strip()
req = request.Request(url)
self.setHttpHeader(req)
req.add_header("Authorization", auth)
#创建包体
b=''
for a in datas:
b+='<data>%s</data>'%(a)
body ='<?xml version="1.0" encoding="utf-8"?><SubAccount><datas>'+b+'</datas><to>%s</to><templateId>%s</templateId><appId>%s</appId>\
</SubAccount>\
'%(to, tempId,self.AppId)
if self.BodyType == 'json':
# if this model is Json ..then do next code
b='['
for a in datas:
b+='"%s",'%(a)
b+=']'
body = '''{"to": "%s", "datas": %s, "templateId": "%s", "appId": "%s"}'''%(to,b,tempId,self.AppId)
req.data = body.encode()
data = ""
try:
res = request.urlopen(req);
data = res.read()
res.close()
if self.BodyType=='json':
#json格式
locations = json.loads(data)
else:
#xml格式
xtj=xmltojson()
locations=xtj.main(data)
if self.Iflog:
self.log(url,body,data)
return locations
except Exception as error:
if self.Iflog:
self.log(url,body,data)
return {'172001':'网络错误'}