Write a web service to provide information about members of an association

Presentation

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:

Server code

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.

Hello World

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:
Screenshot of the web browser requesting localhost:5000

/members

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.

Screenshot of the web browser requesting localhost:5000/members

/members/{ln}

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".

Screenshot of the web browser requesting localhost:5000/members/A07
Screenshot of the web browser requesting localhost:5000/members/XYZ
If you open the Firefox application menu, "More tools", "Web Developer Tools", you can display the HTTP response status code :
Screenshot of the developper console of the web browser requesting localhost:5000/member/XYZ (shows the 404 status)

/membersInRange?minAge=X&maxAge=Y

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).

Screenshot of the web browser requesting localhost:5000/membersInRange&minAge=13&maxAge=40
Screenshot of the web browser requesting localhost:5000/membersInRange&minAge=13&maxAge=23
Screenshot of the web browser requesting localhost:5000/membersInRange&minAge=13&AgeMax=23

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