Howto send a valid XML POST request to ebay API via Python? - python

I guess this is rather a general problem than ebay specific, but I am not sure: I am trying to send an XML request to the ebay developer API to retrieve an XML response. When using curl, everything works fine and I get an XML response telling me which API-keys are missing (if I would provide them via HTTP headers I would get a valid XML result):
curl -d '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>' \
http://svcs.sandbox.ebay.com/services/search/FindingService/v1
Which leads to the correct response:
<?xml version='1.0' encoding='UTF-8'?>
<ms:errorMessage xmlns:ms="http://www.ebay.com/marketplace/services" xmlns="http://www.ebay.com/marketplace/search/v1/services">
<error>
<errorId>2038</errorId>
<domain>CoreRuntime</domain>
<severity>Error</severity>
<category>System</category>
<message>Missing SOA operation name header</message>
<subdomain>System</subdomain>
</error>
</ms:errorMessage>
But when I try to work with Python I just get "500 Internal Server error", no matter how basic I make my examples. I have tried two very basic methods:
Number one:
serverUrl = 'svcs.sandbox.ebay.com'
xmlparameters = '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>'
webservice = httplib.HTTP(serverUrl)
webservice.putrequest("POST", "/services/search/FindingService/v1")
webservice.putheader("Host", serverUrl)
webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
webservice.putheader("Content-length", "%d" % len(xmlparameters))
webservice.endheaders()
webservice.send(xmlparameters)
Number two (which is my prefered method):
serverUrl = 'svcs.sandbox.ebay.com'
xmlparameters = '<!xml version="1.0" encoding="utf-8"?><findItemsByKeywordsRequest xmlns="http://www.ebay.com/marketplace/search/v1/services"><keywords>harry potter phoenix</keywords></findItemsByKeywordsRequest>'
connection = httplib.HTTPConnection(serverUrl)
connection.request("POST", '/services/search/FindingService/v1', xmlparameters)
As you can see in the CURL example, it does not matter that I do not send the API keys etc., it should return an XML error response anyway and not only HTTP status code "500 Internal server error".
Does anyone see what I am doing wrong with my POST request?
[EDIT]
btw using the URL ValueName API works perfectly with Python, but that’s just a GET request on a URL. Yet, I’d prefer to use the XML API. However, if that’s not possible I’d switch to ValueName URIs of course.

It's returning a 500 status and the xml response:
>>> connection.request("POST", '/services/search/FindingService/v1', xmlparameters)
>>> resp = connection.getresponse()
>>> resp.status
<<< 500
>>> resp.read()
<<< '<?xml version=\'1.0\' encoding=\'UTF-8\'?><ms:errorMessage xmlns:ms="http://www.ebay.com/marketplace/services" xmlns="http://www.ebay.com/marketplace/search/v1/services"><error><errorId>2038</errorId><domain>CoreRuntime</domain><severity>Error</severity><category>System</category><message>Missing SOA operation name header</message><subdomain>System</subdomain></error></ms:errorMessage>'

The 500 response status is rather generic and should come back no matter what error is being thrown on the server. Would you consider using the CGI trackback to return the error message?
http://docs.python.org/library/cgitb.html#module-cgitb

Related

requests.exceptions.HTTPError: 401 Client Error atlassian-python-api

