springboot学习(五十二) springboot中使用retrofit实现调用本地接口访问远程服务

本文详细介绍了如何在SpringBoot项目中使用Retrofit进行远程接口调用,涉及引入库、编写远程和本地接口、配置拦截器以及自定义注解。通过实例演示了GET、POST、DELETE等操作及文件上传下载,并展示了如何利用拦截器进行统一处理。
摘要由CSDN通过智能技术生成


前言

上一篇文章我们学习了使用openfeign来调用本地接口访问远程服务,这篇文章我们来学习另一种方式retrofit,这个工具在安卓中用的比较多,不过在springboot中使用也是一样啦。

一、retrofit是什么?

okhttp是一款由square公司开源的java版本http客户端工具。square公司还开源了基于okhttp进一步封装的retrofit工具,用来支持通过接口的方式发起http请求。 retrofit-spring-boot-starter实现了Retrofit与SpringBoot框架快速整合,并且支持了部分功能增强,从而极大的简化spring-boot项目下http接口调用开发。

二、使用步骤

1.引入库

此实例为gradle版本,如果是maven请切换为maven的写法

    api group: 'com.github.lianjiatech', name: 'retrofit-spring-boot-starter', version: "2.2.14"

2.编写远程测试接口

编写远程服务实例,以下实例包含了多种情况,请求方式包括GET、POST、DELETE、PUT,传递函数方式包括RequestParam、PathVariable、RequestBody、RequestHeader、文件上传、文件下载。

package com.iscas.biz.test.retrofit;

import com.iscas.base.biz.util.SpringUtils;
import com.iscas.common.web.tools.file.FileDownloadUtils;
import com.iscas.templet.common.BaseController;
import com.iscas.templet.common.ResponseEntity;
import lombok.Data;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;

/**
 *
 *  测试远程被调用的retrofit接口
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/08/09 9:26
 * @since jdk1.8
 */
@RestController
@RequestMapping("/test/retrofit/remote")
public class RemoteRetrofitController extends BaseController {

    @GetMapping("/t1")
    public ResponseEntity t1(@RequestParam("name") String name) {
        ResponseEntity response = getResponse();
        response.setValue(name);
        return response;
    }

    @DeleteMapping("/t2/{name}")
    public ResponseEntity t2(@PathVariable("name") String name) {
        ResponseEntity response = getResponse();
        response.setValue(name);
        return response;
    }

    @PostMapping("/t3")
    public ResponseEntity t3(@RequestBody Map<String, Object> params) {
        ResponseEntity response = getResponse();
        response.setValue(params);
        return response;
    }

    @PutMapping("/t4")
    public ResponseEntity t4(@RequestBody Map<String, Object> params) {
        ResponseEntity response = getResponse();
        response.setValue(params);
        return response;
    }

    @PostMapping("/t5")
    public ResponseEntity t5(@RequestParam Map<String, Object> params) {
        ResponseEntity response = getResponse();
        response.setValue(params);
        return response;
    }

    @PostMapping("/t6")
    public ResponseEntity t6(RetrofitTestModel model) {
        ResponseEntity response = getResponse();
        response.setValue(model);
        return response;
    }

    @PostMapping("/t7")
    public ResponseEntity t7(String name) {
        ResponseEntity response = getResponse();
        response.setValue(name);
        return response;
    }

    @PostMapping("/t8")
    public ResponseEntity t8(@RequestHeader("name") String name) {
        ResponseEntity response = getResponse();
        response.setValue(name);
        return response;
    }

    @PostMapping("/t9")
    public ResponseEntity t9(@RequestHeader("name") String name, @RequestHeader("token") String token) {
        ResponseEntity response = getResponse();
        response.setValue(name + ";" + token);
        return response;
    }

    @PostMapping("/t10")
    public ResponseEntity t10(MultipartFile file1) {
        ResponseEntity response = getResponse();
        response.setValue(file1.toString());
        return response;
    }

    @PostMapping("/t11")
    public ResponseEntity t11() throws ServletException, IOException {
        HttpServletRequest request = SpringUtils.getRequest();
        Collection<Part> parts = request.getParts();
        if (parts != null) {
            for (Part part : parts) {
                InputStream inputStream = part.getInputStream();
            }
        }
        ResponseEntity response = getResponse();
        response.setValue(parts.toString());
        return response;
    }

    @PostMapping("/t12")
    public void t12() throws Exception {
        FileDownloadUtils.downFile(SpringUtils.getRequest(), SpringUtils.getResponse(),
                "D:/test-sp/aaa.html", "aaa.html");
    }

    @Data
    public static class RetrofitTestModel {
        private String name;
    }

}

3.编写本地接口和测试接口

3.1. retrofit的配置信息

在application.properties中添加retrofit的配置项

