带有MIME附件的HTTP POST请求及其Headers multipart/related和xop?
我正在尝试通过eBay的文件传输API上传大量数据。为了做到这一点,我需要向eBay服务器发送一个xml请求字符串,但这个xml请求中有一个<Data>
部分,必须包含你要传输的数据(在这个例子中,是一个经过base64编码的zip文件,里面包含另一个xml文档)。eBay提供了一个C#的示例,教你如何构建这样的文档:https://ebay.custhelp.com/app/answers/detail/a_id/1561
我一直在尝试用python重现这个示例,使用httplib来POST我构建的字符串,方式和示例差不多(这三个UUID是唯一的):
request = """
--MIMEBoundaryurn_uuid_{XMLUUID}
Content-Type: application/xop+xml;charset=UTF-8;type="text/xml;charset=UTF-8";
Content-Transfer-Encoding: binary
Content-ID:<0.urn:uuid:{REQUUID}>
<?xml version="1.0" encoding="utf-8"?>
<uploadFileRequest xmlns:sct=\"http://www.ebay.com/soaframework/common/types\" xmlns="http://www.ebay.com/marketplace/services">
<fileAttachment>
<Size>{Size}</Size>
<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include"
href="cid:urn:uuid:{ATTCHMNTUUID}>"</Data>
</fileAttachment>
<fileFormat>{fileFormat}</fileFormat>
<fileReferenceId>{fileReferenceId}</fileReferenceId>
<taskReferenceId>{taskReferenceId}</taskReferenceId>
</uploadFileRequest>
--MIMEBoundaryurn_uuid_{XMLUUID}
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <urn.uuid:{ATTCHMNTUUID}>\r\n
{Data}
--MIMEBoundaryurn_uuid_{XMLUUID}--
""".replace("\t", "")
request_dict = {
'Size': size,
'Data': payload,
'fileFormat': 'zip',
'fileReferenceId': '50000935383',
'taskReferenceId': '50000847753',
'REQUUID': reqUUID,
'XMLUUID': xmlUUID,
'ATTCHMNTUUID': attchmntUUID,
}
request = request.format( **request_dict )
我的请求头看起来是这样的:
headers = {
'X-EBAY-SOA-OPERATION-NAME': 'uploadFile',
'X-EBAY-SOA-SERVICE-NAME': 'FileTransferService',
'X-EBAY-SOA-SECURITY-TOKEN': #Auth Token,
'Content-type': "multipart/related; boundary=" + boundary + ";type=\"application/xop+xml\";start=\"<0." + "urn:uuid:" + str(requuid) + ">\";start-info=\"text/xml\""
}
然后是我的POST请求:
connection = httplib.HTTPSConnection( 'storage.sandbox.ebay.com' )
connection.request( "POST", '/FileTransferService', request, headers )
当我发送没有MIME附件信息的xml请求时,文件可以正常接受。但是当我尝试像上面的代码那样,使用MIME multipart/related附件,并且用<xop>
标签指明数据在附件中的位置时,POST请求就失败了,我收到了“错误302:临时移动”的响应。这让我觉得我构建的MIME multipart/related xml请求可能有问题,或者是“Content-type”头的声明有问题,或者是我使用xop的方式有问题,最有可能的是这三者的组合都有问题。
我想问的是:我该如何创建一个包含MIME multipart/related部分并且使用xop的xml请求呢?
谢谢你的帮助!
Wes
1 个回答
2
我找到了我请求出问题的原因。问题出在请求字符串的构造和头部信息上。我采用了一种更有条理和程序化的方法来处理这个问题,结果成功了。
下面是构建请求字符串的部分代码:
###########################################
# UUIDs
###########################################
reqUUID= uuid.uuid4()
attchmntUUID = uuid.uuid4()
##########################################
# MIME strings
##########################################
URN_UUID_REQUEST = "<0.urn:uuid:%s>"% reqUUID
URN_UUID_ATTACHMENT = "urn:uuid:%s" % attchmntUUID
MIME_BOUNDARY = "MIME_boundary"
request_dict = {
'Size': size,
'Data': payload,
'fileFormat': 'gzip',
'fileReferenceId': '50000945773',
'taskReferenceId': '50000858033',
'REQUUID': reqUUID,
'ATTCHMNTUUID': attchmntUUID,
}
def build_request( request_dict):
'''
Build the request string with MIME Attachment
'''
request = '<uploadFileRequest xmlns:sct="http://www.ebay.com/soaframework/common/types" xmlns="http://www.ebay.com/marketplace/services">\r\n'
request += '<taskReferenceId>%s</taskReferenceId>\r\n' % request_dict['taskReferenceId']
request += '<fileReferenceId>%s</fileReferenceId>\r\n' % request_dict['fileReferenceId']
request += '<fileFormat>%s</fileFormat>\r\n' % request_dict['fileFormat']
request += '<fileAttachment>\r\n'
request += '<Size>%s</Size>\r\n'% request_dict['Size']
request += '<Data><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:%s"/></Data>\r\n'%URN_UUID_ATTACHMENT
request += '</fileAttachment>\r\n'
request += '</uploadFileRequest>\r\n'
return request
def build_mime_message( request, data ):
'''
Build the xml string with MIME attachments and the base64 encoded data string
'''
request_part = '\r\n'
request_part += '--%s\r\n' % MIME_BOUNDARY
request_part += 'Content-Type: application/xop+xml; charset=UTF-8; type="text/xml; charset=UTF-8"\r\n'
request_part += 'Content-Transfer_Encoding: binary\r\n'
request_part += 'Content-ID: %s\r\n\r\n' % URN_UUID_REQUEST
request_part += '%s\r\n' % request
binary_part = '\r\n'
binary_part += '--%s\r\n' % MIME_BOUNDARY
binary_part += 'Content-Type: application/octet-stream\r\n'
binary_part += 'Content-Transfer-Encoding: base64\r\n'
binary_part += 'Content-ID: <%s>\r\n\r\n' % URN_UUID_ATTACHMENT
binary_part += '%s\r\n' % data
binary_part += '--%s--' % MIME_BOUNDARY
return request_part + binary_part
request = build_request( request_dict )
request = build_mime_message( request, data )#data is base64 encoded gzip compressed xml file
而头部信息则是这样的:
content_type_string = 'multipart/related;'
content_type_string += ' boundary=%s;' % MIME_BOUNDARY
content_type_string += ' type="application/xop+xml";'
content_type_string += ' start="%s";' % URN_UUID_REQUEST
content_type_string += ' start-info="text/xml"'
headers = {
'X-EBAY-SOA-OPERATION-NAME': 'uploadFile',
'X-EBAY-SOA-SERVICE-NAME': 'FileTransferService',
'X-EBAY-SOA-SECURITY-TOKEN': #auth token,
'Content-Length': len( request ),
'Content-Type': content_type_string
}
通过这些信息,我发现问题出在头部和请求中的换行符和制表符上。