当前位置:网站首页>Feign report 400 processing

Feign report 400 processing

2022-04-23 16:49:00 InfoQ

There is an interface like this
getXxByIds(String Ids)
 id use ',' Separate , After running for a period of time, I reported 400.

Wrong content

feign.FeignException$BadRequest: [400] during [POST] to [http...

Problem recurrence

To simulate the problem

Service interface

@Slf4j
@RestController
public class SkyController {

 @GetMapping("/sky")
 public String sky (@RequestParam(required = false) String name) {
 log.info(name);
 return name;
 }

 @PostMapping("/deliver")
 public String deliver (String packageBox) {
 log.info(packageBox);
 return packageBox;
 }


 @PostMapping("/feignPost")
 public String feignPost(@RequestParam(name = "name") String name){
 log.info(name);
 return name;
 }


 @PostMapping("/feignBody")
 public String feignBody(@RequestBody String name){
 log.info(name);
 return name;
 }


}

Build another service   establish feignClinet,  The sample service joins the gateway , Service name used , You can use it directly url

//  gateway   The service name
@FeignClient(value = "paw-dogs-sky-service")
public interface SkyFeignClient {

 @PostMapping("/deliver")
 String deliver (@RequestParam(name = "packageBox") String packageBox);

 @PostMapping("/feignPost")
 String feignPost(@RequestParam(name = "name") String name);

 @PostMapping("/feignBody")
 String feignBody(@RequestBody String name);
}

Writing test classes

@Slf4j
@SpringBootTest
class SkyFeignClientTest {


 @Autowired
 SkyFeignClient skyFeignClient;

 @Test
 void deliver () {
 String param = RandomUtil.randomString(10*1024);
 log.info(param);
 String result = skyFeignClient.deliver(param);
 log.info(result);

 }

 @Test
 void feignPost () {
 String param = RandomUtil.randomString(10*1024);
 log.info(param);
 String result = skyFeignClient.feignPost(param);
 log.info(result);
 }

 @Test
 void feignBody () {
 String param = RandomUtil.randomString(1*1024);
 log.info(param);
 String result = skyFeignClient.feignBody(param);
 log.info(result);
 }
}

Running tests found  param large  get、post form-url  request was aborted ,requestBody Mode request succeeded .

null
function post form-url  request   Will find post The request is also sent url Splicing method
use postman Direct access to service testing
Through the length of splicing url The visit to fail

null
adopt  form-url Mode access succeeded

null
The problem is url The length limit of

url Length limit

  • browser url Length limit
  • Server length limit   Such as tomcat Limit , nginx url Limit
  • SpringBoot  Project length limit  
    max-http-header-size
      Default 8k, Change the service provided sky Configuration of 100*1024 (100k) , Retry the normal service call .
  • DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8)

 server:
 port: 8080
 max-http-header-size: 102400

Source code analysis

From exception class SynchronousMethodHandler Starting with debug track

Build the requested class  
RequestTemplate
  Corresponding query request   Use an ordered linked list to store parameters  
Map<String, QueryTemplate> queries = new LinkedHashMap<>()
  Form like  key-->value 
packageBox--> packageBox={packageBox}

SpringMvcContract
  Handle annotations

Annotations on the class
processAnnotationOnClass

Comments on methods
processAnnotationOnMethod

Comments on parameters  
processAnnotationsOnParameter

AnnotatedParameterProcessor
  Implementation class of  
RequestParamParameterProcessor
  Yes query Parameters are encapsulated

public boolean processArgument(AnnotatedParameterContext context,
 Annotation annotation, Method method) {
 int parameterIndex = context.getParameterIndex();
 Class<?> parameterType = method.getParameterTypes()[parameterIndex];
 MethodMetadata data = context.getMethodMetadata();

 if (Map.class.isAssignableFrom(parameterType)) {
 checkState(data.queryMapIndex() == null,
 &quot;Query map can only be present once.&quot;);
 data.queryMapIndex(parameterIndex);

 return true;
 }

 RequestParam requestParam = ANNOTATION.cast(annotation);
 String name = requestParam.value();
 checkState(emptyToNull(name) != null,
 &quot;RequestParam.value() was empty on parameter %s&quot;, parameterIndex);
 context.setParameterName(name);

 //  Encapsulate parameters
 Collection<String> query = context.setTemplateParameter(name,
 data.template().queries().get(name));
 data.template().query(name, query);
 return true;
 }

SynchronousMethodHandler
 invoke  Method  
executeAndDecode(template, options)
  perform http Request and decode .

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
 //  Build request
 Request request = targetRequest(template);

 if (logLevel != Logger.Level.NONE) {
 logger.logRequest(metadata.configKey(), logLevel, request);
 }

 Response response;
 long start = System.nanoTime();
 try {
 //  perform http request
 response = client.execute(request, options);
 ...
 }

structure Request request , adopt client visit http service . Realized client Yes
Default
 
LoadBalancerFeignClient
 
FeignBlockingLoadBalancerClient

solve

1.  Increase the of service providers header Parameters ( More micro services   Not very suitable ) 
2.  Change it to requestBody Call the service

summary

​ feign By parsing the interface class 、 Method 、 Comments on parameters , adopt
RequestTemplate
 @RequestParam With Url Splicing method , To build the
Request
request , adopt Client visit http service . For longer parameters, change to RequestBody Method to call the service .

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231645272078.html