Java HTTP请求调用URL时的编码问题排查求助
带有复杂查询参数的Java HTTP请求调用失败问题
原请求URL
https://host.com/rest/v14/commerceDocumentsSimplifiedRenewalsTransaction?q={$and:[{customerSignatureRequestSentOn_srq:{$gte: '2023-04-01'}},{customerSignatureRequestSentOn_srq:{$lte: '2023-06-04'}},{customerSigned_srq:{ $exists: false }},{renewalType_srq:{ $eq: "Amendment" }},{subType_srq:{ $eq: "Proactive" }}]}&fields=quoteNumber_srq,customerSignatureRequestSentOn_srq
原始实现代码
URL url = new URI(uri).toURL(); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); var apiUsername = "***"; var apiPassword = "***"; conn.setRequestProperty(HEADERS_AUTHORIZATION, generateBasicAuthInHeaders(apiUsername, apiPassword)); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setConnectTimeout(3000); conn.setRequestMethod("GET"); conn.connect();
遇到的问题
不编码直接使用时的错误
Exception in thread "main" java.net.URISyntaxException: Illegal character in query at index 98
仅编码查询部分后的错误
编码后的URL:
https://host.com/rest/v14/commerceDocumentsSimplifiedRenewalsTransaction?q=%7B%24and%3A%5B%7BcustomerSignatureRequestSentOn_srq%3A%7B%24gte%3A+%272023-04-01%27%7D%7D%2C%7BcustomerSignatureRequestSentOn_srq%3A%7B%24lte%3A+%272023-06-04%27%7D%7D%2C%7BcustomerSigned_srq%3A%7B+%24exists%3A+false+%7D%7D%2C%7BrenewalType_srq%3A%7B+%24eq%3A+%22Amendment%22+%7D%7D%2C%7BsubType_srq%3A%7B+%24eq%3A+%22Proactive%22+%7D%7D%5D%7D%26fields%3DquoteNumber_srq%2CcustomerSignatureRequestSentOn_srq
返回错误:
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 400 for URL:
使用Jersey UriBuilder尝试的代码
URI url = UriBuilder.fromPath("https://host.com/rest/v14/commerceDocumentsSimplifiedRenewalsTransaction") .queryParam("q", "{$and:[{customerSignatureRequestSentOn_srq:{$gte: '2023-04-01'}},{customerSignatureRequestSentOn_srq:{$lte: '2023-06-04'}},{customerSigned_srq:{ $exists: false }},{renewalType_srq:{ $eq: \"Amendment\" }},{subType_srq:{ $eq: \"Proactive\" }}]}&fields=quoteNumber_srq,customerSignatureRequestSentOn_srq") .build();
该方式同样调用失败。
解决方案
1. 正确使用Jersey UriBuilder拆分参数
问题出在把fields参数塞进了q的参数值里,需分开设置每个查询参数,UriBuilder会自动完成正确编码:
URI url = UriBuilder.fromUri("https://host.com/rest/v14/commerceDocumentsSimplifiedRenewalsTransaction") .queryParam("q", "{$and:[{customerSignatureRequestSentOn_srq:{$gte: '2023-04-01'}},{customerSignatureRequestSentOn_srq:{$lte: '2023-06-04'}},{customerSigned_srq:{ $exists: false }},{renewalType_srq:{ $eq: \"Amendment\" }},{subType_srq:{ $eq: \"Proactive\" }}]}") .queryParam("fields", "quoteNumber_srq,customerSignatureRequestSentOn_srq") .build();
2. 原生HttpsURLConnection的正确编码方式
手动编码单个参数值而非整个URL,同时移除多余的请求头:
import java.net.URLEncoder; import java.nio.charset.StandardCharsets; // 定义参数值 String qValue = "{$and:[{customerSignatureRequestSentOn_srq:{$gte: '2023-04-01'}},{customerSignatureRequestSentOn_srq:{$lte: '2023-06-04'}},{customerSigned_srq:{ $exists: false }},{renewalType_srq:{ $eq: \"Amendment\" }},{subType_srq:{ $eq: \"Proactive\" }}]}"; String fieldsValue = "quoteNumber_srq,customerSignatureRequestSentOn_srq"; // 编码参数值 String encodedQ = URLEncoder.encode(qValue, StandardCharsets.UTF_8); String encodedFields = URLEncoder.encode(fieldsValue, StandardCharsets.UTF_8); // 拼接完整URL String fullUrl = String.format("https://host.com/rest/v14/commerceDocumentsSimplifiedRenewalsTransaction?q=%s&fields=%s", encodedQ, encodedFields); // 创建连接 URL url = new URL(fullUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); var apiUsername = "***"; var apiPassword = "***"; conn.setRequestProperty(HEADERS_AUTHORIZATION, generateBasicAuthInHeaders(apiUsername, apiPassword)); conn.setConnectTimeout(3000); conn.setRequestMethod("GET"); conn.connect();
注意:GET请求无需设置Content-Type请求头,该头用于POST请求的请求体格式,多余设置可能导致服务器解析异常。
3. 额外检查项
- 验证
generateBasicAuthInHeaders生成的Basic Auth格式是否正确(应为Basic base64(username:password)) - 检查查询参数中的JSON格式是否符合服务器要求(部分服务器对JSON内的多余空格敏感)
内容的提问来源于stack exchange,提问作者MI Haque
相关产品推荐
相关产品推荐

