BaseHTTPRequestHandler实现简单的接口
只想写一些简单的接口,结果踩了好多坑,在此记录一下(含客户端数据上传/下载)
一、环境
- 先介绍一下我用的集成环境吧,之前一直用Eclipse,页面显示相当舒服,语法查错补全也特别给力,但是有一些缺点,比如只能安装在window环境下,要想安装在linux环境下需要该环境有图形页面(是的,我为了用它生生把一个mini版的系统给多安装了个图形页面),再比如tab空格移植到linux环境下后不是标准的4空格,这样就会导致你Eclipse编辑的文件在linux下修改格式会让人很抓狂,当然这个我没经历过,是改我项目的小哥哥告诉我的,他改空格改到眼睛发红,哈哈哈…
基于以上两点,本次集成环境用的是Pycharm,界面太丑!Pycharm连的远程linux上的python解释器,然后把本地代码实时同步到linux环境上,这样就能避免两套环境不一致导致同一个脚本出现不同的错误。
Pycharm连远程解释器链接:https://blog.youkuaiyun.com/five3/article/details/78615589
- Linux:centos6.5
- python:python2.6
二、代码功能介绍
1.BaseHTTPRequestHandler介绍
这是一个以TCPServer为基础开发的模块,可以在请求外层添加http协议报文,发送http协议。
基于BaseHTTPServer 的Http Server的处理流程:
1.HTTPServer绑定对应的应答类(BaseHTTPRequestHandler )
http_server = HTTPServer((’’, int(port)), ServerHTTP)
2.监听端口:
http_server.serve_forever()
serve_forever()方法使用select.select()循环监听请求,当接收到请求后调用
当监听到请求时,取出请求对象
3.应答:
创建新线程以连接对象(开始理解成请求对象)为参数实例化应答类:ServerHTTP()
应答类根据请求方式调用ServerHTTP.do_XXX处理方法
常用方法/属性:
BaseHTTPRequestHandler.path #包含的请求路径和GET请求的数据
BaseHTTPRequestHandler.command #请求类型GET、POST…
BaseHTTPRequestHandler.request_version #请求的协议类型HTTP/1.0、HTTP/1.1
BaseHTTPRequestHandler.headers #请求的头
BaseHTTPRequestHandler.responses #HTTP错误代码及对应错误信息的字典
BaseHTTPRequestHandler.handle() #用于处理某一连接对象的请求,调用handle_one_request方法处理
BaseHTTPRequestHandler.handle_one_request() #根据请求类型调用do_XXX()方法,XXX为请求类型
BaseHTTPRequestHandler.do_XXX() #处理请求
BaseHTTPRequestHandler.send_error() #发送并记录一个完整的错误回复到客户端,内部调用send_response()方法实现
BaseHTTPRequestHandler.send_response() #发送一个响应头并记录已接收的请求
BaseHTTPRequestHandler.send_header() #发送一个指定的HTTP头到输出流。 keyword 应该指定头关键字,value 指定它的值
BaseHTTPRequestHandler.end_headers() #发送一个空白行,标识发送HTTP头部结束
BaseHTTPRequestHandler.wfile #self.connection.makefile(‘rb’, self.wbufsize) self.wbufsize = -1 应答的HTTP文本流对象,可写入应答信息
BaseHTTPRequestHandler.rfile #self.connection.makefile(‘wb’, self.rbufsize) self.rbufsize = 0 请求的HTTP文本流对象,可读取请求信息
参考:https://blog.youkuaiyun.com/xhw88398569/article/details/49179967
2. 简单的post请求,代码如下:
def do_POST(self)
enc = "UTF-8" #设置返回的编码格式
path = str(self.path) #获取请求的url
length = int(self.headers.getheader('content-length')) #获取除头部后的请求参数的长度
datas = urlparse.parse_qs(self.rfile.read(length), keep_blank_values=1) #获取请求参数数据,请求数据为json字符串
if path=="/scandatas":
models = datas["models"][0] #获取models参数数据
pass(可以添加对参数的逻辑处理)
#以下是返回报文
self.send_response(200) #返回状态码
self.send_header("Content-type", "text/html;charset=%s" % enc) #返回响应头内容
self.end_headers() #返回响应头结束
buf = {"status": 0, #返回包体数据
"data": {
"filepath": "上传答题卡成功"}}
self.wfile.write(json.dumps(buf)) #发送json格式的返回包体
如果返回html,buf如下
buf = '''<!DOCTYPE HTML>
<html>
<head><title>Post page</title></head>
<body>Post Data:%s <br />Path:%s</body>
</html>'''%(datas,self.path)
3. 文件下载,代码如下(含get请求):
def do_POST(self):
path=self.path
if path=="/download":
models=datas["models"][0]
self.send_response(200)
self.send_header("Content-type","text/html;charset=%s" % enc)
self.end_headers()
buf ={"status": 0,
"data":{
"filepath": "http://capi.syaccuracytest.com/download/zip/"+models+".zip?"+models}} #返回文件下载地址(点击下载地址是个get请求)
self.wfile.write(json.dumps(buf))
def do_GET(self):
paths = self.path
query = urllib.splitquery(paths)
datas = query[1]
models = datas
path=str(query[0])
enc = "UTF-8"
if path[:13]=="/download/zip": #判断下载路径
fn = os.path.join(INPUTF_PATH, models+".zip") #下载文件地址
try:
resultf = open(fn, 'rb') #以读的形式打开下载文件
except BaseException:
self.send_response(200) #请求下载的文件不存在返回文件不存在报文
self.send_header("Content-type", "text/html;charset=%s" % enc)
self.end_headers()
print "不存在"
buf = "获取扫描数据失败,请重新获取数据"
else:
self.send_response(200) #文件存在返回状态码
self.send_header("Content-type", "application/octet-stream") #返回请求格式为"application/octet-stream"
self.end_headers()
buf = resultf.read() #读取文件发送给客户端
self.wfile.write(buf)
4. 文件上传
代码如下
def do_POST(self):
enc = "UTF-8"
path = str(self.path)
if path=="/upload":
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD': 'post',
'CONTENT_TYPE': self.headers['Content-Type']
}
)
models=form['models'].value #获取models参数值
datas=form['file'].value #获取上传文件内容
fname=models+".zip"
fn = os.path.join(INPUTF_PATH, fname) #生成文件存储路径
outf = open(fn, 'wb') #写打开文件
outf.write(datas) #将接收到的内容写入文件
outf.close() #关闭文件
self.send_response(200)
self.send_header("Content-type", "text/html;charset=%s" % enc)
self.send_header("test", "This is test!")
self.end_headers()
buf = {"status": 0,
"data":{
"msg": u"上传成功"}}
self.wfile.write(json.dumps(buf))
cgi.FieldStorage用法
参考:https://blog.youkuaiyun.com/langqing12345/article/details/46539545
注:使用cgi.FieldStorage前,一定不要对data做任何处理,我也不知道为什么,反正调试不过,知道原因后会再更上来的
最后提示各位,如果你只是想自己写个接口玩玩,可以用BaseHTTPRequestHandler来尝试,否则一定不要用它,特别难写且好多坑,也没有很多参考文档。
当然,如果你是python大佬,随您用哎喂!