Negociação de conteúdo
JSON ou CSV no mesmo endereço: o cabeçalho Accept, o q, o curinga — e quando responder 406.
O mesmo recurso pode ter mais de uma representação: a lista de pedidos em JSON para um programa, em CSV para uma planilha. O cliente diz o que aceita no Accept, com um peso q de 0 a 1, e o servidor escolhe. Kiln.negociar faz a escolha como a RFC 9110 manda.
dataforge
adopt Arcane.Kiln as Kiln
pedidos := [{"id": 1, "total": 50}, {"id": 2, "total": 70}]
app := Kiln.app()
action listar(req):
tipo := Kiln.negociar(req, ["application/json", "text/csv"])
given tipo is void:
yield Kiln.problema(406, "Formato não disponível",
"esta rota responde application/json ou text/csv")
given tipo is "text/csv":
linhas := ["id,total"] + [$"{p["id"]},{p["total"]}" cycle p in pedidos]
yield Kiln.text(linhas.join("\n"), 200, {"Content-Type": "text/csv; charset=utf-8", "Vary": "Accept"})
yield Kiln.json(pedidos, 200, {"Vary": "Accept"})
Kiln.get(app, "/pedidos", listar)
assert Kiln.test(app, "GET", "/pedidos")["body"][0]["id"] is 1
csv := Kiln.test(app, "GET", "/pedidos", void, {"Accept": "text/csv"})
assert csv["body"].starts_with("id,total")
assert Kiln.test(app, "GET", "/pedidos", void, {"Accept": "image/png"})["status"] is 406As regras, na ordem em que decidem#
| Accept | Escolhe | Por quê |
|---|---|---|
| (nenhum) | o primeiro oferecido | o cliente não pediu nada |
text/csv;q=0.5, application/json | JSON | q maior vence |
text/* | o primeiro text/… oferecido | curinga de subtipo |
*/*, text/csv;q=0 | nunca CSV | q=0 é recusa explícita, e a faixa mais específica decide |
image/png | void → 406 | nada que você oferece serve |