I am trying to connect to a Confluence page using the python wrapper on the API (as I am not familiar with any of this) but I keep getting the following error:
requests.exceptions.HTTPError: 401 Client Error
I know that people talk about this being caused by the necessity of using an API token but the page runs on an old version of Confluence and I have been told that we cannot use access tokens.
So has anyone any other idea? Here's a small code:
from atlassian import Confluence
confluence = Confluence(
url='https://address',
username='name',
password='pwd'
)
confluence.create_page(
space='Test',
title='A title',
body='something')
I have tried to use an older version of atlassian-python-api just in case there was some conflict but it got me the same error.
Your code looks ok. Authenticating to Confluence using Basic Auth should work without generating an API token, afaik.
The 401 status definitely suggests a problem with the authentication though. The obvious reason for this would be of course wrong credentials, but I assume that you have double checked that the credentials work when interactively logging into confluence with a browser.
To get a better sense of the error, you can import logging to debug your requests and response:
from atlassian import Confluence
import logging
logging.basicConfig(filename='conf_connect.log', filemode='w', level=logging.DEBUG)
try:
c = Confluence(url='https://conf.yoursystem.com', username='name', password='pwd')
# atlassian API does not raise error on init if credentials are wrong, this only happens on the first request
c.get_user_details_by_username('name')
except Exception as e:
logging.error(e)
The Confluence module internally also uses logging, so the requests and responses will appear in your conf_connect.log logfile:
DEBUG:atlassian.rest_client:curl --silent -X GET -H 'Content-Type: application/json' -H 'Accept: application/json' 'https://conf.yoursystem.com/rest/api/user?username=name'
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): conf.yoursystem.com:443
DEBUG:urllib3.connectionpool:https://conf.yoursystem.com:443 "GET /rest/api/user?username=name HTTP/1.1" 401 751
DEBUG:atlassian.rest_client:HTTP: GET rest/api/user -> 401
DEBUG:atlassian.rest_client:HTTP: Response text -> <!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Basic Authentication Failure - Reason : AUTHENTICATED_FAILED</p><p><b>Description</b> The request has not been applied because it lacks valid authentication credentials for the target resource.</p><hr class="line" /><h3>Apache Tomcat/9.0.33</h3></body></html>
ERROR:root:401 Client Error: for url: https://conf.yoursystem.com/rest/api/user?username=name
The response body may include some information on the reason:
HTTP Status 401 – UnauthorizedType Status ReportMessage Basic Authentication Failure - Reason : AUTHENTICATED_FAILEDDescription The request has not been applied because it lacks valid authentication credentials for the target resource.
The reason AUTHENTICATED_FAILED suggests something is likely wrong with your credentials. If you want to dig deeper into that, you can use this SO answer to also display the headers that are being sent with your request.
However, if your reason is AUTHENTICATION_DENIED the problem is likely the following: If you have too many failed authentication attempts in a row, a CAPTCHA challenge is triggered, and this error will occur until the Failed Login Count is reset. This can easily happen when you are developing a script and test it frequently. To remedy this, either open a browser and manually (re-)logon to Confluence, completing the CAPTCHA, or resolve it from the Confluence User Management.

Sending a raw XML request to a SOAP service with Zeep (trying to duplicate an argument)

I am able to send a simple SOAP request with Zeep.
with client.settings(strict=False):
resp = client.service.demandeFicheProduit(
demandeur=self.xxx, motDePasse=self.yyy,
ean13s="foo",
multiple=False)
However, I need to give multiple times the ean13s argument, which is not possible in a Python function call, so I figured I need to build the XML myself.
With Zeep's debug on, I see that the XML sent is this:
<?xml version='1.0' encoding='utf-8'?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:demandeFicheProduit xmlns:ns0="http://fel.ws.accelya.com/">
<demandeur>xxx
</demandeur>
<motDePasse>yyy
</motDePasse>
<ean13s>foo
</ean13s>
<multiple>false
</multiple>
</ns0:demandeFicheProduit>
</soap-env:Body>
</soap-env:Envelope>
So I only need to replicate the
<ean13s>foo
</ean13s>
part.
Looking into Zeep, I see a Transport.post_xml method: https://github.com/mvantellingen/python-zeep/blob/da8a88b9f5/src/zeep/transports.py#L86 which takes an lxml tree as parameter. (doc)
def post_xml(self, address, envelope, headers):
"""Post the envelope xml element to the given address with the headers.
This method is intended to be overriden if you want to customize the
serialization of the xml element. By default the body is formatted
and encoded as utf-8. See ``zeep.wsdl.utils.etree_to_string``.
"""
message = etree_to_string(envelope)
return self.post(address, message, headers)
I tried a post_raw_xml method, without etree_to_string:
def post_raw_xml(self, address, raw_envelope, headers):
return self.post(address, raw_envelope, headers)
I call it with the above XML
transport = zeep.Transport()
transport.post_raw_xml("adress", my_xml, {}) # {}: headers?
and the response status is OK (200), however the service answers it is an invalid request.
Are there XML / SOAP intricacies I didn't pay attention to? Encoding? Headers? (here {})
edit: after spying a bit more the Zeep internals, the headers it sends are
{'SOAPAction': '""', 'Content-Type': 'text/xml; charset=utf-8'}
So I thought I could simply use requests.post, to no avail yet. To use requests, see Sending SOAP request using Python Requests
How to otherwise build an XML manually?
Any more idea on how I can duplicate the eans13 argument?
Thank you.
I solved my problem without Zeep.
I send a raw XML string with requests, I don't construct an XML manually with lxml or xsd.
What helped me was print-debugging the internals of Zeep's transport.py/post method (so the headers are {'SOAPAction': '""', 'Content-Type': 'text/xml; charset=utf-8'}
),
this other question: Sending SOAP request using Python Requests to send a post request:
requests.post(url, data=xml, headers=headers)
and being careful on the XML string (yes there is a newline character after the closing <?xml> tag).
The downside is to parse the XML back.

