Erros que um programa lê
application/problem+json (RFC 9457): o tipo, o título, o detalhe — e por que texto de erro não é contrato.
Todo cliente de uma API acaba precisando decidir o que fazer com um erro: tentar de novo, pedir outro dado, mostrar uma mensagem. Se o erro é só um texto — {"erro": "saldo insuficiente"} —, o cliente decide comparando texto, e a primeira revisão de ortografia no servidor quebra todos eles.
A RFC 9457 resolve isso com cinco campos, e Kiln.problema os monta:
| Campo | O que é | Quem lê |
|---|---|---|
type | uma URI que identifica o tipo do problema | o programa: é por ele que se decide |
title | o resumo do tipo, igual em toda ocorrência | a pessoa, num log |
status | o mesmo código da resposta HTTP | quem só tem o corpo em mãos |
detail | o que aconteceu desta vez | a pessoa, na tela |
instance | qual pedido falhou | o suporte, cruzando com o log |
dataforge
adopt Arcane.Kiln as Kiln
saldos := {"ana": 30}
app := Kiln.app()
action comprar(req):
quem := req["params"]["quem"]
preco := req["body"]["preco"]
given (saldos[quem] ?? void) is void:
yield Kiln.problema(404, "Conta não encontrada", $"não há conta '{quem}'")
given preco bigger saldos[quem]:
yield Kiln.problema(422, "Saldo insuficiente",
$"o saldo é {saldos[quem]} e a compra custa {preco}",
"https://loja.exemplo/erros/saldo-insuficiente",
{"saldo": saldos[quem], "preco": preco})
saldos[quem] -= preco
yield Kiln.json({"saldo": saldos[quem]})
Kiln.post(app, "/contas/:quem/compras", comprar)
r := Kiln.test(app, "POST", "/contas/ana/compras", {"preco": 50})
assert r["status"] is 422
assert r["body"]["type"] is "https://loja.exemplo/erros/saldo-insuficiente"
assert r["body"]["saldo"] is 30
assert Kiln.test(app, "POST", "/contas/bia/compras", {"preco": 1})["status"] is 404Três decisões que a peça cobra#
- Problema é erro.
Kiln.problema(200, …)é recusado: um corpo de problema num 200 faz o cliente que olha o status seguir adiante com um erro na mão. - Os campos da RFC não vêm em `extras`. Um
extrascomstatussobrescreveria o status real no corpo e deixaria corpo e cabeçalho dizendo coisas diferentes. - `about:blank` é o tipo padrão, e ele quer dizer "o status HTTP já diz tudo". Assim que um cliente precisar distinguir dois 422, dê a cada um o seu
type.