如何通过Feign客户端(ApacheHttpClient)将带有空格的请求参数发送到OData。

huangapple go评论73阅读模式
英文:

How to send request parameters with whitespace through a feign client (ApacheHttpClient) to odata

问题

你的Java应用程序中出现了一个问题,与URL中的空格有关。这个问题可能与Feign或URL编码有关。我建议你尝试以下步骤来解决问题:

  1. URL编码:确保URL中的参数经过正确的URL编码。你已经尝试过URLEncoder.encode,但如果没有起作用,可以尝试使用UriComponentsBuilder或类似的方法来构建URL,以确保参数被正确编码。

  2. 替换空格:尝试将空格替换为"%20",而不是" "。确保在构建URL时进行替换。

  3. Feign配置:检查Feign的配置是否正确,特别是在处理URL中的特殊字符时。有时候,Feign需要额外的配置来处理某些字符。

  4. 日志:在Java应用程序中启用更详细的日志,以便更好地理解错误发生的位置。这可以帮助你找到问题所在。

请确保你的Feign配置正确,URL参数被正确编码,然后尝试重新运行应用程序,看看是否仍然出现错误。

英文:

I want to send GET request via odata with $filter parameter ($filter=cast(Договор_Key,'Catalog_Договоры') eq guid'<UUID>'). As you see, there are two whitespaces in the url.
On logs feign shows 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&%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>

This URL works properly on firefox, but my java application throws an error:

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&amp;%24inlinecount=allpages&amp;%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)]: [{
&quot;odata.error&quot;: {
&quot;code&quot;: &quot;-1&quot;,
&quot;message&quot;: {
&quot;lang&quot;: &quot;ru&quot;,
&quot;value&quot;: &quot;{(3, 2)}: Операция не разрешена в предложении \&quot;ГДЕ\&quot;\n&lt;&lt;?&gt;&gt;CAST( sourceAlias.Договор AS Catalog.Договоры )&quot;
}
}

}]

I don't understand, why it works on browser, but not in my application.

I'm using:
SpringBoot 3.0.5, spring-cloud-starter-openfeign 4.0.1, io.github.openfeign.feign-httpclient 12.2.

Feign config class. I've customized my Feign client to disable SSL, therefore io.github.openfeign.feign-httpclient 12.2 dependency was added.

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(&quot;TLS&quot;);
        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;
    }
}

My feign client class:

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 = &quot;odataClient&quot;, url = &quot;${odata.url}&quot;, configuration = FeignClientConfig.class)
public interface OdataClient {

    // other endpoints with cirillic alphabet, but without whitespaces works fine

    // these endpoints with whitespaces doesn&#39;t work
    @GetMapping(&quot;/Document_СчетФактураВыданный&quot;)
    String getTaxInvoiceByContractId(
            @RequestHeader(&quot;Authorization&quot;) String token,
            @RequestParam(&quot;$format&quot;) String format,
            @RequestParam(&quot;$inlinecount&quot;) String inlineCount,
            @RequestParam(&quot;$filter&quot;) String contractId);
}

My repository class where the problematic request ($filter) is formed

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(&quot;${odata.responseFormat}&quot;)
    private String responseFormat;
    @Value(&quot;${odata.inlineCount}&quot;)
    private String inlineCount;
    @Value(&quot;${odata.auth_token}&quot;)
    private String token;

    public JsonNode getTaxInvoiceByContractId(String contractId) {
        JsonNode node = null;
        String filterRequest = &quot;cast(Договор_Key,&#39;Catalog_Договоры&#39;) eq guid&#39;&quot; + contractId + &quot;&#39;&quot;;
        String responseData = odataClient.getTaxInvoiceByContractId(token, responseFormat, inlineCount, filterRequest);
        try {
            node = mapper.readTree(responseData);
        } catch (JsonProcessingException e) {
            log.error(&quot;Object mapper could not read tree&quot;);
            e.printStackTrace();
        }

        return node;
    }
}

PS:
Advices, that didn't help me:

  1. URLEncoder.encode
  2. replaceAll " " by "%20"
  3. RequestInterceptor that replaceAll " " by "%20"
  4. @QueryMap instead of @RequestParam("$filter") String contractId

答案1

得分: 0

Replacing ApacheHttpClient with OkHttpClient io.github.openfeign:feign-okhttp:12.2 and Builder with com.squareup.okhttp3:okhttp:4.10.0 helped.

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;
    }
}

I think ApacheHttpClient internally replaces spaces with ''+''.

英文:

Replacing ApacheHttpClient with OkHttpClient io.github.openfeign:feign-okhttp:12.2 and Builder with com.squareup.okhttp3:okhttp:4.10.0 helped.

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(&quot;TLS&quot;);
sslContext.init(null, trustAllCerts, new SecureRandom());
Builder builder = new Builder();
builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -&gt; true);
return new feign.okhttp.OkHttpClient(builder.build());
}
@Bean
public Feign.Builder feignBuilder(ObjectFactory&lt;HttpMessageConverters&gt; converters) throws Exception {
return Feign.builder()
.client(okHttpClient());
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.HEADERS;
}
}

I think ApacheHttpClient internally replaces spaces with '+'.

huangapple
  • 本文由 发表于 2023年4月4日 09:01:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924756.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定