Skip to content

Commit e0ae4b8

Browse files
committed
refactor: leverage 'pydantic'
1 parent aad7852 commit e0ae4b8

File tree

1 file changed

+75
-58
lines changed

1 file changed

+75
-58
lines changed

playground/client_server/rest_api.py

+75-58
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,110 @@
11
import json
22
from http.server import BaseHTTPRequestHandler, HTTPServer
3-
from typing import Any
4-
5-
Book = dict[str, Any]
6-
books: list[Book] = [
7-
{"id": 1, "title": "The Great Gatsby", "author": "F. Scott Fitzgerald"},
8-
{"id": 2, "title": "To Kill a Mockingbird", "author": "Harper Lee"},
9-
{"id": 3, "title": "1984", "author": "George Orwell"},
10-
{"id": 4, "title": "The Catcher in the Rye", "author": "J.D. Salinger"},
11-
{
12-
"id": 5,
13-
"title": "Foley Is Good: And the Real World Is Faker Than Wrestling",
14-
"author": "Mick Foley",
15-
},
16-
{
17-
"id": 6,
18-
"title": "Hitman: My Real Life in the Cartoon World of Wrestling",
19-
"author": "Bret Hart",
20-
},
21-
{
22-
"id": 7,
23-
"title": "Undisputed: How to Become World Champion in 1,372 Easy Steps",
24-
"author": "Chris Jericho",
25-
},
26-
{
27-
"id": 8,
28-
"title": "Have A Nice Day: A Tale of Blood and Sweatsocks",
29-
"author": "Mick Foley",
30-
},
31-
]
32-
33-
34-
class RequestHandler(BaseHTTPRequestHandler):
3+
4+
from pydantic import BaseModel
5+
6+
# Constants
7+
BOOKS_ENDPOINT = "/books"
8+
CONTENT_TYPE = "Content-type"
9+
JSON_CONTENT = "application/json"
10+
11+
12+
class Book(BaseModel):
13+
id: int
14+
title: str
15+
author: str
16+
17+
18+
class BookList(BaseModel):
19+
books: list[Book]
20+
21+
22+
books = BookList(
23+
books=[
24+
Book(id=1, title="The Great Gatsby", author="F. Scott Fitzgerald"),
25+
Book(id=2, title="To Kill a Mockingbird", author="Harper Lee"),
26+
Book(id=3, title="1984", author="George Orwell"),
27+
Book(id=4, title="The Catcher in the Rye", author="J.D. Salinger"),
28+
Book(
29+
id=5,
30+
title="Foley Is Good: And the Real World Is Faker Than Wrestling",
31+
author="Mick Foley",
32+
),
33+
Book(
34+
id=6,
35+
title="Hitman: My Real Life in the Cartoon World of Wrestling",
36+
author="Bret Hart",
37+
),
38+
Book(
39+
id=7,
40+
title="Undisputed: How to Become World Champion in 1,372 Easy Steps",
41+
author="Chris Jericho",
42+
),
43+
Book(
44+
id=8,
45+
title="Have A Nice Day: A Tale of Blood and Sweatsocks",
46+
author="Mick Foley",
47+
),
48+
]
49+
)
50+
51+
52+
class BooksRequestHandler(BaseHTTPRequestHandler):
3553
def _handle_get_books(self) -> None:
54+
"""Handle GET request for books."""
3655
self._set_response()
37-
self.wfile.write(json.dumps(books).encode("utf-8"))
56+
self.wfile.write(books.model_dump_json().encode("utf-8"))
3857

3958
def _handle_post_book(self) -> None:
59+
"""Handle POST request to add a new book."""
4060
content_length = int(self.headers["Content-Length"])
4161
if content_length == 0:
42-
self._set_response(400)
43-
self.wfile.write(json.dumps({"error": "No data provided"}).encode("utf-8"))
62+
self._send_error_response(400, "No data provided")
4463
return
4564
post_data = self.rfile.read(content_length)
4665
try:
47-
new_book: Book = json.loads(post_data)
48-
if (
49-
"id" not in new_book
50-
or "title" not in new_book
51-
or "author" not in new_book
52-
):
53-
raise ValueError("Missing fields")
54-
books.append(new_book)
66+
new_book = Book.model_validate_json(post_data)
67+
books.books.append(new_book)
5568
self._set_response(201)
56-
self.wfile.write(json.dumps(new_book).encode("utf-8"))
57-
except (json.JSONDecodeError, ValueError) as e:
58-
self._set_response(400)
59-
self.wfile.write(
60-
json.dumps({"error": "Invalid data", "message": str(e)}).encode("utf-8")
61-
)
69+
self.wfile.write(new_book.model_dump_json().encode("utf-8"))
70+
except ValueError as e:
71+
self._send_error_response(400, f"Invalid data: {str(e)}")
6272

6373
def _set_response(self, status_code: int = 200) -> None:
74+
"""Set the response headers."""
6475
self.send_response(status_code)
65-
self.send_header("Content-type", "application/json")
76+
self.send_header(CONTENT_TYPE, JSON_CONTENT)
6677
self.end_headers()
6778

79+
def _send_error_response(self, status_code: int, message: str) -> None:
80+
"""Send an error response."""
81+
self._set_response(status_code)
82+
self.wfile.write(json.dumps({"error": message}).encode("utf-8"))
83+
6884
def do_GET(self) -> None:
69-
if self.path == "/books":
85+
"""Handle GET requests."""
86+
if self.path == BOOKS_ENDPOINT:
7087
self._handle_get_books()
7188
else:
72-
self._set_response(404)
73-
self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8"))
89+
self._send_error_response(404, "Not Found")
7490

7591
def do_POST(self) -> None:
76-
if self.path == "/books":
92+
"""Handle POST requests."""
93+
if self.path == BOOKS_ENDPOINT:
7794
self._handle_post_book()
7895
else:
79-
self._set_response(404)
80-
self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8"))
96+
self._send_error_response(404, "Not Found")
8197

8298

8399
def run(
84100
server_class: type[HTTPServer] = HTTPServer,
85-
handler_class: type[BaseHTTPRequestHandler] = RequestHandler,
101+
handler_class: type[BaseHTTPRequestHandler] = BooksRequestHandler,
86102
port: int = 8000,
87103
) -> None:
104+
"""Run the HTTP server."""
88105
server_address = ("", port)
89106
httpd = server_class(server_address, handler_class)
90-
print(f"Starting server on port {port}...")
107+
print(f"Starting books server on port {port}...")
91108
httpd.serve_forever()
92109

93110

0 commit comments

Comments
 (0)