python post request throwing '400 Bad Request'

I am trying to execute below script where I try to send POST request. I have replaced some values in headers part in order to post it here. The issue I have is related to body of my request which I read from xmlFile.xml. File is in the same directory as my script. XML is written in one line and begins with following line:
<?xml version="1.0"?>
Could you please help? I cannot understand why it is returning 400 Bad Request. XML separately is working fine, but not from within the py script.
#!/usr/bin/python
import httplib
def do_request(xmlFile):
request = open(xmlFile, "r").read()
conn = httplib.HTTPConnection("ipAddress", port)
conn.putrequest("POST", "selector HTTP/1.1")
conn.putheader("Content-Length", "%d" % len(request))
conn.putheader("Content-Type", "text/xml")
conn.putheader("Host", "ipAddress")
conn.putheader("User-Agent", "userAgent")
conn.endheaders()
conn.send(request)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data)
conn.close()
do_request('xmlFile.xml')
IIRC the second argument to putrequest() should be the path part of the url (/ for the root). But you're making it much more complicated than it has to be - you could use conn.request(method, path, params, headers) as showed here, or (even better) just use python-requests actually (even the official httplib doc recommend it).

cURL XML POST works but not Python Requests - 3rd party Expert Rating API (w/ examples)

I'm trying to make a POST request that sends XML data to a 3rd party server, it works with cmd line curl but not the python requests library (which I need to work). I've omitted out the username, password, and domain urls. I'm using requests==2.2.1
CURL EXAMPLE (WORKS):
Request:
$ curl --data '<?xml version="1.0" encoding="UTF-8"?><request><authentication partnerid="12345" password="mypass1234" /><method name="GetTestList" /></request>' 'http://dev.expertrating.com/mydomain/webservices/'
Response:
<response><result name="GetTestList"><records><record test_id="6683" test_name="Demo Adobe Photoshop CS3 Test" coverage="Layers ;Type ;Automating Tasks and Keyboard Shortcuts ;Workspace ;Working with Images, Retouching and Transforming ;Color Management ;Color and Tonal Adjustments ;Using Selection and Drawing ;Filters and Saving Images ;Working With Web, Video and Animation" total_questions="10" duration="10" passing_marks="60" category="Demo Tests" /><record test_id="6684" test_name="Demo Programming with C++ Test" coverage="Classes ;Constructors ;Variables and Datatypes" total_questions="10" duration="10" passing_marks="60" category="Demo Tests" /><record test_id="6685" test_name="Demo HTML 4.01 Test" coverage="Advanced Tags ;Fundamentals ;Tags ;Tables ;Links and Images ;Forms and Frames" total_questions="10" duration="10" passing_marks="60" category="Demo Tests" /><record test_id="6682" test_name="Demo Editing Skills Certification" coverage="Prepositions ;Parallel Construction ;Comma Usage ;Punctuation ;Misplaced Modifiers ;Apostrophe Usage ;Sentence Structure ;Pronoun Usage ;Tenses ;Article Usage" total_questions="10" duration="10" passing_marks="60" category="Demo Tests" /></records></result></response>
REQUESTS EXAMPLE: (FAILS) (via ipython/django)
IPython:
In [1]: import requests
In [2]: payload = '<?xml version="1.0" encoding="UTF-8"?><request><authentication partnerid="12345" password="mypass1234" /><method name="GetTestList" /></request>'
In [3]: r = requests.post('http://dev.expertrating.com/mydomain/webservices/', data=payload, headers={'Content-Type': 'application/xml'})
In [4]: r.text
Out[4]: u"<?xml version='1.0' encoding='UTF-8'?><response><error id='101' message='Invalid Request'><info>You have not posted a valid request.</info></error></response>"
Am I encoding the requests string wrong? Including the wrong headers? I literally copy/pasted the urls and payload xml. I also tried POSTing with urllib2 with similar failing results.
Any help will be greatly appreciated, thanks in advance to anyone who reads this.
It worked by setting the Content-Type header to Content-Type: application/x-www-form-urlencoded instead of Content-Type: application/xml.

