调用微信公众平台模板消息发送接口时,如果响应里出现 errcode 为 47001 或 47003,通常就说明请求体里的 data 对象没有按微信要求拼接。很多情况下 JSON 本身是合法的,但 value 字段类型不对、必填字段缺失,或者 data 被误传为空对象,都会触发这类错误。本文把触发条件、错误码含义和修复方式整理清楚,方便在服务端直接定位。

先分清错误码:47001 和 47003 分别对应什么
微信模板消息接口返回的错误码里,47001 的官方描述是数据格式错误,也就是服务端解析出来的 data 字段不符合模板定义。它可能由几种情况引起:某个 value 不是字符串、JSON 非法、漏传 data 字段,或者 data 本身为空对象。很多开发者只检查 JSON 是否合法,却忽略了微信还要求每个变量的值必须是字符串这一细节。
另一个常见错误码 47003 表示模板参数不正确,通常是 data 里某个 key 和模板中定义的字段名对不上。比如后台模板里写的是 keyword1.DATA,但请求体传成了 keynote1,或者模板已经修改,代码里还沿用旧字段名。遇到 47003 时,不要盲目调整发送逻辑,先去公众号后台核对当前模板的详细字段。
如果日志里只出现“模板消息内容为空”这样的业务提示,但错误码没有明确返回,也可以按照 47001 的排查方向处理。因为 data 一旦被序列化成 null 或 {},接口可能直接给出参数错误,也可能返回 47001。所以统一从 data 结构和字段类型入手,能覆盖大多数内容为空的问题。
data 字段的正确结构:为什么内容会变成空
公众号模板消息的请求体并不需要完整消息内容,只需要填充 data 对象。微信后台模板里已经写好了 {{first.DATA}}、{{keyword1.DATA}} 这些占位符,接口负责把对应变量传上去。所以只要模板在后台配置正常,发送内容为空几乎可以断定是后端构造 data 时出了问题。
最常见的错误是把 value 写成数字、布尔值或 null。微信要求每个字段的值必须是字符串。比如订单金额 100 必须写成 str(100),不能直接传 100。一些弱类型语言如 PHP 如果不注意,数组里的整数会原样进入 JSON,最终返回 47001。字段缺失同样会触发报错,假设模板配置了 first、keyword1、remark 三个变量,但请求体只传了 keyword1,微信就会认为内容不完整。解决方法是根据后台模板详情逐个核对字段名,并且对空值做兜底处理,例如把 null 转为空字符串。
# 正确结构示例
payload = {
"touser": "OPENID",
"template_id": "模板ID",
"data": {
"first": {"value": "你好,有一条新通知"},
"keyword1": {"value": "订单已支付"},
"keyword2": {"value": "98.00"},
"remark": {"value": "点击查看详情"}
}
}
上面示例中所有的 value 都是字符串。如果某个字段确实没有内容,也不要省略 key,而应传 {'value': ''}。否则一旦模板变量被声明为必填,就会复现内容为空或数据格式不正确的错误。很多定时任务发送通知时,因为变量没有取到值,直接传了空字典,最终导致发送失败。
从请求体排查:序列化、转义和隐藏字符
排查这一类问题时,不要只看前端回调,应该先把发送前的 JSON 字符串打印出来。比如 Python 使用 json.dumps(payload, ensure_ascii=False) 后,直接复制到微信公众平台的接口调试工具发送,观察返回码。如果本地打印出的 data 是空对象 {},说明上游赋值逻辑没有走到,需要检查变量来源。
JSON 转义是另一个容易忽略的点。消息内容里如果包含反斜杠、双引号或换行符,必须符合 JSON 标准。例如 Windows 文件路径 C:\Users\WeChat 在 JSON 字符串中应写成 C:\\Users\\WeChat,否则服务端解析可能直接失败。微信接口对长度也有要求,单个 value 一般不能超过 20 个字符或 50 个字符,超长也会报格式错误。
引号嵌套常见于拼接字符串时手工拼 JSON。比如在 Java 里直接拼 "value":" + msg + ",一旦 msg 里有引号未被转义,就会产生非法 JSON。正确做法是使用 JSON 序列化库,不要手动拼接。统一封装发送函数后,变量内容变化也不会影响格式正确性。
import com.alibaba.fastjson.JSONObject;
public class WxTemplateSender {
public static void main(String[] args) {
JSONObject payload = new JSONObject();
payload.put("touser", "OPENID");
payload.put("template_id", "模板ID");
JSONObject data = new JSONObject();
JSONObject first = new JSONObject();
// value 必须是字符串,数字需要转换
first.put("value", String.valueOf(98));
data.put("first", first);
payload.put("data", data);
System.out.println(payload.toJSONString());
}
}
上面的 Java 示例依赖 Fastjson,实际项目中也可用 Jackson 或 Gson。关键点不是用哪个库,而是通过库构建对象后统一序列化,避免手工拼接造成引号、反斜杠等特殊字符丢失或错位。如果项目里已经有统一的 HTTP 请求工具,建议把模板消息发送方法也收口到工具层。
错误码对照与实际修复步骤
除了 47001 和 47003,发送模板消息时还可能遇到其他错误码。下面整理一个简表,方便在日志中快速判断。
| errcode | 含义 | 常见触发原因 |
|---|---|---|
| 40037 | template_id 不正确 | 模板 ID 复制错误或使用了订阅号模板 |
| 43004 | 需要用户关注服务号 | 接收者 openid 未关注该服务号 |
| 47001 | 数据格式错误 | value 类型非字符串、JSON 非法、字段缺失 |
| 47003 | 模板参数不正确 | data 中的 key 与模板字段不一致 |
如果日志里先出现 40037,就要先核对 template_id 是否属于当前公众号后台的模板库。若出现 43004,则优先检查 openid 是否与当前 appid 对应,以及用户是否已经关注服务号。内容为空或格式不正确的场景,主要集中在 47001 和 47003 两类。
实际修复时建议按以下顺序:第一步查看模板详情,确认每个字段的 key 名;第二步打印完整请求体,确认 data 不为空且所有 value 为字符串;第三步用官方接口调试工具发送一次,排除业务代码干扰;第四步检查 JSON 转义字符,尤其是路径中的反斜杠和用户输入里的双引号;第五步统一封装发送函数,避免多处拼接造成格式不一致。
只要把构造 data 对象的逻辑收口到一个工具函数里,并且所有字段都强制转换为字符串,后续即使模板变更也只需要调整字段映射,不再容易出现内容为空或格式不正确的错误。对于已经上线的业务,建议增加发送前校验,发现 data 为空或字段类型错误时直接抛出业务异常,减少微信接口调用失败对用户体验的影响。