Misleading 5xx errors: `application/ld+json` bodies are never parsed, and bodied `DELETE /delete` 404s upstream
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 48/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- express, javascript, nodejs
调研方向
先从 app.js 和 rest.js 开始,跟踪 content-type 和 body-parser 对内容类型及 body 的处理行为,然后检查 routes/create.js、routes/query.js 和 routes/delete.js。运行现有的路由测试,并使用提供的 curl 命令复现这两种情况。完成的标准是 ld+json 行为一致且经过测试,同时确认带 body 的 DELETE contract,并且不再报告误导性的 upstream failure。
由索引模型根据 Issue 内容生成。
描述
Summary
While smoke testing the package updates on the 8-24-26-packages branch, two separate 502-producing
defects turned up. Neither is caused by the dependency bumps — both reproduce on main as well. They
are filed together because both were found the same way, but they are independent problems.
The first one is the more serious of the two: every route mishandles application/ld+json, and
/create leaks a raw TypeError to the client as a 500.
Finding 1: application/ld+json passes the content-type gate but is never parsed
rest.js accepts application/ld+json as a valid request content type
(rest.js#L1-L4), so verifyJsonContentType lets the request through
instead of returning 415. But app.js only mounts express.json()
(app.js#L20), whose body-parser type option defaults to
application/json and does not match the +json structured suffix. express.text() on the next
line only claims text/plain.
The result is that no parser claims the body, so req.body is undefined (Express 5 no longer
defaults it to {}), and every route then misbehaves in its own way.
Reproduction
Every request below sends a well-formed JSON body with a valid @id, and differs from a working
request only in the Content-Type header:
curl -X POST -H 'Content-Type: application/ld+json' \
--data '{"@id":"https://devstore.rerum.io/v1/id/000000000000000000000000","type":"Probe"}' \
http://localhost:3002/create
Observed behavior
| Route | Status | Response body |
|---|---|---|
POST /query |
502 | 400: .../query?limit=10&skip=0 - Detected empty JSON object. |
POST /create |
500 | Cannot read properties of undefined (reading 'id') |
PUT /update |
400 | No record id to update! |
PUT /overwrite |
400 | No record id to overwrite! |
DELETE /delete |
400 | No record id to delete! |
The identical body sent as application/json returns 200/201 as expected.
Why each is wrong
/createreturns 500 and leaks an internal error.req.body.idon
routes/create.js#L12 throws aTypeErroragainstundefined. The route's
catchhas noerr.status, so it falls through to 500 and sends the raw V8 message
(Cannot read properties of undefined (reading 'id')) to the client. That is an internal
implementation detail on the wire, and it contradicts our own guidance about returning generic
error messages rather than internal details./queryreturns 502.JSON.stringify(undefined)isundefined, not'{}', so the empty-query
guard on routes/query.js#L16 is bypassed. The request is forwarded upstream
with no body, RERUM 400s, and TinyNode maps that to 502 — reporting an upstream failure for what is
really a client error we should have caught./update,/overwrite,/deletereturn a misleading 400. The status code is defensible, but
the message tells the caller they omitted a record id when they plainly supplied one. That will send
people debugging the wrong thing.
Suggested fix
Either accept ld+json properly or reject it, but stop doing both. Accepting it is the smaller change
and matches what rest.js already advertises:
app.use(express.json({ type: ["application/json", "application/ld+json"] }))
Given this is a JSON-LD store, accepting it seems clearly correct. Worth deciding separately whether
/create should also guard req.body defensively so a missing body can never produce a raw
TypeError again — the same class of bug would resurface for any future unparsed content type.
This is a gap left by #110, which introduced rest.js and the ld+json allowance without a matching
parser change.
Finding 2: bodied DELETE /delete returns 502 because the upstream endpoint does not exist
The legacy bodied-delete handler (routes/delete.js#L27) posts to
${RERUM_API_ADDR}delete. That endpoint does not exist on devstore — it 404s, and TinyNode maps the
404 to a 502.
Reproduction
# create something to delete, then:
curl -X DELETE -H 'Content-Type: application/json' \
--data '{"@id":"https://devstore.rerum.io/v1/id/<id>"}' \
http://localhost:3002/delete
# -> HTTP 502
# 404: https://devstore.rerum.io/v1/api/delete - This page does not exist
Confirmed against RERUM directly, so this is upstream and not a TinyNode routing mistake:
curl -i -X DELETE -H 'Content-Type: application/json' \
--data '{"@id":"..."}' https://devstore.rerum.io/v1/api/delete
# -> HTTP 404
The path form works correctly:
curl -X DELETE http://localhost:3002/delete/<id>
# -> HTTP 204
What to decide
This route cannot succeed against devstore in its current form, so one of the following is needed:
- Confirm whether
/v1/api/deletewith a body is supposed to exist on RERUM. If it is, this is a
RERUM bug and TinyNode is fine. - If it is not coming back, have the bodied handler extract the id from
@idand delegate to the
path form, so the documented legacy call keeps working. - Failing both, deprecate the bodied form and return a 400 that says so, rather than a 502 that
implies an upstream outage.
Related to #89, which covers different odd behavior on the same endpoint (a delete with no id
returning 204). That specific case now correctly returns 400, so #89 may be partly stale.
Environment
- Branch
8-24-26-packages, also reproduces onmain - Node v24.19.0, npm 12.0.2
RERUM_API_ADDR=https://devstore.rerum.io/v1/api/- All 80 route tests and 5 e2e tests pass, so neither defect is currently covered by a test
- 主要语言
- JavaScript
- 星标
- 1
- 派生
- 3
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
CenterForDigitalHumanities/TinyNode 的其他 Issue
-
dependencies enhancement
难度 2/5 1-3 小时 新手友好度 90/100
-
难度 2/5 1-3 小时 新手友好度 78/100
-
copilot
难度 4/5 3-5 天 新手友好度 35/100
-
copilot
难度 5/5 一周以上 新手友好度 35/100
-
难度 3/5 1-2 天 新手友好度 35/100
查看 CenterForDigitalHumanities/TinyNode 的全部 Issue
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 72/100
维护者通常 1 天内回复
-
type/bug
难度 2/5 1-3 小时 新手友好度 62/100
flarum/framework#5301 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 82/100
AOSSIE-Org/BabyNest#284 ·
维护者通常 1 天内回复
-
status:untriaged
难度 1/5 1 小时以内 新手友好度 86/100
midnightntwrk/servicedesk#275 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 85/100
seek4science/seek#2762 ·
维护者通常 5 天内回复