base.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Created on 2018-9-17
  4. @author: xiaoxuan.lp
  5. '''
  6. try: import http.client
  7. except ImportError:
  8. import http.client as httplib
  9. import urllib.request, urllib.parse, urllib.error
  10. import time
  11. import hashlib
  12. import json
  13. import dingtalk
  14. import itertools
  15. import mimetypes
  16. import hmac
  17. import base64
  18. '''
  19. 定义一些系统变量
  20. '''
  21. SYSTEM_GENERATE_VERSION = "taobao-sdk-python-dynamicVersionNo"
  22. P_APPKEY = "app_key"
  23. P_API = "method"
  24. P_ACCESS_TOKEN = "access_token"
  25. P_VERSION = "v"
  26. P_FORMAT = "format"
  27. P_TIMESTAMP = "timestamp"
  28. P_SIGN = "sign"
  29. P_SIGN_METHOD = "sign_method"
  30. P_PARTNER_ID = "partner_id"
  31. P_CODE = 'errcode'
  32. P_MSG = 'errmsg'
  33. def sign(secret, parameters):
  34. #===========================================================================
  35. # '''签名方法
  36. # @param secret: 签名需要的密钥
  37. # @param parameters: 支持字典和string两种
  38. # '''
  39. #===========================================================================
  40. # 如果parameters 是字典类的话
  41. if hasattr(parameters, "items"):
  42. keys = list(parameters.keys())
  43. keys.sort()
  44. parameters = "%s%s%s" % (secret,
  45. str().join('%s%s' % (key, parameters[key]) for key in keys),
  46. secret)
  47. sign = hashlib.md5(parameters.encode("utf-8")).hexdigest().upper()
  48. return sign
  49. def mixStr(pstr):
  50. if(isinstance(pstr, str)):
  51. return pstr
  52. elif(isinstance(pstr, str)):
  53. return pstr.encode('utf-8')
  54. else:
  55. return str(pstr)
  56. class FileItem(object):
  57. def __init__(self,filename=None,content=None):
  58. self.filename = filename
  59. self.content = content
  60. class MultiPartForm(object):
  61. """Accumulate the data to be used when posting a form."""
  62. def __init__(self):
  63. self.form_fields = []
  64. self.files = []
  65. self.boundary = "PYTHON_SDK_BOUNDARY"
  66. return
  67. def get_content_type(self):
  68. return 'multipart/form-data;charset=UTF-8; boundary=%s' % self.boundary
  69. def add_field(self, name, value):
  70. """Add a simple field to the form data."""
  71. self.form_fields.append((name, str(value)))
  72. return
  73. def add_file(self, fieldname, filename, fileHandle, mimetype=None):
  74. """Add a file to be uploaded."""
  75. body = fileHandle.read()
  76. if mimetype is None:
  77. mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
  78. self.files.append((mixStr(fieldname), mixStr(filename), mixStr(mimetype), mixStr(body)))
  79. return
  80. def __str__(self):
  81. """Return a string representing the form data, including attached files."""
  82. # Build a list of lists, each containing "lines" of the
  83. # request. Each part is separated by a boundary string.
  84. # Once the list is built, return a string where each
  85. # line is separated by '\r\n'.
  86. parts = []
  87. part_boundary = '--' + self.boundary
  88. # Add the form fields
  89. parts.extend(
  90. [ part_boundary,
  91. 'Content-Disposition: form-data; name="%s"' % name,
  92. 'Content-Type: text/plain; charset=UTF-8',
  93. '',
  94. value,
  95. ]
  96. for name, value in self.form_fields
  97. )
  98. # Add the files to upload
  99. parts.extend(
  100. [ part_boundary,
  101. 'Content-Disposition: form-data; name="%s"; filename="%s"' % \
  102. (field_name, filename),
  103. 'Content-Type: %s' % content_type,
  104. 'Content-Transfer-Encoding: binary',
  105. '',
  106. body,
  107. ]
  108. for field_name, filename, content_type, body in self.files
  109. )
  110. # Flatten the list and add closing boundary marker,
  111. # then return CR+LF separated data
  112. flattened = list(itertools.chain(*parts))
  113. flattened.append('--' + self.boundary + '--')
  114. flattened.append('')
  115. return '\r\n'.join(flattened)
  116. class TopException(Exception):
  117. #===========================================================================
  118. # 业务异常类
  119. #===========================================================================
  120. def __init__(self):
  121. self.errcode = None
  122. self.errmsg = None
  123. self.application_host = None
  124. self.service_host = None
  125. def __str__(self, *args, **kwargs):
  126. sb = "errcode=" + mixStr(self.errcode) +\
  127. " errmsg=" + mixStr(self.errmsg) +\
  128. " application_host=" + mixStr(self.application_host) +\
  129. " service_host=" + mixStr(self.service_host)
  130. return sb
  131. class RequestException(Exception):
  132. #===========================================================================
  133. # 请求连接异常类
  134. #===========================================================================
  135. pass
  136. class RestApi(object):
  137. #===========================================================================
  138. # Rest api的基类
  139. #===========================================================================
  140. def __init__(self, url=None):
  141. #=======================================================================
  142. # 初始化基类
  143. # Args @param domain: 请求的域名或者ip
  144. # @param port: 请求的端口
  145. #=======================================================================
  146. if(url == None):
  147. raise RequestException("domain must not be empty.")
  148. if(url.find('http://') >= 0):
  149. self.__port = 80
  150. pathUrl = url.replace('http://','')
  151. elif(url.find('https://') >= 0):
  152. self.__port = 443
  153. pathUrl = url.replace('https://','')
  154. else:
  155. raise RequestException("http protocol is not validate.")
  156. index = pathUrl.find('/')
  157. if(index > 0):
  158. self.__domain = pathUrl[0:index]
  159. self.__path = pathUrl[index:]
  160. else:
  161. self.__domain = pathUrl
  162. self.__path = ''
  163. # print("domain:" + self.__domain + ",path:" + self.__path + ",port:" + str(self.__port))
  164. def get_request_header(self):
  165. return {
  166. 'Content-type': 'application/json;charset=UTF-8',
  167. "Cache-Control": "no-cache",
  168. "Connection": "Keep-Alive",
  169. }
  170. def getHttpMethod(self):
  171. return "GET"
  172. def getapiname(self):
  173. return ""
  174. def getMultipartParas(self):
  175. return [];
  176. def getTranslateParas(self):
  177. return {};
  178. def _check_requst(self):
  179. pass
  180. def getResponse(self, authrize='',accessKey='',accessSecret='',suiteTicket='',corpId='', timeout=30):
  181. #=======================================================================
  182. # 获取response结果
  183. #=======================================================================
  184. if(self.__port == 443):
  185. connection = http.client.HTTPSConnection(self.__domain, self.__port, None, None, timeout)
  186. else:
  187. connection = http.client.HTTPConnection(self.__domain, self.__port, timeout)
  188. sys_parameters = {
  189. P_PARTNER_ID: SYSTEM_GENERATE_VERSION,
  190. }
  191. if authrize is not None:
  192. sys_parameters[P_ACCESS_TOKEN] = authrize
  193. application_parameter = self.getApplicationParameters()
  194. sign_parameter = sys_parameters.copy()
  195. sign_parameter.update(application_parameter)
  196. header = self.get_request_header();
  197. if(self.getMultipartParas()):
  198. form = MultiPartForm()
  199. for key, value in list(application_parameter.items()):
  200. form.add_field(key, value)
  201. for key in self.getMultipartParas():
  202. fileitem = getattr(self,key)
  203. if(fileitem and isinstance(fileitem,FileItem)):
  204. form.add_file(key,fileitem.filename,fileitem.content)
  205. body = str(form)
  206. header['Content-type'] = form.get_content_type()
  207. else:
  208. body = urllib.parse.urlencode(application_parameter)
  209. if(accessKey != ''):
  210. timestamp = str(int(round(time.time()))) + '000'
  211. print(("timestamp:" + timestamp))
  212. canonicalString = self.getCanonicalStringForIsv(timestamp, suiteTicket)
  213. print(("canonicalString:" + canonicalString))
  214. print(("accessSecret:" + accessSecret))
  215. signature = self.computeSignature(accessSecret, canonicalString)
  216. print(("signature:" + signature))
  217. ps = {}
  218. ps["accessKey"] = accessKey
  219. ps["signature"] = signature
  220. ps["timestamp"] = timestamp
  221. if(suiteTicket != ''):
  222. ps["suiteTicket"] = suiteTicket
  223. if(corpId != ''):
  224. ps["corpId"] = corpId
  225. queryStr = urllib.parse.urlencode(ps)
  226. if (self.__path.find("?") > 0):
  227. fullPath = self.__path + "&" + queryStr
  228. else:
  229. fullPath = self.__path + "?" + queryStr
  230. print(("fullPath:" + fullPath))
  231. else:
  232. if (self.__path.find("?") > 0):
  233. fullPath = (self.__path + "&access_token=" + str(authrize)) if len(str(authrize)) > 0 else self.__path
  234. else:
  235. fullPath = (self.__path + "?access_token=" + str(authrize)) if len(str(authrize)) > 0 else self.__path
  236. if(self.getHttpMethod() == "GET"):
  237. if (fullPath.find("?") > 0):
  238. fullPath = fullPath + "&" + body
  239. else:
  240. fullPath = fullPath + "?" + body
  241. connection.request(self.getHttpMethod(), fullPath, headers=header)
  242. else:
  243. if (self.getMultipartParas()):
  244. body = body
  245. else:
  246. body = json.dumps(application_parameter)
  247. connection.request(self.getHttpMethod(), fullPath, body=body, headers=header)
  248. response = connection.getresponse()
  249. if response.status != 200:
  250. raise RequestException('invalid http status ' + str(response.status) + ',detail body:' + response.read())
  251. result = response.read()
  252. # print("result:" + result)
  253. jsonobj = json.loads(result)
  254. if P_CODE in jsonobj and jsonobj[P_CODE] != 0:
  255. error = TopException()
  256. error.errcode = jsonobj[P_CODE]
  257. error.errmsg = jsonobj[P_MSG]
  258. error.application_host = response.getheader("Application-Host", "")
  259. error.service_host = response.getheader("Location-Host", "")
  260. raise error
  261. return jsonobj
  262. def getCanonicalStringForIsv(self, timestamp, suiteTicket):
  263. if(suiteTicket != ''):
  264. return timestamp + '\n' + suiteTicket
  265. else:
  266. return timestamp
  267. def computeSignature(self, secret, canonicalString):
  268. message = canonicalString.encode(encoding="utf-8")
  269. sec = secret.encode(encoding="utf-8")
  270. return str(base64.b64encode(hmac.new(sec, message, digestmod=hashlib.sha256).digest()))
  271. def getApplicationParameters(self):
  272. application_parameter = {}
  273. for key, value in self.__dict__.items():
  274. if not key.startswith("__") and not key in self.getMultipartParas() and not key.startswith("_RestApi__") and value is not None :
  275. if(key.startswith("_")):
  276. application_parameter[key[1:]] = value
  277. else:
  278. application_parameter[key] = value
  279. #查询翻译字典来规避一些关键字属性
  280. translate_parameter = self.getTranslateParas()
  281. for key, value in application_parameter.items():
  282. if key in translate_parameter:
  283. application_parameter[translate_parameter[key]] = application_parameter[key]
  284. del application_parameter[key]
  285. return application_parameter