在 Feign 调用 中模拟 Swagger 请求
在Feign 调用中模拟 Swagger 请求,本质是:构建一个与 Swagger 请求体完全一致的 JSON 字符串,然后塞入 DTO 的data字段(类型为Object)。
下面用Gson和Hutool两种方式,给出完整代码示例。
一、Swagger 中的 JSON 请求体示例
假设你在 Swagger 中提交的 JSON 是:
json
{ "phone": "15921892280", "templateCode": "SMS_123456789", "templateParam": { "code": "1234", "extra": { "param": "test" } } }注意:templateParam是一个嵌套 JSON 对象,不是字符串。
二、使用 Gson 构建相同的 JSON
1. 构建嵌套的JsonObject
java
import com.google.gson.JsonObject; // 构建内层 extra JsonObject extra = new JsonObject(); extra.addProperty("param", "test"); // 构建 templateParam JsonObject templateParam = new JsonObject(); templateParam.addProperty("code", "1234"); templateParam.add("extra", extra); // 嵌套对象 // 构建根对象 JsonObject root = new JsonObject(); root.addProperty("phone", "15921892280"); root.addProperty("templateCode", "SMS_123456789"); root.add("templateParam", templateParam);2. 转为 JSON 字符串
java
String jsonString = root.toString(); System.out.println(jsonString); // 输出: {"phone":"15921892280","templateCode":"SMS_123456789","templateParam":{"code":"1234","extra":{"param":"test"}}}3. 设置到 DTO 并调用 Feign
java
// DTO 中的 data 字段是 Object MessageSendReq req = new MessageSendReq(); req.setData(jsonString); // 传入 JSON 字符串 // Feign 调用 messageClient.sendMessage(req);
三、如果 Swagger 中的templateParam是字符串而不是对象
有些接口要求templateParam是JSON 字符串(如阿里云短信),此时 Swagger 请求体可能是:
json
{ "phone": "15921892280", "templateCode": "SMS_123456789", "templateParam": "{\"code\":\"1234\",\"extra\":{\"param\":\"test\"}}" }这种情况下,你需要先构建内部 JSON,再转为字符串作为值:
java
// 1. 构建内部对象 JsonObject inner = new JsonObject(); inner.addProperty("code", "1234"); JsonObject extra = new JsonObject(); extra.addProperty("param", "test"); inner.add("extra", extra); // 2. 将内部对象转为 JSON 字符串 String innerJson = inner.toString(); // {"code":"1234","extra":{"param":"test"}} // 3. 构建根对象 JsonObject root = new JsonObject(); root.addProperty("phone", "15921892280"); root.addProperty("templateCode", "SMS_123456789"); root.addProperty("templateParam", innerJson); // 注意:作为字符串传入 String finalJson = root.toString();四、使用 Hutool(更简洁,推荐)
如果项目已引入 Hutool,可以更直观地构建:
java
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; // 构建内层 JSONObject extra = JSONUtil.createObj() .put("param", "test"); JSONObject templateParam = JSONUtil.createObj() .put("code", "1234") .put("extra", extra); // 构建根对象 JSONObject root = JSONUtil.createObj() .put("phone", "15921892280") .put("templateCode", "SMS_123456789") .put("templateParam", templateParam); // 转为字符串 String jsonString = root.toString(); // 设置到 DTO req.setData(jsonString);五、JsonObject和String的区别(关键点)
| 传入 DTO 的类型 | Jackson 序列化后的 HTTP Body | Swagger 是否一致 |
|---|---|---|
JsonObject | ❌ 序列化失败(抛出异常) | — |
Map<String, Object> | {"data":{"code":"1234"}} | ✅ 如果 Swagger 是对象,则一致 |
String(JSON 字符串) | {"data":"{\"code\":\"1234\"}"} | ⚠️ 如果 Swagger 是对象,则多了引号 |
结论:你必须明确 Swagger 中data字段期望的是JSON 对象还是JSON 字符串。
如果期望JSON 对象,你应该传入
Map或 POJO,而不是String。如果期望JSON 字符串(如阿里云短信),则传入
String。
六、如何确认 Swagger 期望的类型?
查看 Swagger 的请求体示例:
如果显示为
{ "code": "1234" }→ 是JSON 对象如果显示为
"{\"code\":\"1234\"}"→ 是JSON 字符串
根据你的情况,阿里云短信的TemplateParam是字符串,所以用jsonObject.toString()是正确的。
七、完整调用示例(Gson 版)
java
public void sendSms() { // 1. 构建参数 JsonObject param = new JsonObject(); param.addProperty("code", "1234"); param.addProperty("phone", "15921892280"); // 如果有嵌套 JsonObject extra = new JsonObject(); extra.addProperty("type", "notification"); param.add("extra", extra); // 2. 转为 JSON 字符串(因为阿里云要求 TemplateParam 是字符串) String templateParamStr = param.toString(); // 3. 构建请求 DTO MessageSendReq req = new MessageSendReq(); req.setPhone("15921892280"); req.setTemplateCode("SMS_123456789"); req.setData(templateParamStr); // 传入字符串 // 4. Feign 调用 try { MessageSendResp resp = messageClient.sendMessage(req); System.out.println("发送成功: " + resp); } catch (Exception e) { System.err.println("发送失败: " + e.getMessage()); } }八、总结
| 步骤 | 操作 |
|---|---|
| 1 | 使用 Gson / Hutool 构建与 Swagger 格式一致的 JSON 结构 |
| 2 | 如果是阿里云短信,调用toString()转为 JSON 字符串 |
| 3 | 将该字符串设置到 DTO 的data字段 |
| 4 | 通过 Feign 发送 |
这样,Feign 发送的请求体就与 Swagger 完全一致,不会再有序列化错误。
