如何通过虚拟客户端(ApacheHttpClient)向odata发送带有空白的请求参数



我想通过odata与$filter参数($filter=cast(Договор_Key,'Catalog_Договоры') eq guid'<UUID>')发送GET请求。如您所见,url中有两个空白。假装在日志显示url: https://mysite/odata/standard.odata/Document_%D0%A1%D1%87%D0%B5%D1%82%D0%A4%D0%B0%D0%BA%D1%82%D1%83%D1%80%D0%B0%D0%92%D1%8B%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9?%24format=json%3Bodata%3Dnometadata& % 24 inlinecount = allpages& % 24过滤器=铸D0 % % 28% 94% d0%be % D0%B3%D0%BE % D0%B2%D0%BE % D1 % 80 _key % 2 c % 27 catalog_ d0%be % D0 % 94% % D0%B3%D0%BE %开头D0%B2%D0%BE D1 % 8 b % % D1 % 80% 27% 29% 20 eq % 20 guid % 27 e1004688 - 92 - d7 - 11 - ed - 8 f65 - 005056992589 % 27

这个URL在firefox上可以正常工作,但是我的java应用程序抛出了一个错误:

feign.FeignException$InternalServerError: [500 Internal server error] during [GET] to [https://mysite/odata/standard.odata/Document_%D0%A1%D1%87%D0%B5%D1%82%D0%A4%D0%B0%D0%BA%D1%82%D1%83%D1%80%D0%B0%D0%92%D1%8B%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9?%24format=json%3Bodata%3Dnometadata&%24inlinecount=allpages&%24filter=cast%28%D0%94%D0%BE%D0%B3%D0%BE%D0%B2%D0%BE%D1%80_Key%2C%27Catalog_%D0%94%D0%BE%D0%B3%D0%BE%D0%B2%D0%BE%D1%80%D1%8B%27%29%20eq%20guid%27e1004688-92d7-11ed-8f65-005056992589%27] [OdataClient#getTaxInvoiceByContractId(String,String,String,String)]: [{
"odata.error": {
"code": "-1",
"message": {
"lang": "ru",
"value": "{(3, 2)}: Операция не разрешена в предложении "ГДЕ"n<<?>>CAST( sourceAlias.Договор AS Catalog.Договоры )"
}
}

}]

我不明白,为什么它在浏览器上工作,而不是在我的应用程序。

我用:SpringBoot 3.0.5,spring-cloud-starter-openfeign 4.0.1,io.github.openfeign.feign-httpclient 12.2.

配置类。我已经自定义了我的Feign客户端来禁用SSL,因此添加了io.github.openfeign.feign-httpclient 12.2依赖项。

import feign.Feign;
import feign.Logger;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
@Configuration
public class FeignClientConfig {
@Bean
public ApacheHttpClient apacheFeignClient() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}}, new SecureRandom());
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
return new ApacheHttpClient(HttpClientBuilder.create()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(hostnameVerifier)
.build());
}
@Bean
public Feign.Builder feignBuilder() throws Exception {
return Feign.builder()
.client(apacheFeignClient());
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}

我的虚拟客户端类:

package ru.rcitsakha.bitrixapi.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import ru.rcitsakha.bitrixapi.config.FeignClientConfig;
@FeignClient(name = "odataClient", url = "${odata.url}", configuration = FeignClientConfig.class)
public interface OdataClient {
// other endpoints with cirillic alphabet, but without whitespaces works fine
// these endpoints with whitespaces doesn't work
@GetMapping("/Document_СчетФактураВыданный")
String getTaxInvoiceByContractId(
@RequestHeader("Authorization") String token,
@RequestParam("$format") String format,
@RequestParam("$inlinecount") String inlineCount,
@RequestParam("$filter") String contractId);
}

生成有问题的请求($filter)的存储库类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import ru.rcitsakha.bitrixapi.client.OdataClient;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Repository
@RequiredArgsConstructor
@Log4j2
public class OdataRepository {
private final ObjectMapper mapper;
private final OdataClient odataClient;
@Value("${odata.responseFormat}")
private String responseFormat;
@Value("${odata.inlineCount}")
private String inlineCount;
@Value("${odata.auth_token}")
private String token;
public JsonNode getTaxInvoiceByContractId(String contractId) {
JsonNode node = null;
String filterRequest = "cast(Договор_Key,'Catalog_Договоры') eq guid'" + contractId + "'";
String responseData = odataClient.getTaxInvoiceByContractId(token, responseFormat, inlineCount, filterRequest);
try {
node = mapper.readTree(responseData);
} catch (JsonProcessingException e) {
log.error("Object mapper could not read tree");
e.printStackTrace();
}
return node;
}
}

PS:那些建议,对我没有帮助:

  1. URLEncoder.encode
  2. replaceAll";";通过"% 20">
  3. replaceAll ";通过"% 20">
  4. @QueryMap代替@RequestParam("$filter")字符串contractId

将ApacheHttpClient替换为OkHttpClientio.github.openfeign:feign-okhttp:12.2和Builder替换为com.squareup.okhttp3:okhttp:4.10.0有帮助。

import feign.Feign;
import feign.Logger;
import feign.okhttp.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
@Configuration
public class FeignClientConfig {
@Bean
public OkHttpClient okHttpClient() throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
Builder builder = new Builder();
builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -> true);
return new feign.okhttp.OkHttpClient(builder.build());
}
@Bean
public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> converters) throws Exception {
return Feign.builder()
.client(okHttpClient());
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.HEADERS;
}
}

我认为ApacheHttpClient内部用'+'代替空格。

最新更新