企业微信回调验证失败,而测试入口似乎通过,先比较实际 HTTP 响应。2022 年项目中的关键差异是:原本应返回的解密字符串,被统一返回处理变成了带双引号的 JSON 字符串。
字符串值相同,响应内容可能不同
用非真实回调值说明:
原始文本:example
JSON 字符串:"example"
统一包装:{"data":"example"}
这三种响应不是相同的字节。协议要求原样返回验证结果时,额外引号、字段包装、BOM 或多余输出,都可能改变验证内容。企业微信接收消息文档入口
旧稿把测试与正式配置的差异判断为平台 bug。没有保存完整请求和版本,不能据此认定平台缺陷;本文保留的是当时“响应被二次序列化”的定位结果。
先排除统一返回包装
ControllerAdvice 只是扩展机制的入口,不会天然把所有字符串变成 JSON。需要检查项目中的 ResponseBodyAdvice、转换器、过滤器和网关究竟做了什么。
协议回调应从业务 JSON 包装中排除,保留自己的响应契约。仅把返回类型改成 String,不能保证已有包装逻辑不再介入。
下面是 Spring 5.x / Servlet 的纯文本传输演示,不是企业微信验签解密实现:
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RawTextDemo {
@GetMapping("/raw-text-demo")
public void rawText(HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("example");
}
}
这里使用普通 Controller 并直接处理响应,展示“原始文本”与 JSON 序列化的区别。真实回调只有通过签名验证并正确解密后,才能输出协议规定的结果;不要把示例固定字符串当作回调答案。
仍需返回对象时,检查转换器顺序
若 Fastjson 转换器排在 StringHttpMessageConverter 前,且两者都能处理当前响应,字符串可能被按 JSON 处理。以下为 Spring 5 的增量配置示例:
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class TextConverterConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
}
这会影响应用中的字符串响应,需要同时检查正常业务接口;它也不会自动撤销 Advice 已经做过的包装。不要为修一个接口清空全部转换器,或无意加上 @EnableWebMvc 接管整套 Boot MVC 配置。Spring 消息转换器
检查时比较响应状态、Content-Type 与原始字节,而不是只看控制器调试器中的 Java 字符串。相关的序列化选择问题见JSONField 与 Web MVC。