# HTTP methodBody 源契约

使用 snake_case 编写、生成和保存 HTTP 模块方法，并正确处理历史驼峰输入。

HTTP 方法的 `methodBody` 是项目源码的一部分。所有可修改源码的生产者，包括 Manager、
代码生成器、项目模板、迁移脚本和其他自动化工具，都必须直接写入本页定义的 snake\_case 字段。

这个要求只作用于 HTTP `methodBody`。`module.json` 和方法定义外层的 `paramDefinitions`、
`returnType`、`errorTypeMap` 等字段仍服从各自现行协议，不能做全文件机械重命名。

## 方法类型与 HTTP 动词

HTTP 模块方法文件外层的 `type` 表示平台方法实现类型，编译后对应运行时方法的 `@type`。
当前 HTTP 方法的合法值是 `HttpMethod`；它不是 HTTP 请求动词。

`GET`、`POST` 等 HTTP 动词必须写入 `methodBody.http_method`，查询参数必须写入
`methodBody.query`。`GET`、`POST` 和 `QUERY` 都不能写入方法实现类型。准确层级是：

```text
type = HttpMethod
└── methodBody.http_method = GET | POST | ...
└── methodBody.query = 查询参数映射
```

创建一个 GET 查询方法时，方法文件中的 `methodBody` 可以写为：

```json title="method-body.json"
{
  "request_url": "{serviceBaseUrl}/api/items",
  "protocol": "http",
  "http_method": "GET",
  "url": {
    "serviceBaseUrl": {
      "required": true,
      "position": "property",
      "key": "serviceBaseUrl"
    }
  },
  "body": null,
  "query": {
    "PageIndex": {
      "required": false,
      "position": "params",
      "key": "pageIndex"
    }
  },
  "header": {},
  "body_format": "json",
  "return_format": "json"
}
```

方法通过项目 Git 源码维护，再由 `baijimu bundle module version create --help` 当前命令面从精确
Git 提交创建不可变模块版本。不要使用历史 `baijimu module method create` 命令或旧的离线能力快照；
它们不是当前模块源码写入入口。

## 规范字段

`methodBody` 顶层只允许以下字段：

| 字段                         | 必填 | 含义                     |
| -------------------------- | -- | ---------------------- |
| `request_url`              | 是  | 请求地址或路径                |
| `protocol`                 | 否  | 请求协议                   |
| `http_method`              | 否  | HTTP 方法，如 `GET`、`POST` |
| `url`                      | 否  | URL 路径参数映射             |
| `body`                     | 否  | 请求体参数映射                |
| `query`                    | 否  | 查询参数映射                 |
| `header`                   | 否  | 请求头参数映射                |
| `body_format`              | 否  | 请求体格式                  |
| `return_format`            | 否  | 方法返回格式                 |
| `backend_response_format`  | 否  | 后端响应模型                 |
| `error_code_field`         | 否  | 响应错误码字段                |
| `error_message_field`      | 否  | 响应错误信息字段               |
| `data_field`               | 否  | 响应数据字段                 |
| `success_expression`       | 否  | 成功判定表达式                |
| `error_code_expression`    | 否  | 错误码提取表达式               |
| `error_message_expression` | 否  | 错误信息提取表达式              |
| `data_expression`          | 否  | 数据提取表达式                |

`url`、`body`、`query` 和 `header` 决定解析后的值写入 HTTP 请求的目标位置。每个映射项可包含
`required`、`position`、`key`、`default` 和 `expression`；映射对象的键是目标字段名，`key` 是
对应来源中的字段名。目标位置与值来源是两个不同维度，不能把外层映射名重复写进 `position`。

### 公开参数来源

模块项目源码允许写入的 `position` 只有以下四种：

| `position` | 值来源                | 主要用途                                                                                                 |
| ---------- | ------------------ | ---------------------------------------------------------------------------------------------------- |
| `params`   | 调用模块方法时传入的公共参数     | 把 `paramDefinitions` 中的输入写入 URL、查询、请求体或请求头                                                           |
| `context`  | Runtime 提供的可信调用上下文 | 读取当前调用方、用户或请求上下文；字段范围见[模块调用上下文](/development/bundle-development/module-development/runtime-context/) |
| `property` | 当前模块服务的已配置属性       | 读取服务地址、租户配置或凭据；敏感属性不得改写为固定值                                                                          |
| `fixed`    | 当前映射项的 `default`   | 写入非敏感协议常量；使用时必须同时提供 `default`                                                                        |

