2
\$\begingroup\$

I just wrote an echo server that responds to curl requests with the method used and the data sent. Sticking to GET and POST for now, but I would like to know if there's something I can do to improve my RESTful API.

I'm supposed to:

  1. Only be able to call the /data endpoint
  2. Only allow JSON parameters
  3. Use best practices of coding a RESTful API

Expected JSON response:

{
  "method": <HTTP METHOD REQUESTED>,
  "data": <HTTP PARAMETERS SENT IN THE REQUEST>
}

Here is my current code:

from bottle import run, post, request, response, get, route, put, delete
from json import dumps
import json

@get('/data')
def data():
    #if headers are not specified we can check if body is a json the following way
    #postdata = request.body.read()
    postdata = request.json
    try:
        #rv = {"method": request.method, "data": json.loads(postdata.decode('utf-8'))}
        rv = {"method": request.method, "data": postdata}
    except:
        raise ValueError
    response.content_type = 'application/json'
    return dumps(rv)

@post('/data')
def data():
    #postdata = request.body.read()
    postdata = request.json
    try:
        #rv = {"method": request.method, "data": json.loads(postdata.decode('utf-8'))}
        rv = {"method": request.method, "data": postdata}
    except:
        raise ValueError
    response.content_type = 'application/json'
    return dumps(rv)

Everything seems to work fine for now. I'm looking to improve my code so any feedback is appreciated.

\$\endgroup\$
1
\$\begingroup\$

The code looks a bit incomplete, e.g. shouldn't there be a main method somewhere?

I added the following to get it to run for now:

if __name__ == "__main__":
    run()

It's good practice to have the __name__ check in there instead of just executing, because then you're still able to import the file elsewhere without it automatically executing code.


Okay, so with that out of the way, there's commented out code that should be there: Either it's correct, then it should replace the uncommented code, or it's not, then it should be deleted. To me, reading that just confuses me what the purpose is, or was.

And then there's two functions, which are identical ... why is that? I suppose that's a feature of the framework that allows you to have both functions because of the annotations, but generally do not do that and give them separate names, at least something like data_get/data_post.

The specification is pretty vague, what are you supposed to echo, the POST body, or the URL parameters? Because then I don't get what the GET one is supposed to be doing. With that in mind I'd delete it.

So, then, trying it out:

> curl -d '{"data": 1}' -H "Content-Type: application/json" -v http://localhost:8080/data
{"method": "POST", "data": {"data": 1}}%

Seems to work.

(Also, since there's a single endpoint that echoes data ... there's little REST here. I'm not sure what other best practices are meant to be followed for this exercise.)

I've no idea how you'd get an exception in there, but in any case, raise ValueError isn't super helpful, it says exactly nothing about what went wrong. Consider adding some information like raise ValueError("Couldn't format data."). Note that in this particular situation there's little that adds information, maybe don't catch the exception then.

At that point it might look like this:

@post("/data")
def data():
    response.content_type = "application/json"
    return dumps({"method": request.method, "data": request.json})

However, then I was thinking, maybe this framework does the JSON formatting for us. And it does:

@post("/data")
def data():
    return {"method": request.method, "data": request.json}

Probably can't get it much smaller than that actually. And then you can delete a lot of imports too:

from bottle import run, request, post
from json import dumps

Hope that helps. In general it's good to let readers know all you can, like "which dependencies do I need" and "how do I run this", including example invocations.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.