px普贤教程PuXian
普贤教程 › 生态包

Web 与 HTTP

四个「写服务端」用得最多的包:template 渲染、mock 起桩服务、cookiejar 管会话、webroute 匹配路径。

template:一个文件就能出页面

px
import template

var tpl = "你好 {{name}}!{{if vip}}VIP{{else}}普通{{end}} {{each items as it, i}}[{{i}}:{{it}}]{{end}}"
print(tmpl_render(tpl, {"name": "张三", "vip": true, "items": ["a", "b"]}).ok())
text
你好 张三!VIP [0:a][1:b]
语法说明
{{key}} / {{a.b.c}}变量替换,支持点路径
{{if key}}…{{else}}…{{end}}条件;0.2.0 支持 > < >= <= == !=
{{each list as x}} / {{each list as x, i}}循环,带索引
{{v | upper}} 等过滤器(upper/lower/trim/len)
模板里不做业务计算过滤器只有四个。要格式化金额、日期,先在数据里算好再传进来——模板引擎不该背这个责任,也背不动。

mock:测试用桩服务

mock 用 tcp_listen 起一个本地 HTTP 服务,可以断言「我方发出的请求长什么样」。写客户端集成测试时比真起一个后端省事:

px
import mock

var routes = [mk_route("GET", "/health", 200, "ok")]
var reqlog = []
var srv = mk_start_server(18080, routes, reqlog)   # 绑定成功才返回
# … 用 http_request 打 127.0.0.1:18080 …
print(len(reqlog))                                 # 收到的请求都记在 reqlog

# 也可以直接当客户端用
print(mk_request(18080, "GET", "/health"))
端口别写常用值测试用 18080 / 18081 这类高位端口;而且 mk_start_server 是「绑定成功才返回」,端口被占会失败——并行跑测试时按 worker 号加偏移。

cookiejar:把 Set-Cookie 管起来

px
import cookiejar

var jar = cj_new()
cj_update(jar, {"set-cookie": "sid=abc; Path=/; HttpOnly"})
print(cj_header(jar, "https://example.com/api"))   # Cookie: sid=abc
print(cj_len(jar))                                 # 1

做「登录 → 拿 cookie → 带着 cookie 访问」这类脚本时用它,别自己拼字符串:Path、Expires、域匹配与大小写都得处理,这些函数里都已经封好(cj_domain_match / cj_path_match / cj_url_secure)。

webroute:路径匹配

自己写路由表时用 webroute,支持静态段、参数段与通配。在 Ma 上通常不需要它——Ma 的 px_routes 已经做了 exact / prefix / regex / dir 四种匹配,路由该写在配置里,不是写在脚本里。

场景用什么
Ma 托管的接口配置里的 px_routes
自建 HTTP 服务mock 的服务器骨架 + webroute
命令行里判断子命令cli(见 文本页)

写一个最小的接口(不依赖框架)

px
def json_resp(code, body):
    print("Status: " + str(code))
    print("Content-Type: application/json; charset=utf-8")
    print("")
    print(body)

def main():
    var method = env("REQUEST_METHOD")
    if method != "POST":
        json_resp(405, "{\"ok\":false,\"error\":\"method not allowed\"}")
        return
    var raw = read_file("/dev/stdin")          # 请求体从 stdin
    var body = json_parse_opt(raw)
    if body.is_err():
        json_resp(400, "{\"ok\":false,\"error\":\"bad json\"}")
        return
    json_resp(200, "{\"ok\":true,\"echo\":" + json_stringify(body.ok()) + "}")
三个必须知道的 Ma + px 约束① 响应头用 print,空行之后才是 body;② 处理中间不要 print 日志——print 写的是 stdout,会被当成响应体;日志要 open("/dev/stderr","w") 写进去;③ Content-Length 由 Ma 决定,脚本写了会被忽略。