Java 开发 / SpringCloud

使用 Feign 调用分页接口报错:Method has too many Body parameters

言七墨 · 11月8日 · 2020年 · · 4627次已读

一、背景

  • 接口定义:
@ApiOperation(value = "分页查询会话")
@PostMapping(Routes.SESSIONS_QUERY)
JsonResult<Pagination<SessionInfo>> querySessions(@RequestBody @Valid SessionsQo qo,
                   @PageableDefault(size = 20, sort = "id", direction = Sort.Direction.DESC) Pageable pageable);
https://qimok.cn
  • 服务消费方调用报错:
Method has too many Body parameters: public abstract com.xingren.common.data.JsonResult com.xingren.xxx.yyy.contract.api.controller.ISessionController.querySessions(com.xingren.xxx.yyy.contract.qo.SessionsQo,org.springframework.data.domain.Pageable)

二、解决

通过搜索、调研,目前有三种解决方法:言七墨

1、将分页属性直接通过入参传递,接口定义如下:言七墨

@ApiOperation(value = "分页查询会话")
@PostMapping(Routes.SESSIONS_QUERY)
JsonResult<Pagination<SessionInfo>> querySessions(@RequestBody @Valid SessionsQo qo,
       @RequestParam("page") Integer page, @RequestParam("size") Integer size, @RequestParam("sort") Sort sort);

2、将分页对象冗余在Qo中(通过继承实现):

@Data
@NoArgsConstructor
@ApiModel(value = "查询会话")
public class SessionsQo extends PageableParam {

    @ApiParam(value = "会话id列表")
    private List<Long> sessionIdIn = Lists.newArrayList();
    
    ...
}

3、通过注解传递(参考:Issue):

  1. 服务提供方定义注解言七墨
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PageableParam {
}
  1. 服务提供方定义接口:
@ApiOperation(value = "分页查询会话")
@PostMapping(Routes.SESSIONS_QUERY)
JsonResult<Pagination<SessionInfo>> querySessions(@RequestBody @Valid SessionsQo qo,
                                                  @PageableParam @SpringQueryMap Pageable pageable);
  1. 服务消费方定义processor
@Bean
public PageableParamProcessor pageableParamProcessor() {
    return new PageableParamProcessor();
}

public static class PageableParamProcessor implements AnnotatedParameterProcessor {

    private static final Class<PageableParam> ANNOTATION = PageableParam.class;

    @Override
    public Class<? extends Annotation> getAnnotationType() {
        return ANNOTATION;
    }

    @Override
    public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
        int parameterIndex = context.getParameterIndex();
        MethodMetadata data = context.getMethodMetadata();
        data.queryMapIndex(parameterIndex);
        return true;
    }

}
  1. 服务消费方自定义PageableUtil
https://qimok.cn
public class PageableUtil extends PageRequest implements Map<String, Object> {

    public static final String PAGE = "page";
    public static final String SIZE = "size";
    public static final String SORT = "sort";

    @Delegate
    protected Map<String, Object> delegate = Maps.newHashMap();

    public PageableUtil(int page, int size, Sort sort) {
        super(page, size, sort);
        delegate.put(PAGE, page);
        delegate.put(SIZE, size);
        if (Objects.nonNull(sort)) {
            delegate.put(SORT, sort.toString().replace(": ", ","));
        }
    }

    public PageableUtil(int page, int size) {
        super(page, size);
        delegate.put(PAGE, page);
        delegate.put(SIZE, size);
    }

}
  • 定义PageableUtil原因:主要是因为FeignQueryMap类型参数的序列化和反序列化的方式与Sort.Order的不兼容,导致排序失效。
  1. 服务消费方调用方式:
SessionsQo qo = SessionsQo.builder().sessionIdIn(Collections.singletonList(20L)).build();
JsonResult<Pagination<SessionInfo>> pageInfo = sessionContract.querySessions(qo, new PageableUtil(0, 5, new Sort(Sort.Direction.DESC, "session_id")));
2 条回应
  1. 匿名2021-5-27 · 17:50

    可以仿你的模板吗

    • 匿名2021-8-11 · 15:26

      bx