###############retrofit配置###############
#连接池相关配置
retrofit.pool.test.max-idle-connection=3
retrofit.pool.test.keep-alive-second=100
#日志打印拦截器配置,可以继承BaseLoggingInterceptor实现自己的日志记录方式
retrofit.logging-interceptor=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
#异常格式化处理,可以继承BaseHttpExceptionMessageFormatter,实现自己的异常格式化
retrofit。http-exception-message-formatter=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter

3.2. 本地和测试接口

新建接口,添加@RetrofitClient注解,配置baseUrl和poolNamepoolName对应上一步的配置
先忽略@Interceptor@Sign注解,后面再说明。整体Api文件如下,对应了步骤2的远程测试接口,各种传参方式可见接口的注释

package com.iscas.biz.test.retrofit;

import com.github.lianjiatech.retrofit.spring.boot.annotation.Intercept;
import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient;
import com.iscas.templet.common.ResponseEntity;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import org.springframework.web.bind.annotation.RequestMapping;
import retrofit2.Call;
import retrofit2.http.*;

import java.util.List;
import java.util.Map;

/**
 * 参考 https://blog.csdn.net/why_still_confused/article/details
 *
 *  /108041657
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/8/8 18:25
 * @since jdk1.8
 */
@RetrofitClient(baseUrl = "http://localhost:7901/demo", poolName = "test")
@Intercept(handler = SimpleInterceptor.class, include = {"/demo/test/**"}) //添加拦截器
@Sign(accessKeyId = "xxxx", accessKeySecret = "yyyyy", exclude = {"/demo/test/xxxx"})
public interface RetrofitApi {

    /**url中参数 ?name=xxx*/
    @GET("test/retrofit/remote/t1")
    ResponseEntity t1(@Query("name") String name);

    /**路径中传参 /{}*/
    @DELETE("test/retrofit/remote/t2/{name}")
    ResponseEntity t2(@Path("name") String name);

    /**使用请求体传送json*/
    @POST("test/retrofit/remote/t3")
    ResponseEntity t3(@Body Map<String, Object> params);

    /**put请求使用请求体传送json*/
    @PUT("test/retrofit/remote/t4")
    ResponseEntity t4(@Body Map<String, Object> params);

    /**多个url参数使用map传递*/
    @POST("test/retrofit/remote/t5")
    ResponseEntity t5(@QueryMap Map<String, Object> params);

    /**form表单传送数据,多个参数使用map传递*/
    @FormUrlEncoded
    @POST("test/retrofit/remote/t6")
    ResponseEntity t6(@FieldMap Map<String, Object> model);

    /**form表单传送数据,多个参数一个一个传递*/
    @FormUrlEncoded
    @POST("test/retrofit/remote/t7")
    ResponseEntity t7(@Field("name") String name);

    /**header传送数据,多个参数一个一个传递*/
    @POST("test/retrofit/remote/t8")
    ResponseEntity t8(@Header("name") String name);

    /**header传送数据,多个参数一起传递*/
    @POST("test/retrofit/remote/t9")
    @Headers({"name:quanwen", "token:wgwegwe"})
    ResponseEntity t9();

    /**单个文件上传*/
    @POST("test/retrofit/remote/t10")
    @Multipart
    ResponseEntity t10(@Part MultipartBody.Part filePart);

    /**多个文件上传*/
    @POST("test/retrofit/remote/t11")
    @Multipart
    ResponseEntity t11(@Part List<MultipartBody.Part> parts);

//    /**多个文件上传*/
//    @POST("test/retrofit/remote/t11")
//    @Multipart
//    ResponseEntity t11_2(@PartMap Map<String, MultipartBody.Part> parts);

    /**文件下载*/
    @POST("test/retrofit/remote/t12")
    @Streaming
    Call<ResponseBody> t12();

}

3.3. 测试

这是测试还是使用了Http的接口

package com.iscas.biz.test.retrofit;

import com.iscas.templet.common.ResponseEntity;
import com.rabbitmq.client.GetResponse;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import retrofit2.Call;
import retrofit2.Response;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/8/8 20:58
 * @since jdk1.8
 */
@RestController
@RequestMapping("/test/retrofit")
public class RetrofitTestController {
    @Autowired
    private RetrofitApi retrofitApi;
    @GetMapping("t1")
    public ResponseEntity t1() {
        return retrofitApi.t1("quanwen");
    }

    @GetMapping("t2")
    public ResponseEntity t2() {
        return retrofitApi.t2("quanwen");
    }

