Skip to content

Conversation

@WoofZJ
Copy link

@WoofZJ WoofZJ commented Feb 11, 2026

问题描述

当前服务端的中间件在校验 Content-Type 时使用了严格全等判断 (!==):

if (req.headers['content-type'] !== 'application/json') { ... }

导致的问题是,一些 HTTP 客户端(如 .NET 的 HttpClient)在发送请求时会自动附加编码信息,将头设置为 application/json; charset=utf-8,从而导致服务端判定为不匹配返回 415

修改内容

改成了包含判断

if (!req.headers['content-type']?.includes('application/json')) { ... }

Summary by Sourcery

Bug Fixes:

  • 允许带有类似 application/json; charset=utf-8 这种 Content-Type 头的 API 请求通过校验,而不是返回 415 错误。
Original summary in English

Summary by Sourcery

Bug Fixes:

  • Allow API requests with Content-Type headers like application/json; charset=utf-8 to pass validation instead of being rejected with 415.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 11, 2026

评审者指南(在小型 PR 上折叠)

评审者指南

放宽 Milky HTTP access-token 中间件中的 Content-Type 校验逻辑,使带有额外参数的 JSON 请求(例如 application/json; charset=utf-8)可以被接受,而不是返回 415 错误。

Access token 中间件中放宽 JSON Content-Type 校验的时序图

sequenceDiagram
actor Client
participant MilkyHttpHandler
participant AccessTokenMiddleware
participant ApiHandler

Client->>MilkyHttpHandler: HTTP request to /prefix/api
activate MilkyHttpHandler
MilkyHttpHandler->>AccessTokenMiddleware: Invoke middleware
activate AccessTokenMiddleware
AccessTokenMiddleware->>AccessTokenMiddleware: Read req.headers[content-type]
AccessTokenMiddleware->>AccessTokenMiddleware: includes application/json ?
alt ContentType_includes_application_json
  AccessTokenMiddleware-->>MilkyHttpHandler: next()
  MilkyHttpHandler->>ApiHandler: Handle API request
  activate ApiHandler
  ApiHandler-->>Client: 2xx/4xx/5xx response
  deactivate ApiHandler
else ContentType_missing_or_not_json
  AccessTokenMiddleware-->>Client: 415 Unsupported Media Type
  deactivate MilkyHttpHandler
end
deactivate AccessTokenMiddleware
Loading

MilkyHttpHandler access token 中间件 Content-Type 检查的类图

classDiagram
class MilkyHttpHandler {
  -app
  -config
  -ctx
  +registerApiAccessTokenMiddleware()
}

class ApiAccessTokenMiddleware {
  +handle(req,res,next)
  +isJsonContentType(contentType)
}

MilkyHttpHandler --> ApiAccessTokenMiddleware: uses

ApiAccessTokenMiddleware : isJsonContentType(contentType) checks includes application/json
Loading

文件级变更

变更 详情 文件
放宽 access-token 中间件中的 Content-Type 检查,以接受带可选参数的 application/json。
  • 将对 'application/json' 的严格相等检查替换为对 Content-Type 头部调用 .includes('application/json') 进行检查。
  • 保持现有日志和中间件结构不变,仅修改校验条件的行为。
src/milky/network/http.ts

技巧和命令

与 Sourcery 交互

  • 触发新评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 在评审评论下回复,让 Sourcery 从该评论创建一个 issue。你也可以回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文的任意位置写上 @sourcery-ai summary,即可在你想要的位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成评审者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成评审者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。当你已经处理完所有评论且不想再看到它们时非常有用。
  • 忽略所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有已有的 Sourcery 评审。尤其适用于你想从头开始一次新评审的情况——别忘了再评论 @sourcery-ai review 来触发新的评审!

自定义你的体验

访问你的 控制面板 以:

  • 启用或禁用评审功能,例如 Sourcery 自动生成的 pull request 摘要、评审者指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

获取帮助

Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Relaxes the Content-Type validation in the Milky HTTP access-token middleware so that JSON requests with additional parameters like charset (e.g., application/json; charset=utf-8) are accepted instead of rejected with 415.

Sequence diagram for relaxed JSON Content-Type validation in access token middleware

sequenceDiagram
actor Client
participant MilkyHttpHandler
participant AccessTokenMiddleware
participant ApiHandler

Client->>MilkyHttpHandler: HTTP request to /prefix/api
activate MilkyHttpHandler
MilkyHttpHandler->>AccessTokenMiddleware: Invoke middleware
activate AccessTokenMiddleware
AccessTokenMiddleware->>AccessTokenMiddleware: Read req.headers[content-type]
AccessTokenMiddleware->>AccessTokenMiddleware: includes application/json ?
alt ContentType_includes_application_json
  AccessTokenMiddleware-->>MilkyHttpHandler: next()
  MilkyHttpHandler->>ApiHandler: Handle API request
  activate ApiHandler
  ApiHandler-->>Client: 2xx/4xx/5xx response
  deactivate ApiHandler
else ContentType_missing_or_not_json
  AccessTokenMiddleware-->>Client: 415 Unsupported Media Type
  deactivate MilkyHttpHandler
end
deactivate AccessTokenMiddleware
Loading

Class diagram for MilkyHttpHandler access token middleware Content-Type check

classDiagram
class MilkyHttpHandler {
  -app
  -config
  -ctx
  +registerApiAccessTokenMiddleware()
}

class ApiAccessTokenMiddleware {
  +handle(req,res,next)
  +isJsonContentType(contentType)
}

