好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Feign远程调用传递对象参数并返回自定义分页数据的过程解析

Feign介绍

Feign是Netflix公司开源的轻量级rest客户端,使用Feign可以非常方便的实现Http 客户端。Spring Cloud引入Feign并且集成了Ribbon实现客户端负载均衡调用。

Feign测试

1.在yml文件里面增加了配置信息

?

1

2

3

feign:

   httpclient:

    enabled: true

2.在客户端pom.xml文件中引入的依赖(消费者端)

?

1

2

3

4

5

6

7

8

9

10

11

12

<!-- 配置feign 发送请求使用 httpclient,而不是java原生 -->

        < dependency >

            < groupId >org.apache.httpcomponents</ groupId >

            < artifactId >httpclient</ artifactId >

        </ dependency >

       

  <!-- 此处不使用Apache HttpClient 的httpclient依赖,一定要使用下面这个依赖,因为我们要返回的是POJO类-->

        < dependency >

            < groupId >io.github.openfeign</ groupId >

            < artifactId >feign-httpclient</ artifactId >

            < version >10.1.0</ version >

        </ dependency >

此处注意

此处依赖为什么使用io.github.openfeign的httpclient,而不使用Apache 的HttpClient替换Feign原生httpclient。

看了很多文章,都是说引用这个依赖:

?

1

2

3

4

5

6

<!-- 使用Apache HttpClient替换Feign原生httpclient-->

<!--        <dependency>-->

<!--            <groupId>com.netflix.feign</groupId>-->

<!--            <artifactId>feign-httpclient</artifactId>-->

<!--            <version>8.16.1</version>-->

<!--        </dependency>-->

但是不知道哪里的问题,在获取返回结果时一直报错:

Caused by: java.lang.NoSuchMethodError: feign.Response.create(ILjava/lang/String;Ljava/util/Map;Lfeign/Response$Body;)Lfeign/Response;

查看源码得知,openfeign在接受返回值时调用的不是httpclient的feign-core包的代码而是调用的本身的feign-core的代码,而本身的feign-core包中的Response类没有create方法。两个feign-core包中的Retryer接口不一致导致的,openfeign的feign-core版本为10.1.0 httpclient的版本为8.16.1。

找了半天问题,最后就把httpclient的依赖换成代码块中的依赖就OK了。

3.服务调用端接口为

此处使用POST请求,第6步有解释。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

@Slf4j

@RequestMapping ( "/list" )

@RestController

public class WebQueryListController {   

    

    @Autowired

    private TourismListService listService;

   

    @PostMapping ( "/ad/allByQuery" )

    public ApiResult<Page<TourismAd>> allByQuery( @RequestBody TourismAdQuery adQuery){

        ApiResult<Page<TourismAd>>  pageApiResult =  listService.selectAllAdByQuery(adQuery);

        return pageApiResult;

    }

我的TourismAdQuery类继承了Page类(似乎没有影响)

?

1

2

3

4

5

6

7

8

9

@Data

public class TourismAdQuery extends   Page<TourismAd> {

 

    /**

      * 标题

      */

    private String title;

      。。。。。。。

}

4.服务调用端Service代码

此处@PostMapping地址为服务端提供的api接口地址

?

1

2

3

4

5

6

7

8

9

10

@FeignClient (name = "fisher-back-service" , fallback = TourismListFallback. class , configuration = FeignConfig. class )

public interface TourismListService {

 

    /**

      * 分页查询广告根据查询条件

      * @param adQuery

      * @return

      */

    @PostMapping (value = "/ad/get/allByQuery" )

    ApiResult<Page<TourismAd>> selectAllAdByQuery(TourismAdQuery adQuery);

5.服务调用端Fallback为

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@Slf4j

@Service

public class TourismListFallback implements TourismListService {

 

    /**

      * 分页查询广告根据查询条件

      *

      * @param adQuery

      * @return

      */

    @Override

    public ApiResult<Page<TourismAd>> selectAllAdByQuery(TourismAdQuery adQuery) {

        log.error( "调用 selectAllAdByQuery 方法异常,参数:{}" , adQuery);

        return null ;

    }

6.服务提供端代码为

此处传进来的参数是一个POJO类,如果不使用@RequestBody注解 的话,feign远程调用时参数是无法被接收到的。

虽然获取数据时,大多数使用 Get请求方法,但是GET方法无法接收@RequestBody参数体。

所以只好改GET请求为POST请求。

?

1

2

3

4

5

6

7

8

9

10

11

12

@RestController

@RequestMapping ( "/ad" )

public class TourismAdController extends BaseController<TourismAdService, TourismAd, Integer> {

 

    @Autowired

    private TourismAdService adService;

 

    @ApiOperation (value = "分页查询广告根据查询条件" , notes = "分页查询广告根据查询条件" , httpMethod = "POST" )

    @PostMapping ( "/get/allByQuery" )

    public ApiResult<Page<TourismAd>> allByQuery( @RequestBody TourismAdQuery adQuery){

        return adService.selectAllByQuery(adQuery);

    }

7.测试

调用接口http://localhost:9009/list/ad/allByQuery 传递json格式参数即可:

?

1

2

3

4

5

{

   "address" : "" ,

   "title" : "广告位1" ,

   "size" : 6

}

成功分页获取数据 自定义的返回类型数据:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

{

   "data" : {

    "records" : [

      {

        "id" : 1 ,

        "title" : "广告位1" ,

        "description" : "招商" ,

        "sort" : 0 ,

        "datetime" : "2019-09-26 17:46:50" ,

        "updatetime" : "2019-09-26 17:46:50" ,

        "peopleid" : 0 ,

        "display" : 0 ,

        "content" : "04004" ,

        "file" : "444//44.jpg" ,

        "leaseperson" : "找找" ,

        "address" : "杭州市" ,

        "idcard" : "1154465656656" ,

        "phone" : "131654799"

      }

    ],

    "total" : 1 ,

    "size" : 6 ,

    "current" : 1 ,

    "searchCount" : true ,

    "pages" : 1

   },

   "code" : 200 ,

   "message" : "分页获取成功"

}

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

接口定义:

?

1

2

3

4

@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);

服务消费方调用报错:

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、将分页属性直接通过入参传递,接口定义如下:

?

1

2

3

4

@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中(通过继承实现):

?

1

2

3

4

5

6

7

8

@Data

@NoArgsConstructor

@ApiModel (value = "查询会话" )

public class SessionsQo extends PageableParam {

@ApiParam (value = "会话id列表" )

private List<Long> sessionIdIn = Lists.newArrayList();

...

}

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

服务提供方定义注解:

?

1

2

3

4

@Target (ElementType.PARAMETER)

@Retention (RetentionPolicy.RUNTIME)

public @interface PageableParam {

}

服务提供方定义接口:

?

1

2

3

4

@ApiOperation (value = "分页查询会话" )

@PostMapping (Routes.SESSIONS_QUERY)

JsonResult<Pagination<SessionInfo>> querySessions( @RequestBody @Valid SessionsQo qo,

@PageableParam @SpringQueryMap Pageable pageable);

服务消费方定义processor:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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

     }

}

服务消费方自定义PageableUtil:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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原因:主要是因为Feign对QueryMap类型参数的序列化和反序列化的言七墨方式与Sort.Order的不兼容,导致排序失效。

服务消费方调用方式:

?

1

2

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,

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/qq_36068521/article/details/102565751

查看更多关于Feign远程调用传递对象参数并返回自定义分页数据的过程解析的详细内容...

  阅读:31次