    @GetMapping("t3")
    public ResponseEntity t3() {
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("name", "quanwen");
        }};
        return retrofitApi.t3(map);
    }

    @GetMapping("t4")
    public ResponseEntity t4() {
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("name", "quanwen");
        }};
        return retrofitApi.t4(map);
    }

    @GetMapping("t5")
    public ResponseEntity t5() {
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("name", "quanwen");
        }};
        return retrofitApi.t5(map);
    }

    @GetMapping("t6")
    public ResponseEntity t6() {
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("name", "quanwen");
        }};
        return retrofitApi.t6(map);
    }

    @GetMapping("t7")
    public ResponseEntity t7() {
        return retrofitApi.t7("quanwen");
    }

    @GetMapping("t8")
    public ResponseEntity t8() {
        return retrofitApi.t8("quanwen");
    }

    @GetMapping("t9")
    public ResponseEntity t9() {
        return retrofitApi.t9();
    }

    @GetMapping("t10")
    public ResponseEntity t10() {
        File file = new File("D:/test-sp/aaa.html");
        RequestBody requestBody = RequestBody.create(MediaType.parse("text/html"), file);
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file1", file.getName(), requestBody);
        return retrofitApi.t10(filePart);
    }

    @GetMapping("t11")
    public ResponseEntity t11() {
        File file1 = new File("D:/test-sp/aaa.html");
        File file2 = new File("D:/test-sp/post_download_file.html");
        RequestBody requestBody1 = RequestBody.create(MediaType.parse("text/html"), file1);
        MultipartBody.Part filePart1 = MultipartBody.Part.createFormData("file1", file1.getName(), requestBody1);
        RequestBody requestBody2 = RequestBody.create(MediaType.parse("text/html"), file2);
        MultipartBody.Part filePart2 = MultipartBody.Part.createFormData("file2", file2.getName(), requestBody2);
        return retrofitApi.t11(Arrays.asList(filePart1, filePart2));
    }

//    @GetMapping("t11_2")
//    public ResponseEntity t11_2() {
//        File file1 = new File("D:/test-sp/aaa.html");
//        File file2 = new File("D:/test-sp/post_download_file.html");
//        RequestBody requestBody1 = RequestBody.create(MediaType.parse("text/html"), file1);
//        MultipartBody.Part filePart1 = MultipartBody.Part.createFormData("file1", file1.getName(), requestBody1);
//        RequestBody requestBody2 = RequestBody.create(MediaType.parse("text/html"), file2);
//        MultipartBody.Part filePart2 = MultipartBody.Part.createFormData("file2", file2.getName(), requestBody2);
//
//        return retrofitApi.t11_2(new HashMap<String, MultipartBody.Part>(){{
//            put("file1", filePart1);
//            put("file2", filePart2);
//        }
//        });
//    }

    @GetMapping("t12")
    public ResponseEntity t12() throws IOException {
        ResponseEntity<Object> responseEntity = new ResponseEntity<>();
        Call<ResponseBody> responseBodyCall = retrofitApi.t12();
        Response<ResponseBody> res = responseBodyCall.execute();
        ResponseBody body = res.body();
        return responseEntity;
    }

}

4.编写拦截器

可是通过编写拦截器来拦截retrofit的请求,在请求中添加一些统一的处理,拦截器需要继承BasePathMatchInterceptor并重写doIntercept方法,示例如下:

package com.iscas.biz.test.retrofit;

import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/8/9 11:20
 * @since jdk1.8
 */
@Component
public class SimpleInterceptor extends BasePathMatchInterceptor {
    @Override
    protected Response doIntercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl url = request.url();
        url = url.newBuilder().addQueryParameter("time", String.valueOf(System.currentTimeMillis()))
                .build();
        Headers headers = request.headers();
        headers = headers.newBuilder().add("add-header", "lalala").build();
        request = request.newBuilder().headers(headers)
                .url(url).build();
        return chain.proceed(request);
    }
}

在接口类中添加@Interceptor注解,handler属性为上面定义的拦截器,include属性为拦截的URL,exclude为不拦截的URL

@Intercept(handler = SimpleInterceptor.class, include = {"/demo/test/**"}) //添加拦截器

5.自定义注解拦截器

注意必须添加@InterceptMark

package com.iscas.biz.test.retrofit;

import com.github.lianjiatech.retrofit.spring.boot.annotation.InterceptMark;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;

import java.lang.annotation.*;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/8/9 13:20
 * @since jdk1.8
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark //必须加这个注解
public @interface Sign {
    /**
     * 密钥key
     * 支持占位符形式配置。
     *
     * @return
     */
    String accessKeyId();

    /**
     * 密钥
     * 支持占位符形式配置。
     *
     * @return
     */
    String accessKeySecret();

    /**
     * 拦截器匹配路径
     *
     * @return
     */
    String[] include() default {"/**"};

    /**
     * 拦截器排除匹配,排除指定路径拦截
     *
     * @return
     */
    String[] exclude() default {};

    /**
     * 处理该注解的拦截器类
     * 优先从spring容器获取对应的Bean,如果获取不到,则使用反射创建一个!
     *
     * @return
     */
    Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}


在接口类上添加@Sign注解

@Sign(accessKeyId = "xxxx", accessKeySecret = "yyyyy", exclude = {"/demo/test/xxxx"})

总结

retrofit对okhttp做了封装,使用起来就像调用本地方法一样,使远程调用Http请求变得简单很多,并支持拦截器等配置,功能友好强大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值