普通公共参数可以省略 `position`，省略时按 `params` 处理；生成器、模板和迁移脚本应显式写
`position: "params"`，避免来源含糊。使用 `expression` 计算值时可以省略 `position`，表达式中只能引用
当前公开上下文支持的变量。除上表四个值之外的来源由平台内部物化流程拥有，不属于模块项目作者协议，
不得由项目源码、Manager、模板或生成器写入。

例如，`item_id` 是后端 URL 占位符，而它的值来自公共方法参数 `itemId`；`name` 和 `pageNumber`
也来自公共方法参数：

```json
{
  "request_url": "https://api.example.com/v1/items/{item_id}",
  "protocol": "http",
  "http_method": "POST",
  "url": {
    "item_id": {
      "required": true,
      "position": "params",
      "key": "itemId"
    }
  },
  "body": {
    "name": {
      "required": true,
      "position": "params",
      "key": "name"
    }
  },
  "query": {
    "page": {
      "required": false,
      "position": "params",
      "key": "pageNumber"
    }
  },
  "header": {},
  "body_format": "json",
  "return_format": "json",
  "backend_response_format": "cmodel",
  "error_code_field": "code",
  "error_message_field": "message",
  "data_field": "data",
  "success_expression": "response.code == 0",
  "error_code_expression": "response.code",
  "error_message_expression": "response.message",
  "data_expression": "response.data"
}
```

以下写法是错误的，因为它把 HTTP 目标位置误写成了值来源，模块版本创建会拒绝：

```json
{
  "url": {
    "item_id": {
      "position": "url"
    }
  },
  "body": {
    "name": {
      "position": "body"
    }
  },
  "query": {
    "page": {
      "position": "query"
    }
  }
}
```

项目服务能够读取对象形式或历史项目中的 JSON 字符串形式；修改时保留当前方法文件的
外层存储形状，但字符串内部也必须使用上述 snake\_case 字段。

## 历史兼容边界

历史 `httpMethod`、`bodyFormat`、`returnFormat`、`backendResponseFormat`、
`errorCodeField`、`errorMessageField`、`dataField` 以及各类 `*Expression` 驼峰字段，只能
在受控输入边界读取并转换。它们不是新的写入协议，任何生产者都不能继续产生这些字段。

如果同一语义同时出现 snake\_case 和历史字段，保存方必须拒绝冲突，不能猜测哪一个正确。
未知顶层字段也会在模块版本创建时被拒绝，不能静默丢弃。读取旧项目后只要发生保存，就应
输出单一的规范字段集合。

历史项目还可能把外层 `url`、`body`、`query` 或 `header` 名称写入映射项的 `position`。迁移时
不能按外层位置机械替换：先确认值的权威来源；来自公共方法参数时改为 `params`，来自 Runtime 上下文时
改为 `context`，来自模块属性时改为 `property`，非敏感协议常量才改为 `fixed` 并提供 `default`。
无法确认来源时必须停止迁移并检查 `paramDefinitions`、模块属性和调用上下文，不能猜测或增加兜底。

当前 `baijimu bundle manifest validate` 只校验 Bundle 项目清单，不校验模块项目中的
`module.json` 和 `methods/*.json`。创建模块版本前仍须按本页检查源码；最终由模块版本创建接口使用
当前平台合同执行严格校验。公开模块方法 JSON Schema 或对应 CLI 离线校验命令发布后，应以其版本化
合同为准，不能自行复制平台内部类型生成另一份枚举。

## 修改检查

提交前至少检查：

1. 只修改预期的 `methods/*.json`，没有机械重命名方法定义外层字段。
2. `methodBody` 不包含带大写字母的顶层字段，也不同时包含历史别名。
3. `request_url` 非空；使用 `params` 时，映射项的 `key` 能在 `paramDefinitions` 中找到对应公共参数。
4. `position` 只使用 `params`、`context`、`property` 或 `fixed`，没有写入 `url`、`body`、`query`、`header` 或内部来源。
5. 生成器、模板和 UI 保存结果与手写项目文件使用同一规范。
6. 创建模块版本后，安装 Artifact 中仍只出现 snake\_case 字段。

完成源码修改后，按[模块版本创建与 Bundle 交付](/development/bundle-development/module-development/publish-and-install/)
提交项目 Git、创建模块版本并纳入 Bundle。