MilkyHttpHandler --> ApiAccessTokenMiddleware: uses

ApiAccessTokenMiddleware : isJsonContentType(contentType) checks includes application/json
Loading

File-Level Changes

Change Details Files
Relax Content-Type check in access-token middleware to accept application/json with optional parameters.
  • Replace strict equality check against 'application/json' with an .includes('application/json') check on the Content-Type header.
  • Keep existing logging and middleware structure unchanged so only the validation condition behavior is modified.
src/milky/network/http.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些高层次的反馈:

  • 使用 includes('application/json') 可能会匹配到非预期的值(例如 application/jsonfoo);建议使用更严格的检查方式,比如解析媒体类型(例如使用 content-type 包),或者在去掉参数后使用 startsWith('application/json') 进行检查。
  • 日志消息仍然写着 Content-Type not application/json,即使现在已经允许 charset 变体;建议更新日志文案以反映放宽后的匹配规则,或者直接记录实际的 Content-Type 值以提高可读性。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- Using `includes('application/json')` could match unintended values (e.g. `application/jsonfoo`); consider using a stricter check like parsing the media type (e.g. via the `content-type` package) or checking `startsWith('application/json')` after splitting off parameters.
- The log message still says `Content-Type not application/json` even though charset variants are now allowed; consider updating the message to reflect the relaxed matching or log the actual `Content-Type` value for clarity.

## Individual Comments

### Comment 1
<location> `src/milky/network/http.ts:48` </location>
<code_context>
     if (this.config.accessToken) {
       this.app.use(`${this.config.prefix}/api`, (req, res, next) => {
-        if (req.headers['content-type'] !== 'application/json') {
+        if (!req.headers['content-type']?.includes('application/json')) {
           this.ctx.logger.warn(
             'MilkyHttp',
</code_context>

<issue_to_address>
**suggestion:** Consider making the Content-Type check case-insensitive and robust to array values.

`req.headers['content-type']` can be `string | string[] | undefined`, and header names/values may vary in case. Normalizing and flattening first avoids subtle mismatches, e.g.:

```ts
const contentType = req.headers['content-type'];
const contentTypeStr = Array.isArray(contentType) ? contentType.join(',') : contentType || '';

if (!contentTypeStr.toLowerCase().includes('application/json')) {
  // ...
}
```

This will handle values like `application/json; charset=utf-8`, mixed casing, and multi-valued headers correctly.
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Using includes('application/json') could match unintended values (e.g. application/jsonfoo); consider using a stricter check like parsing the media type (e.g. via the content-type package) or checking startsWith('application/json') after splitting off parameters.
  • The log message still says Content-Type not application/json even though charset variants are now allowed; consider updating the message to reflect the relaxed matching or log the actual Content-Type value for clarity.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Using `includes('application/json')` could match unintended values (e.g. `application/jsonfoo`); consider using a stricter check like parsing the media type (e.g. via the `content-type` package) or checking `startsWith('application/json')` after splitting off parameters.
- The log message still says `Content-Type not application/json` even though charset variants are now allowed; consider updating the message to reflect the relaxed matching or log the actual `Content-Type` value for clarity.

## Individual Comments

### Comment 1
<location> `src/milky/network/http.ts:48` </location>
<code_context>
     if (this.config.accessToken) {
       this.app.use(`${this.config.prefix}/api`, (req, res, next) => {
-        if (req.headers['content-type'] !== 'application/json') {
+        if (!req.headers['content-type']?.includes('application/json')) {
           this.ctx.logger.warn(
             'MilkyHttp',
</code_context>

<issue_to_address>
**suggestion:** Consider making the Content-Type check case-insensitive and robust to array values.

`req.headers['content-type']` can be `string | string[] | undefined`, and header names/values may vary in case. Normalizing and flattening first avoids subtle mismatches, e.g.:

```ts
const contentType = req.headers['content-type'];
const contentTypeStr = Array.isArray(contentType) ? contentType.join(',') : contentType || '';

if (!contentTypeStr.toLowerCase().includes('application/json')) {
  // ...
}
```

This will handle values like `application/json; charset=utf-8`, mixed casing, and multi-valued headers correctly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

if (this.config.accessToken) {
this.app.use(`${this.config.prefix}/api`, (req, res, next) => {
if (req.headers['content-type'] !== 'application/json') {
if (!req.headers['content-type']?.includes('application/json')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 建议让 Content-Type 的检查对大小写不敏感,并且能正确处理数组形式的值。

req.headers['content-type'] 可能是 string | string[] | undefined,并且头名/头值的大小写都可能不同。先统一大小写并将其拍平成字符串,可以避免一些细微的不匹配,例如:

const contentType = req.headers['content-type'];
const contentTypeStr = Array.isArray(contentType) ? contentType.join(',') : contentType || '';

if (!contentTypeStr.toLowerCase().includes('application/json')) {
  // ...
}

这样可以正确处理诸如 application/json; charset=utf-8、大小写混合以及多值 header 等情况。

Original comment in English

suggestion: Consider making the Content-Type check case-insensitive and robust to array values.

req.headers['content-type'] can be string | string[] | undefined, and header names/values may vary in case. Normalizing and flattening first avoids subtle mismatches, e.g.:

const contentType = req.headers['content-type'];
const contentTypeStr = Array.isArray(contentType) ? contentType.join(',') : contentType || '';

if (!contentTypeStr.toLowerCase().includes('application/json')) {
  // ...
}

This will handle values like application/json; charset=utf-8, mixed casing, and multi-valued headers correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant