看一眼 JSON 出口
(每道题开头都有同一段:上面那个应用的全部源码——render 把标题和正文塞进页面骨架 PAGE,list_html / form_html 生成列表和表单,STATIC 是静态文件表,handle 是入口。判题机不联网,题里直接调 handle。)
贯穿全条的应用(留言板):
handle(method, url, body, notes) → (状态码, 头, 正文)
/ GET 200 首页:留言列表 + 发表表单
/about GET 200 关于页
/notes/<n> GET 200 第 n 条的详情;越界 404
/api/notes GET 200 application/json {"count": n, "notes": [...]}
/add POST 303 Location: /(正文 text 追加进 notes);空文本 400;GET 405
/static/... GET 文件(只有 style.css);没有 404
其余 404调 /api/notes,把正文 loads 回来看:
import html
import json
from urllib.parse import urlparse, parse_qs
PAGE = "<!doctype html><html><head><meta charset=\"utf-8\"><title>{title}</title><link rel=\"stylesheet\" href=\"/static/style.css\"></head><body><h1>{title}</h1>{body}</body></html>"
def render(title, body):
return PAGE.replace("{title}", html.escape(title)).replace("{body}", body)
def list_html(notes):
if not notes:
return "<p>还没有留言</p>"
return "<ul>" + "".join("<li>" + html.escape(n) + "</li>" for n in notes) + "</ul>"
def form_html():
return "<form method=\"post\" action=\"/add\"><input name=\"text\"><button>发表</button></form>"
STATIC = {"/static/style.css": ("text/css", "h1 { color: #2563eb; }\n")}
def handle(method, url, body="", notes=None):
notes = [] if notes is None else notes
u = urlparse(url)
path = u.path
if path in STATIC:
if method != "GET":
return 405, {"Content-Type": "text/html"}, "<p>只能 GET</p>"
ctype, content = STATIC[path]
return 200, {"Content-Type": ctype}, content
if path.startswith("/static/"):
return 404, {"Content-Type": "text/html"}, "<p>没有这个文件</p>"
if path == "/":
return 200, {"Content-Type": "text/html"}, render("留言板", list_html(notes) + form_html())
if path == "/about":
return 200, {"Content-Type": "text/html"}, render("关于", "<p>一个用标准库写的留言板。</p>")
if path.startswith("/notes/"):
tail = path[len("/notes/"):]
if not tail.isdigit() or not 1 <= int(tail) <= len(notes):
return 404, {"Content-Type": "text/html"}, render("没有这条", "<p>没有这条留言</p>")
return 200, {"Content-Type": "text/html"}, render("第 " + tail + " 条", "<p>" + html.escape(notes[int(tail) - 1]) + "</p>")
if path == "/api/notes":
return 200, {"Content-Type": "application/json"}, json.dumps({"count": len(notes), "notes": notes}, ensure_ascii=False)
if path == "/add":
if method != "POST":
return 405, {"Content-Type": "text/html"}, "<p>请用表单提交</p>"
text = parse_qs(body).get("text", [""])[0].strip()
if not text:
return 400, {"Content-Type": "text/html"}, "<p>留言不能为空</p>"
notes.append(text)
return 303, {"Content-Type": "text/html", "Location": "/"}, ""
return 404, {"Content-Type": "text/html"}, render("找不到", "<p>没有这个页面</p>")
SAMPLE = ["第一条留言", "今天天气不错", "<b>不许</b>"]
s, h, b = handle("GET", "/api/notes", "", list(SAMPLE))
data = json.loads(b)
print(str(s) + "/" + h["Content-Type"] + "/" + str(data["count"]) + "/" + data["notes"][1] + "/" + str("\\u" in b))
全部评论