1

I'm trying to send a SOAP request with python requests module, where part of the SOAP Body contains a Base64 encoded .csv file. The basic request is working. The file is attached as a Base64-encoded string inside an arbitrary tag inside <soapenv:Body>.

The SOAP service is a facade for an internal email service, which I also maintain. I know it's working because I'm getting the email, and it has an attachment, but the attached file is garbled, for lack of a better word.

I'm fairly new to python, so plenty of this could be bad code, not pythonic, etc.
But most of this code I've copied from another developer, and the rest is working, except for the file attachment piece.

Using python 3.8 and requests 2.26.0

I'm reading the .csv from a local file with the following:

file = open('myfile.csv', 'rb')
file_content = file.read()
base64_data = base64.b64encode(file_content)

I also tried using base64.encodebytes(file_content) with similar results of garbled characters, although they were slightly different. Not sure what the difference in these methods is, but one of them appears to have many more lines, and the other appeared to have fewer lines, which I think is right, as it's a small file with only a few lines.

then creating the request with the following:

def _createRequest(self, toemail, emailsubject, emailbody, file_to_attach):

    soap_request = """<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <soap:Header>
      ...
   </soap:Header>
   <soap:Body>
      <EmailRequest xmlns="...">
         <AddressTo>
            {to_email}
         </AddressTo>
         <From>blah</From>
         <Subject>{subject}</Subject>
         <Body><![CDATA[
             {body}
         ]]></Body>
         <BinaryAttachment filename="myfile.csv" MediaServer="N">{file_base64_encoded}</BinaryAttachment>
         <ContentType>HTMLText</ContentType>
      </EmailRequest>
   </soap:Body>
</soap:Envelope>""".format(to_email=toemail, subject=emailsubject, body = emailbody, file_base64_encoded=file_to_attach)

    return soap_request

I wonder if it has something to do with the headers. We are using Basic auth in the headers with this:

def __getBasicAuthHeaders(self, user_name, password):
    authHeader = base64.b64encode(
        (user_name + ':' + password).encode()).decode()
    return {
        'Authorization': 'Basic ' + authHeader,
        'Content-Type': 'application/xml'
    }
user26270
  • 6,904
  • 13
  • 62
  • 94

1 Answers1

1

By any chance are you sending base64_data as a parameter to the _createRequest function? From your code you seem to expect a string, but you get bytes there. For example:

>>> file = open('test.txt', 'rb')
>>> file_content = file.read()
>>> base64_data = base64.b64encode(file_content)
>>>
>>> soap_request1 = "<BinaryAttachment>{file_base64_encoded}</BinaryAttachment>".format(file_base64_encoded=base64_data)
>>> soap_request1
"<BinaryAttachment>b'Tm90aGluZyB0byBzZWUgaGVyZSE='</BinaryAttachment>"
>>>
>>> soap_request2 = "<BinaryAttachment>{file_base64_encoded}</BinaryAttachment>".format(file_base64_encoded=base64_data.decode())
>>> soap_request2
'<BinaryAttachment>Tm90aGluZyB0byBzZWUgaGVyZSE=</BinaryAttachment>'
>>>

Look at the differences between soap_request1 and soap_request2 and you might find your culprit. Also have a look at this: Convert bytes to a string

Use whatever data encoding you need, most probably utf-8.

You also wondered if the Basic auth has something to do with this. It doesn't, but those encode() / decode() could have provided a clue.

Bogdan
  • 23,890
  • 3
  • 69
  • 61