Python: Making a request with suds

i'm testing out SUDS library and I'm trying to make a simple request to an endpoint but i get unusual output. Why?
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
client = Client(url)
print client
Output:
Martynass-MacBook-Air:CH martynas$ python ch.py
DEBUG:suds.xsd.schema:loaded:
schema collection
Schema:0x109a7db90
(raw)
<schema/>
(model)
DEBUG:suds.xsd.schema:MERGED:
Schema:0x109a7db90
(raw)
<schema/>
(model)
You can't use suds for this servce, suds is based on SOAP, which is another web service protocol. What you can do is send an xml request and get a response.
import requests
target_url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
headers={'Content-type': 'text/xml'}
print requests.post(target_url, data=xml, headers=headers).text
Where the xml is defined according to their schemes.
http://xmlgw.companieshouse.gov.uk/example_http.html
This is one examaple
xml = ('''
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
xmlns:gt="http://www.govtalk.gov.uk/schemas/govtalk/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader">
<EnvelopeVersion>1.0</EnvelopeVersion>
<Header>
<MessageDetails>
<Class>CompanyDetails</Class>
<Qualifier>request</Qualifier>
<TransactionID>14456553</TransactionID>
</MessageDetails>
<SenderDetails>
<IDAuthentication>
<SenderID>My_SenderID</SenderID>
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
<EmailAddress>sometest#some.email.address</EmailAddress>
</SenderDetails>
</Header>
<GovTalkDetails>
<Keys/>
</GovTalkDetails>
<Body>
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
</Body>
</GovTalkMessage>
''')
<Class>CompanyDetails</Class> What type of info you're getting. kinda what "function" to call
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
Here you would put the login info i guess
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
The "function" call and it's parameters
Now this will give me a response telling me the authorization failed. So if you have an account there, this should work for you.
Here you can find the list of schemes they have for different types of request. Some of them have sample request to help you out.
http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/SchemaStatusOutput
Here is the complete guide of all their schemes.
http://xmlgw.companieshouse.gov.uk/data_usage_guide_dec_2013.pdf
There aren't any wsdl definitions for that site. Try something like
http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
for your url
then you can try something like
client.service.GetWeatherInformation()
From the suds document, "You will need to know the url for WSDL for each service used."
An explicit example
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = " http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"
client = Client(url)
client.service.GetWeatherInformation()
Outputs a ton of data.
Suds does not make it easy to discover the service, it's better to first test a bit with soapui or generate a human-readable doc of the wsdl with this xslt : http://code.google.com/p/wsdl-viewer/ . So you know the structure of requests and replies, and which services are available.
Requests and responses in soap are xml trees, so once you get the result, you need to access the content of the xml tag that contains the information you're interested in. Here is an example that should work ( I don't have a username, but the result.Status.Success works ).
import suds
client = suds.client.Client("http://webservices.data-8.co.uk/companieshouse.asmx?WSDL")
result = client.service.GetCompanyDetails("username", "password", 1234)
print result.Status.Success
print result.Result.CompanyName
You can not make a request against .xsd. XSD is definition of the exchanged message. You must make a request against webservice Looking here you can find more info about that web service. But also there is pricing page indicating that you must pay to use their service. Probably when you pay you will get username and password to authenticate with the service.

Categories

Resources