SpringBoot 使用 Feign进行远程调用、文件上传等操作

  • Post author:
  • Post category:其他




JAVA 项目中接口调用怎么做?



1 Httpclient

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。

HttpClient 相比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。



2. Okhttp

一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代 HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持多种协议(HTTP/2 和 SPDY)。



3. HttpURLConnection

HttpURLConnection 是

Java

的标准类,它继承自 URLConnection,可用于向指定网站发送 GET 请求、POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。



4.RestTemplate

RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。



5. Feign

Feign 是一个声明式的 REST 客户端,它能让 REST 调用更加简单。Feign 供了 HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。

而 Feign 则会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。


Spring Cloud

对 Feign 进行了封装,使其支持 SpringMVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。



在Spring Boot 中集成Feign

1 引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.1.0</version>
</dependency>

2 通过@EnableFeignClients开启 feign 功能

@EnableFeignClients
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3 编写 feign接口进行远程调用

//${}可以应用application.yml内的配置,path会拼接到最后面
@FeignClient(value = "feignInterface",url="${test.url}/test",path = "/service",configuration = GlobalFeignConfig.class)
public interface FeignInterface {
	
    //请求 ${test.url}/test/service/hello 接口
    @GetMapping("/hello")
    public String getNameByParam( @RequestParam String userName );
}



Feign配置类

@FeignClient内的configuration可以对 feign请求进行先关配置

public class GlobalFeignConfig implements RequestInterceptor {


    @Bean
    public OkHttpInterceptor okHttpInterceptor() {
        return new OkHttpInterceptor();
    }

    @Bean
    public okhttp3.OkHttpClient okHttpClient() {
        return new okhttp3.OkHttpClient.Builder()
                .readTimeout(60, TimeUnit.SECONDS)
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool())
                .addInterceptor(okHttpInterceptor())
                .build();
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    @Bean
    public Decoder feignDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
    }

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    @Bean
    public ErrorDecoder errorDecoder() {
        return new ErrorDecoder.Default();
    }


    public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new PhpMappingJackson2HttpMessageConverter());
        return () -> httpMessageConverters;
    }

    class PhpMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
        PhpMappingJackson2HttpMessageConverter() {
            List<MediaType> mediaTypes = new ArrayList<>();
            mediaTypes.add(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8")); //关键
            setSupportedMediaTypes(mediaTypes);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            setObjectMapper(objectMapper);
        }
    }
    /**
     * 如果需要basic认证
     */
//    @Bean
//    public BasicAuthenticationInterceptor basicAuthenticationInterceptor(){
//        return new BasicAuthenticationInterceptor();
//    }
}

OkHttpInterceptor: 拦截器可以设置请求头等信息

@Slf4j
public class OkHttpInterceptor implements HandlerInterceptor, Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        log.debug("进入okhttp拦截器");
        Request request = chain.request();
        Request.Builder builder = request.newBuilder();

        //加上租户标识
        if(StrUtil.isNotBlank(SessionValueHolder.getCompanyCode())){
            builder.header(Constants.COMPANY_CODE, SessionValueHolder.getCompanyCode());
        }
        //加上token
        if(StrUtil.isNotBlank(SessionValueHolder.getAuthorization())){
            builder.header(Constants.AUTHORIZATION_KEY, SessionValueHolder.getAuthorization());
        }
        //加上feign调用标识
        builder.header(Constants.FEGIN_FLAG, "1");

        return chain.proceed(builder.build());
    }
}

feign 配置文件相关配置

# 链接超时时间 feignName写自己feign接口的类名
feign.client.config.feignName.connectTimeout=5000
# 读取超时时间
feign.client.config.feignName.readTimeout=5000
# 日志等级
feign.client.config.feignName.loggerLevel=full
# 重试
feign.client.config.feignName.retryer=com.example.SimpleRetryer
# 拦截器
feign.client.config.feignName.requestInterceptors[0]=com.example.FooRequestInterceptor
feign.client.config.feignName.requestInterceptors[1]=com.example.BarRequestInterceptor
# 编码器
feign.client.config.feignName.encoder=com.example.SimpleEncoder
# 解码器
feign.client.config.feignName.decoder=com.example.SimpleDecoder
# 契约
feign.client.config.feignName.contract=com.example.SimpleContract

#开启GZIP压缩,请求数据量很大的接口提高性能
#对请求数据进行压缩
feign.compression.request.enabled=true
#对响应数据进行压缩
feign.compression.response.enabled=true
#指定 请求方式 进行压缩
feign.compression.request.mime-types=text/xml,application/xml,application/json
#设置最小压缩大小,只有超过了这个大小才会进行压缩
feign.compression.request.min-request-size=2048



实战练习

通过Feign调用我们实现申明的接口进行测试:

Controller

@RestController
@RequestMapping("service")
public class ServiceController {

	//请求参数
    @GetMapping("/param")
    public String getNameByParam( String userName ){
        return userName;
    }

    //请求体
    @PostMapping("/body")
    public String getNameByBody( @RequestBody String userName ){
        return userName;
    }

    @GetMapping("/timeout/5000")
    public String timeout( @RequestParam(required = false,defaultValue = "5000") long millis ) throws Exception {
        Thread.sleep( millis );
        return "success";
    }

    //单文件
    @PostMapping(value = "/file",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String file(@RequestPart("file2") MultipartFile multipartFile){
        if( multipartFile == null ) return null;
        return multipartFile.getName();
    }

    //多文件
    @PostMapping("/files")
    public Iterable<String> files(MultipartRequest multipartRequest){
        if( multipartRequest == null ) return null;


        List<String> fileNameArray = new ArrayList();


        if( multipartRequest instanceof  StandardMultipartHttpServletRequest){
            StandardMultipartHttpServletRequest request = (StandardMultipartHttpServletRequest) multipartRequest;

            List<String> fileNameList = request.getMultiFileMap().values().stream()
                    .flatMap(o -> o.stream())
                    .map( MultipartFile::getOriginalFilename ).collect(Collectors.toList());
            return fileNameList;
        }

        Iterator<String> fileNames = multipartRequest.getFileNames();

        while(fileNames.hasNext()){
            String fileName = fileNames.next();
            fileNameArray.add( multipartRequest.getFile(fileName).getOriginalFilename() );
        }

        return fileNameArray;
    }

}



Get请求传递参数

@GetMapping("/param")
public String getNameByParam( @RequestParam String userName );

Post请求传递请求头

@PostMapping("/body")
public String getNameByBody( @RequestBody String userName );

路径参数

@GetMapping("/timeout/{millis}")
public String timeout( @PathVariable long millis );

文件上传

//必须设置MediaType.MULTIPART_FORM_DATA_VALUE  使用 @RequestPart接收form格式的文件数据
@PostMapping(value="/file",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String file(@RequestPart("file2") MultipartFile multipartFile);

多文件上传

@PostMapping(value="/files",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Iterable<String> files( @RequestPart("file2") Collection<MultipartFile>  file);



版权声明:本文为dndndnnffj原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。