A server stores information about members of a sport association. The members are elements of a list, and each member is represented by a dictionnary with the following keys: licenseNumber, name, birthYear and gender.
We want the server to provide the following routes:
/members
: returns the list of all the members, formatted in JSON[ { "licenseNumber": "A07", "name": "Alice", "birthYear": 2000, "gender": "F" }, { "licenseNumber": "C11", "name": "Bob", "birthYear": 2001, "gender": "M" } ]
/members/{ln}
: returns information about the member whose licenseNumber is ln/members/A07
{ "licenseNumber": "A07", "name": "Alice", "birthYear": 2000, "gender": "F" }
/members/XYZ000
404 - Not Found
/membersInRange?minAge=X&maxAge=Y
: returns information about the members in the provided age range [X-Y]/membersInRange?minAge=15&maxAge=23
{ "licenseNumber": "C11", "name": "Bob", "birthYear": 2001, "gender": "M" }
We need to build a web server application (a program always on, that can receive HTTP requests and reply with HTTP responses).
The http.server and socketserver modules, part of Python standard library, make it possible to build it from scratch. We will use that solution so you can get a better grasp at the basics before transitioning to frameworks like Flask or Django that will ease your work when building more complex web applications.
Before we implement the expected services, take a look at a basic use of the http.server and socketserver modules. The following code creates a TCPServer that listens for requests on the TCP port number 5000 of the localhost interface. Incoming requests are handled by the MyHandler
class that extends the http.server.SimpleHTTPRequestHandler
class. When a GET request is received, the handler prepares a HTTP response. The status line of the response is the HTTP code 200, which means "OK". The response contains a header that indicates to interpret the content of the message as plain text. And the self.wfile
is an object to which the content (or "body" part of the message) is written using the write
method that expects a bytes-like parameter.
Note: using a context manager such as the with
block guarantees that the server is correctly closed at the end, even in the case of errors or exceptions. An alternative is:
Copy and execute this code. Once the server runs (the program does not return), enter http://localhost:5000 in the address bar of your web browser to send a GET request to your server:
The path
of the SimpleHTTPRequestHandler contains the request path. Modify your server code so that it keeps responding "Hello World!" for the "/
" path, and add the "/members
" path. When this path is requested, transform the members
dictionary into a json object using the json.dumps
method, next encode the resulting string to bytes using the str.encode
method, and write the result to the HTTP response body, also change the Content-type
header to "application/json
". Test both pathes using a web browser.
For this new path, first you need to extract from the url the requested license number. For instance, if the user request the resources "/members/A07", you need to extract from this string the last part, "A07". Hint: You can use the str.split
method to transform "/members/AO7" into ["","members","AO7"].
Next you need to search the members
list to find the member whose license number is the one provided. Once you found the member, return an HTTP reply with code 200, and the associated information formatted as JSON as previously. If there are no member with this license number, return an HTTP reply with code 404 ("not found") and set a plain text error message "unknown license number".
If you open the Firefox application menu, "More tools", "Web Developer Tools", you can display the HTTP response status code :
For that last path, you could proceed as before and parse the url to extract the minimum age and maximum age considered. Instead, explore the methods offered by the urllib.parse
module. In particular, the urlparse
method returns a named-tuple where the parameters are named "query" (ie. "query" is "minAge=13&maxAge=40"). Once the parameters list is extracted, use the parse_qs
method to get a dictionary where the keys are the parameter names and the values are list of values associated with each key. For instance:
{'minAge': ['13'], 'maxAge': ['40']}.
If "minAge" or "maxAge" are missing, return an error message (ie HTTP message with code 4XX).
Note: instead of using a web browser to test your server, you can use the curl utility program:
abc@graoully:~$ curl "http://localhost:5000" Hello World!abc abc@graoully:~$ curl "http://localhost:5000/members" [{"licenseNumber": "A07", "name": "Alice", "birthYear": 2000, "gender": "F"}, {"licenseNumber": "C11", "name": "Bob", "birthYear": 2001, "gender": "M"}] abc@graoully:~$ curl "http://localhost:5000/members/A07" {"licenseNumber": "A07", "name": "Alice", "birthYear": 2000, "gender": "F"} abc@graoully:~$ curl "http://localhost:5000/member/B99" Unknown license number abc@graoully:~$ curl "http://localhost:5000/membersInRange?minAge=19&maxAge=25" [{"licenseNumber": "A07", "name": "Alice", "birthYear": 2000, "gender": "F"}, {"licenseNumber": "C11", "name": "Bob", "birthYear": 2001, "gender": "M"}] abc@graoully:~$ curl "http://localhost:5000/membersInRange?minAge=19&ageMax=25" Bad Request: Missing 'minAge' or 'maxAge' parameter