I am following the blog: Build your own Python RESTful Web Service to mock a web server using cherrypy.
The server code is
import cherrypy
class MyWebService(object):
@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
def process(self):
return "hello world!"
if __name__ == '__main__':
config = {'server.socket_host': '0.0.0.0'}
cherrypy.config.update(config)
cherrypy.quickstart(MyWebService())
running above script by python server.py
will start a service at http://localhost:8080
.
Successful Call
Then we can call the service using post method:
import requests
headers = {'Content-Type': 'application/json'}
response = requests.post('http://localhost:8080/process', headers=headers, json={})
It successfully returns "hello world!" with status= 200.
Failed Call
However, if changing "Content-Type": application/json -> text/plain in headers and json -> data:
headers = {'Content-Type': 'text/plain'}
response = requests.post('http://localhost:8080/process', headers=headers, data={})
It responds the error code 415, and the error message
Traceback (most recent call last):
File "/Users/hshung/opt/anaconda3/lib/python3.9/site-packages/requests/models.py", line 910, in json
return complexjson.loads(self.text, **kwargs)
File "/Users/hshung/opt/anaconda3/lib/python3.9/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/Users/hshung/opt/anaconda3/lib/python3.9/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/Users/hshung/opt/anaconda3/lib/python3.9/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
.....
I did try the following server code
@cherrypy.tools.json_in(content_type=['application/json', 'text/plain'])
The API call still fails, and the status code is 400.
Can anyone who is familiar with cherryPy and API request help me figure out how to fix it?
The problem is that with:
headers = {'Content-Type': 'text/plain'}
response = requests.post('http://localhost:8080/process', headers=headers, data={})
You're sending a POST
request with an empty body, it should work if you specify the data
param as a string containing any valid json:
headers = {'Content-Type': 'text/plain'}
response = requests.post('http://localhost:8080/process', headers=headers, data='{}')
For reference take a look a the values that are sent:
>>> requests.post('http://localhost:8080/process', headers=headers, json={})
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8080
send: b'POST /process HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: python-requests/2.27.1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\n'
send: b'{}'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/json
header: Server: CherryPy/18.6.1
header: Date: Fri, 22 Jul 2022 23:16:49 GMT
header: Content-Length: 14
DEBUG:urllib3.connectionpool:http://localhost:8080 "POST /process HTTP/1.1" 200 14
<Response [200]>
>>> requests.post('http://localhost:8080/process', headers=headers, data={})
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8080
send: b'POST /process HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: python-requests/2.27.1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 0\r\n\r\n'
reply: 'HTTP/1.1 400 Bad Request\r\n'
header: Content-Type: text/html;charset=utf-8
header: Server: CherryPy/18.6.1
header: Date: Fri, 22 Jul 2022 23:16:55 GMT
header: Content-Length: 3023
DEBUG:urllib3.connectionpool:http://localhost:8080 "POST /process HTTP/1.1" 400 3023
<Response [400]>
>>> requests.post('http://localhost:8080/process', headers=headers, data='{}')
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8080
send: b'POST /process HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: python-requests/2.27.1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 2\r\n\r\n'
send: b'{}'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/json
header: Server: CherryPy/18.6.1
header: Date: Fri, 22 Jul 2022 23:17:00 GMT
header: Content-Length: 14
DEBUG:urllib3.connectionpool:http://localhost:8080 "POST /process HTTP/1.1" 200 14
<Response [200]>
>>>
And you're correct with:
@cherrypy.tools.json_in(content_type=['application/json', 'text/plain'])
That is also required.