NexarAPI中GraphQL带参数查询报错及RestTemplate调用问题求助
问题现象
- 发送无参数的基础GraphQL查询,设置
Content-Type: application/json时可正常获取数据:{ "query": "query Categories { supCategories { id name path }}" } - 发送带
workspaceUrl参数的查询时,抛出语法错误:
错误信息:{ "query": "query design2_Library { desLibrary (workspaceUrl: \"test\") { components { nodes { id name manufacturerParts { partNumber companyName } details { parameters { name value } } } } } }" }<200,{"errors":[{"message":"Expected a
String-token, but found aName-token.","locations":[{"line":1,"column":64}],"extensions":{"code":"HC0011"}}]},[Date:"Fri, 21 Jun 2024 11:16:40 GMT", Content-Type:"application/json; charset=utf-8", Transfer-Encoding:"chunked", Server:"Kestrel", Cache-Control:"proxy-revalidate", X-SYMC-Transaction-UUID:"8b74d844d6cf25c1-000000000b3ff64b-0000000066756118", Connection:"keep-alive"]> - 尝试将
Content-Type改为graphql时,返回404错误。
相关Java RestTemplate代码
public String getComponent(String query) { logger.info("getComponent {Query:" + query + "}"); try { Context initContext = new InitialContext(); Map token = generateOAuthAcessToken(initContext, "QA"); String url = nexarEndPoint; UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParam("partNo", query); HttpHeaders headers = new HttpHeaders(); headers.setAccept( Arrays.asList(new MediaType[] { MediaType.valueOf("application/graphql-response+json") })); headers.setContentType(MediaType.valueOf("application/graphql-response+json")); // headers.setContentType(customMediaType); headers.add("Authorization", "Bearer " + token.get("access_token")); String alternateQuery = "{\"query\":\"query design2_Library { desLibrary (workspaceUrl: \\\\<altium workspace url\\\\\") { components { nodes { id name manufacturerParts { partNumber companyName } details { parameters { name value } } } } } }\",\"variables\":{}}"; query = "{ \"query\": \"query Categories { supCategories { id name path }}\"}"; String graphqlQuery = "{ \"query\": \"query design2_Library { desLibrary (workspaceUrl: \"test\") { components { nodes { id name manufacturerParts { partNumber companyName } details { parameters { name value } } } } } }\" }"; // String graphqlQuery1 = "{ \"query\": \"query design2_Library { desLibrary System.setProperty("https.protocols", "TLSv1.2"); HttpEntity<String> entity = new HttpEntity<String>(query.toString(), headers); RestTemplate template = new RestTemplate(); ResponseEntity<String> response = template.exchange(builder.toUriString(), HttpMethod.POST, entity, String.class); String result = response.getBody(); JSONArray resultArr = new JSONObject(result).getJSONObject("data").getJSONObject("desLibrary") .getJSONObject("components").getJSONArray("nodes"); return resultArr.toString(); } catch (Exception ex) { logger.error("Search value " + query + "\n" + ex.getMessage()); return "Error : " + ex.getMessage(); } }
解决方案
1. 修复查询字符串转义问题
报错核心是JSON嵌套双引号未正确转义:workspaceUrl: "test"里的双引号会被JSON解析器误判为外层JSON的结束符。Java中需要用\\转义嵌套双引号,修正后的查询字符串:
String graphqlQuery = "{ \"query\": \"query design2_Library { desLibrary (workspaceUrl: \\\"test\\\") { components { nodes { id name manufacturerParts { partNumber companyName } details { parameters { name value } } } } } }\" }";
2. 规范Content-Type设置
保持Content-Type: application/json即可,Nexar API大概率不支持graphql类型的POST端点。同时统一Accept头为标准格式:
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setContentType(MediaType.APPLICATION_JSON);
3. 用JSON库构造请求体(推荐)
手动拼接JSON容易出错,使用Jackson/Gson等库序列化请求体,彻底避免转义问题:
// 构造请求体Map Map<String, Object> requestBody = new HashMap<>(); String graphqlQueryStr = "query design2_Library { desLibrary (workspaceUrl: \"test\") { components { nodes { id name manufacturerParts { partNumber companyName } details { parameters { name value } } } } } }"; requestBody.put("query", graphqlQueryStr); // 序列化为JSON字符串 ObjectMapper objectMapper = new ObjectMapper(); String requestJson = objectMapper.writeValueAsString(requestBody); // 构造请求实体 HttpEntity<String> entity = new HttpEntity<>(requestJson, headers);
4. 移除不必要的URL参数
Nexar API的GraphQL参数应放在请求体中,而非URL参数,移除代码中的partNo查询参数:
// 原代码:UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParam("partNo", query); // 修改为: UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
5. 增加响应空值判断
原代码链式调用getJSONObject/getJSONArray容易因数据为空抛出异常,添加空值检查:
JSONObject resultObj = new JSONObject(result); JSONArray resultArr = new JSONArray(); if (resultObj.has("data") && !resultObj.isNull("data")) { JSONObject dataObj = resultObj.getJSONObject("data"); if (dataObj.has("desLibrary") && !dataObj.isNull("desLibrary")) { JSONObject desLibraryObj = dataObj.getJSONObject("desLibrary"); if (desLibraryObj.has("components") && !desLibraryObj.isNull("components")) { JSONObject componentsObj = desLibraryObj.getJSONObject("components"); if (componentsObj.has("nodes") && !componentsObj.isNull("nodes")) { resultArr = componentsObj.getJSONArray("nodes"); } } } }
内容的提问来源于stack exchange,提问作者Solaiappan Elangovan

