传入字符串变量与直接传字符串值调用API结果不同的问题
问题
将字符串变量作为参数调用API时返回“invalid input data”错误,但复制变量的值直接传入API却能正常运行,推测该问题与编译器对转义引号"的处理行为有关。
API定义
@PostMapping(value = "/data/v0/query", consumes = {MediaType.APPLICATION_JSON_VALUE}) JsonNode mecGraphQLQuery( @RequestHeader(HttpHeaders.AUTHORIZATION) String bearerToken, @RequestBody String query );
API期望的query参数格式
"{ \"query\": \"query getMecFeedbackDataByDateRange( $filter: Filter, $numPerPage: Int!, $orderBy: [RecordOrder] ) { feedback( filter: $filter, first: $numPerPage, orderBy: $orderBy ) { nodes { e_bp_digital_uuid_txt: fieldValue(fieldId: \\\"e_bp_digital_uuid_txt\\\") responsedate: fieldValue(fieldId: \\\"e_responsedate\\\") } totalCount pageInfo { endCursor hasNextPage } } }\", \"variables\": { \"filter\": { \"and\": [ { \"fieldIds\": [ \"e_responsedate\" ], \"gte\": \"2023-08-22 01:52:15\" }, { \"fieldIds\": [ \"e_responsedate\" ], \"lte\": \"2023-08-24 01:57:15\" } ] }, \"numPerPage\": 1000, \"orderBy\": [ { \"fieldId\": \"e_responsedate\", \"direction\": \"DESC\" } ] }}"
生成query的代码
public String getMecFeedbackDataByDateRangeQuery(String host, String mecAccessToken, String fromDate, String toDate, Set<MecField> fieldNames) { Preconditions.checkArgument(!fieldNames.isEmpty(), "List of fields must not be empty!"); Map<String,Object> velocityContext = new HashMap<>(); velocityContext.put("fromDate", fromDate); velocityContext.put("toDate", toDate); velocityContext.put("fieldNames", fieldNames); String query = generateGraphQLQuery(velocityContext, "/graphQLTemplates/feedbackDataByDateRange.vm"); log.info("MEC GraphQL Query: " + query); return query.replace("\n", ""); } String query = graphQLFeedbackClient.getMecFeedbackDataByDateRangeQuery(mecIntegrationConfiguration.getHostHeader(), mecAccessToken, fromDateAsString, toDateAsString, fieldNames);
Velocity模板文件/graphQLTemplates/feedbackDataByDateRange.vm内容
"{ \"query\": \"query getMecFeedbackDataByDateRange( $filter: Filter, $numPerPage: Int!, $orderBy: [RecordOrder] ) { feedback( filter: $filter, first: $numPerPage, orderBy: $orderBy ) { nodes { #foreach ($field in $fieldNames) $field.getField(): fieldValue(fieldId: \\\"$field.getField()\\\") #end responsedate: fieldValue(fieldId: \\\"e_responsedate\\\") } totalCount pageInfo { endCursor hasNextPage } } }\", \"variables\": { \"filter\": { \"and\": [ { \"fieldIds\": [ \"e_responsedate\" ], \"gte\": \"$fromDate\" }, { \"fieldIds\": [ \"e_responsedate\" ], \"lte\": \"$toDate\" } ] }, \"numPerPage\": 1000, \"orderBy\": [ { \"fieldId\": \"e_responsedate\", \"direction\": \"DESC\" } ] } }"
调用代码对比
- 传变量时报错:
mecApisGraphQLClient.mecGraphQLQuery(MecGraphQLClient.BEARER_PREFIX + mecAccessToken, query)
- 直接传复制的值时正常:
mecApisGraphQLClient.mecGraphQLQuery(MecGraphQLClient.BEARER_PREFIX + mecAccessToken, "{ \"query\": \"query getMecFeedbackDataByDateRange( $filter: Filter, $numPerPage: Int!, $orderBy: [RecordOrder] ) { feedback( filter: $filter, first: $numPerPage, orderBy: $orderBy ) { nodes { e_bp_digital_uuid_txt: fieldValue(fieldId: \\\"e_bp_digital_uuid_txt\\\") responsedate: fieldValue(fieldId: \\\"e_responsedate\\\") } totalCount pageInfo { endCursor hasNextPage } } }\", \"variables\": { \"filter\": { \"and\": [ { \"fieldIds\": [ \"e_responsedate\" ], \"gte\": \"2023-08-22 01:52:15\" }, { \"fieldIds\": [ \"e_responsedate\" ], \"lte\": \"2023-08-24 01:57:15\" } ] }, \"numPerPage\": 1000, \"orderBy\": [ { \"fieldId\": \"e_responsedate\", \"direction\": \"DESC\" } ] }}");
通过DiffUtils.diff得到差异对比结果(见附图)。
解决方案
问题根源
核心问题是Velocity生成的字符串存在多余的包裹层级和转义处理错误:
- 直接传入的字符串是Java代码字面量,编译器会自动处理双重转义(比如
\\\"最终会解析为\"); - 但Velocity模板中手动添加的最外层双引号会被直接输出,导致整个query变成了带额外引号包裹的字符串,而API期望的是纯JSON内容,不需要这层多余的引号。
修复步骤
- 修改Velocity模板,移除最外层双引号
删除模板开头的"{和结尾的}",因为这层引号是Java字面量的语法需要,不是实际JSON的一部分。修改后的模板如下:{ \"query\": \"query getMecFeedbackDataByDateRange( $filter: Filter, $numPerPage: Int!, $orderBy: [RecordOrder] ) { feedback( filter: $filter, first: $numPerPage, orderBy: $orderBy ) { nodes { #foreach ($field in $fieldNames) $field.getField(): fieldValue(fieldId: \\\"$field.getField()\\\") #end responsedate: fieldValue(fieldId: \\\"e_responsedate\\\") } totalCount pageInfo { endCursor hasNextPage } } }\", \"variables\": { \"filter\": { \"and\": [ { \"fieldIds\": [ \"e_responsedate\" ], \"gte\": \"$fromDate\" }, { \"fieldIds\": [ \"e_responsedate\" ], \"lte\": \"$toDate\" } ] }, \"numPerPage\": 1000, \"orderBy\": [ { \"fieldId\": \"e_responsedate\", \"direction\": \"DESC\" } ] } } - 验证生成的字符串格式
生成后通过日志打印query,确认内容和直接传入的字面量(去掉Java语法转义后的纯JSON)完全一致。 - 长期优化方案
避免用字符串模板拼接JSON,推荐使用Jackson/Gson等JSON库构建实体类后序列化:- 定义Query、Variables对应的实体类;
- 填充实体类数据;
- 将实体类序列化为JSON字符串传入API,彻底规避转义错误。
内容的提问来源于stack exchange,提问作者aaa
相关产品推荐
相关产品推荐

