You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Feign(ApacheHttpClient)向OData发送含空格的请求参数

OData请求含$filter参数时Feign调用报500错误问题

问题描述

需要通过OData发送带$filter参数的GET请求,参数内容为$filter=cast(Договор_Key,'Catalog_Договоры') eq guid'<UUID>',该参数包含两个空格。Feign日志输出的URL在Firefox中可正常请求,但Java应用调用时抛出500内部服务器错误。

报错信息

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)]: [{"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)

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

Feign客户端类

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 {    
    // 其他含西里尔字母但无空格的端点可正常工作    
    // 这些含空格的端点无法工作    
    @GetMapping("/Document_СчетФактураВыданный")    
    String getTaxInvoiceByContractId(@RequestHeader("Authorization") String token, 
                                     @RequestParam("$format") String format, 
                                     @RequestParam("$inlinecount") String inlineCount, 
                                     @RequestParam("$filter") String contractId);
}

生成$filter参数的Repository类

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

已尝试但无效的方法

  • 使用URLEncoder.encode对参数进行编码
  • 将参数中的空格手动替换为%20
  • 通过RequestInterceptor拦截请求并将空格替换为%20
  • 使用@QueryMap替代@RequestParam("$filter") String contractId传递参数

内容的提问来源于stack exchange,提问作者Zakhar Borisov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 00:07:04