SpringMVC+Feign


前言

这几天针对一些功能性接口的替换,涉及多个微服务,基本都在同一个eureka且使用的都是springboot+springcloud的架构,所以把各自系统请求统一调用某个基础功能服务。feign调用调试时发现些问题(ContentType=multipart/form-data),总结问题如下:

  1. feign不支持使用多个@RequestPart
  2. MutiValueMap传递文件参数不支持MultiparFile类型编码
  3. MutiValueMap传递文件参数,中文文件名乱码(springboot1.5+对应的springcloud

因此,索性总结下针对目前主流ContentType的web请求,springMvc和feign调用的处理方式。以下总结和demo适用版本(springboot1.5.x+springcloud-Dalston和springboot2.0.x+springcloud-Finchley),推荐使用2.0版本


一、ContentType

在 Web 请求中,Content-Type 是 HTTP 请求头(Request Header)的一个核心字段,用于告诉服务器当前请求体(Request Body)中数据的格式类型,以便服务器能够正确解析请求内容

application/x-www-form-urlencoded

简单表单提交,请求体中的数据会被编码为「键值对字符串」,格式为 key1=value1&key2=value2

![[urlEncoded.png]]在这里插入图片描述

application/json

当下最常用请求体类型,将数据体json序列化传递

在这里插入图片描述

application/xml

一些政府机构或老服务常用请求体类型,与json处理相似

在这里插入图片描述

multipart/form-data

文本或文件等复杂表单请求体类型,建议有文件参数时使用

在这里插入图片描述

查询参数

非请求体参数(无ContentType),用&直接拼接在url之后,GET参数传值,在此补充

在这里插入图片描述

二、SpringMVC实践

针对不同的ContentType的参数解析,SpringMVC有提供对应的标注注解,有些情况下不使用注解spring也可以自行推断处理但不建议,一来是便于spring精确处理避免不必要的性能损耗,二来也方便自己区分,不留歧义。

核心注解总览

请求处理

  • @RequestMapping 标注控制器的处理方法
    • value 请求路径
    • method 请求方式
    • consumes 解析的ContentType
    • produces 返回的ContentType

请求体处理

  • @RequestParam【标注简单表单参数和查询参数】
  • @RequestBody【标注json参数、xml参数】
  • @RequestPart【标注复杂表单参数如文件】

必要配置

服务端需要限制文件大小


spring:  
# 2.0版本配置  
  servlet:  
    multipart:  
      enabled: true  
      max-file-size: 30MB       # 单个文件最大大小  
      max-request-size: 30MB    # 整个请求最大大小  
## 1.5版本配置  
#  http:  
#    multipart:  
#      enabled: true  
#      max-file-size: 30MB       # 单个文件最大大小  
#      max-request-size: 30MB    # 整个请求最大大小

application/x-www-form-urlencoded

  • 核心注解
    • @RequestParam 标注简单表单参数
@PostMapping(value = "/urlEncoded", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) 
public String simplyFormData(@RequestParam("name")String name,  
                             @RequestParam("age")Integer age) {  
    return name+": "+age;  
}

application/json

  • 核心注解:
    • @RequestBody 标注请求体反序列化的java对象
@PostMapping(value = "/json", consumes = MediaType.APPLICATION_JSON_VALUE)  
public String json(@RequestBody User user) {  
    return JSONUtil.toJsonStr(user);  
}

application/xml

  • 核心注解:
    • @RequestBody 标注请求体反序列化的java对象
  • 额外的xml支持
    • springboot1.5 自动配置xml转换器Jaxb2RootElementHttpMessageConverter
    • springboot2.0 不再自动配置xml转换器,需要自行导入相应的xml依赖,并且推荐使用jackson族的xml转换器

xml-jackson依赖


<dependency>  
    <groupId>com.fasterxml.jackson.dataformat</groupId>  
    <artifactId>jackson-dataformat-xml</artifactId>  
    <!-- 版本由 Spring Boot 父工程管理,不要手动指定 -->  
</dependency>

@PostMapping(value = "/xml",  
        consumes = MediaType.APPLICATION_XML_VALUE,  
        produces = MediaType.APPLICATION_XML_VALUE)  
public XmlUser xml(@RequestBody XmlUser xmlUser) {  
    xmlUser.setName("Welcome " + xmlUser.getName());  
    return xmlUser;  
}

@JacksonXmlRootElement(localName = "xuser")//指定根元素名称  
@Data  
public class XmlUser {  
    private String name;  
    private Integer age;  
  
    public XmlUser() {}  
  
    public XmlUser(String name, Integer age) {  
        this.name = name;  
        this.age = age;  
    }  
}


multipart/form-data

  • 核心注解
    • @RequestPart 标注复杂表单参数如文件,当然也可以标注简单表单参数
    • @RequestParam 简单表单参数

//单文件表单
@PostMapping(value = "/multiFormData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String multipartFormData(@RequestPart("file")MultipartFile file,  
                                @RequestParam("name")String name) {  
    StringBuilder sb = new StringBuilder();  
    if (file != null) {  
	        sb.append("fileName: ").append(file.getOriginalFilename()).append("fileSize: ").append(file.getSize()).append("\n");  
	    }  
    sb.append("username: ").append(name);  
    return sb.toString();  
}

//多文件表单
@PostMapping(value = "/manyMultiFormData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String manyMultipartFormData(@RequestPart("files") MultipartFile[] files,  
                                    @RequestParam("name")String name) {  
    StringBuilder sb = new StringBuilder();  
    if (files != null) {  
        for (MultipartFile file : files) {  
            sb.append("fileName: ").append(file.getOriginalFilename()).append("fileSize: ").append(file.getSize()).append("\n");  
        }  
    }  
    sb.append("username: ").append(name);  
    return sb.toString();  
}

查询参数请求

核心注解

  • @RequestParam 标注指定的key
@GetMapping(value = "/queryParams")  
public String urlEncoded(@RequestParam("name") String name,  
                         @RequestParam("age") Integer age) {  
    return name+": "+age;  
}

混合参数请求

文件(Multipart)+文本(text)+json

对于这种混合的复杂请求体,ContentType=multipart/form-data,对于文件和json一定要使用@RequestPart 标注

核心注解

  • @RequestPart 标注复杂表单参数
  • @RequestParam 标注简单表单参数
@PostMapping(value = "/mixMultiFormData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)  
public String mixRequestBody(@RequestPart("file")MultipartFile file,  
                             @RequestParam("expireTime")String expireTime,  
                             @RequestPart("user") String user) {  
    StringBuilder sb = new StringBuilder();  
    if (file != null) {  
        sb.append("fileName: ").append(file.getOriginalFilename()).append("fileSize: ").append(file.getSize()).append("\n");  
    }  
    sb.append("expireTime: ").append(expireTime).append("\n");  
    sb.append("user: ").append(JSONUtil.toJsonStr(user));  
    return sb.toString();  
}

三、feign实践

作为声明式http请求,feign调用的核心部分:mvc注解/feign注解+编码器,习惯了springboot的开箱即用,本着一个能用配置的就不用自定义的原则,给出以下两套主流请求方式。

必要依赖

注意

  • springbootspringcloud版本一定要对应(这个可以查看官网或者问AI)
    • boot1.5+cloud Dalston+feign
    • boot2.0+cloud Finchley+openfeign
  • feign-formfeign-form-spring也要与springcloud版本适配,不然会有编码异常等问题
    • 3.0.3版本可以适配上面的feignopenfeign
  • 在实践过程中遇到问题特别是框架相关的,依赖冲突与不适配是主要问题所在
<parent>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-parent</artifactId>  
<!-- 	<version>1.5.2.RELEASE</version>--> 
	<version>2.0.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->  
</parent>

<properties>  
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>  
<!--        <spring.cloud.version>Dalston.RELEASE</spring.cloud.version>-->  
        <spring.cloud.version>Finchley.SR2</spring.cloud.version>  
    </properties>

<!-- springmvc -->  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>  
</dependency>

<!-- XML 处理核心依赖(必须) -->  
<dependency>  
    <groupId>com.fasterxml.jackson.dataformat</groupId>  
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

<!-- 一定要与springcloud版本对应 -->  
<!--        <dependency>-->  
<!--            <groupId>org.springframework.cloud</groupId>-->  
<!--            <artifactId>spring-cloud-starter-feign</artifactId>-->  
<!--        </dependency>-->

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

<!-- feign文件上传支持 -->  
<dependency>  
    <groupId>io.github.openfeign.form</groupId>  
    <artifactId>feign-form</artifactId>  
    <version>3.0.3</version>  
</dependency>  
<dependency>  
    <groupId>io.github.openfeign.form</groupId>  
    <artifactId>feign-form-spring</artifactId>  
    <version>3.0.3</version>  
</dependency>

<dependencyManagement>  
    <dependencies>  
        <dependency>  
            <groupId>org.springframework.cloud</groupId>  
            <artifactId>spring-cloud-dependencies</artifactId>  
            <version>${spring.cloud.version}</version>  
            <type>pom</type>  
            <scope>import</scope>  
        </dependency>  
    </dependencies>  
</dependencyManagement>

必要配置

feign-mvc契约

注解与SpringMVC一致,但是有几个注意的地方

  1. feign不允许使用多个@RequestPart,这意味这无法发送多个文件MultipartFile(如果你想用一个对象封装多个file或者标注一个MultipartFile[]数组,那你需要自定义编码器,依赖提供的编码器是不支持的)
  2. @RequestParam 标注的参数会被当成查询参数拼接在url之后
  3. 解决“1-2”问题,再不自定义编码器的前提下使用原生的MultiValueMap来发送简单表单和复杂表单数据
  4. 使用MultiValueMap传递文件时,若是springboot1.5版本会出现中文名乱码问题,2.0框架已解决这个问题
  5. form-data表单请求体编码,需要引入相关依赖来配置相应的编码器(注意版本适配问题)
配置

开放日志

logging:  
  level:  
    org.example.feign: DEBUG

开启feign功能

@EnableFeignClients(basePackages = "org.example.feign")  
@SpringBootApplication  
public class FeignClient {  
    public static void main(String[] args) {  
        SpringApplication.run(FeignClient.class);  
    }  
}

配置编码器

public class FeignDefaultConfig {  

	//feign编码器
    @Bean  
    public Encoder feignEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {  
        return new SpringFormEncoder(new SpringEncoder(messageConverters));  
    }  
	
	//feign日志输出
    @Bean  
    public Logger.Level feignLoggerLevel() {  
        return Logger.Level.HEADERS;  
    }  
}
FeignClient demo

FeignClient

package org.example.feign;  
  
import org.example.entity.User;  
import org.example.entity.XmlUser;  
import org.springframework.cloud.openfeign.FeignClient;  
import org.springframework.http.MediaType;  
import org.springframework.util.MultiValueMap;  
import org.springframework.web.bind.annotation.*;  
import org.springframework.web.multipart.MultipartFile;  
  
 
@FeignClient(name = "feign-server", path = "/test", configuration = FeignDefaultConfig.class)  
public interface UserFeignClientV1 {  
  
    //查询参数  
    @GetMapping(value = "/queryParams")  
    String urlEncoded(@RequestParam("name") String name,  
                      @RequestParam("age") Integer age);  
  
    //json  
    @PostMapping(value = "/json", consumes = MediaType.APPLICATION_JSON_VALUE)  
    String json(@RequestBody User user);  
  
    //xml  
    @PostMapping(value = "/xml", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)  
    String xml(@RequestBody XmlUser xmlUser);  
  
  
    //普通表单  
    @PostMapping(value = "/ulrEncoded", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)  
    String simplyFormData(MultiValueMap<String,Object> map);  
  
    //单文件,这里的@RequestParam标注的key会被拼接到url后面,只是为了演示注解可用,实际对于表单参数来说还是用MultiValueMap
    @PostMapping(value = "/multiFormData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)  
    String multipartFormData(@RequestPart("file") MultipartFile file,  
                             @RequestParam("name")String username);  
  
    //多文件  
    @PostMapping(value = "/manyMultiFormData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)  
    String manyMultipartFormData(MultiValueMap<String,Object> map);  
  
    //混合请求体  
    @PostMapping(value = "/mixMultiFormData",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)  
    String mixRequestBody(MultiValueMap<String,Object> map);  
  
}

测试demo

package feign;  
  
import org.example.FeignClient;  
import org.example.entity.User;  
import org.example.entity.XmlUser;  
import org.example.feign.UserFeignClientV1;  
import org.junit.Test;  
import org.junit.runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.context.SpringBootTest;  
import org.springframework.core.io.FileSystemResource;  
import org.springframework.mock.web.MockMultipartFile;  
import org.springframework.test.context.junit4.SpringRunner;  
import org.springframework.util.LinkedMultiValueMap;  
import org.springframework.util.MultiValueMap;  
import org.springframework.web.multipart.MultipartFile;  
  
import java.io.IOException;  
import java.nio.file.Files;  
import java.nio.file.Path;  
import java.nio.file.Paths;  
    
@SpringBootTest(classes = FeignClient.class)  
@RunWith(SpringRunner.class)  
public class UserFeignClientV1Test {  
  
    @Autowired  
    private UserFeignClientV1 userFeignClientV1;  
  
    @Test  
    public void test001() {  
        String resp = userFeignClientV1.urlEncoded("赵大",  1);  
        System.out.println("urlEncoded返回值========\n"+resp);  
    }  
  
    @Test  
    public void test002() {  
        User user = new User("钱二", 2);  
        String resp = userFeignClientV1.json(user);  
        System.out.println("json返回值========\n"+resp);  
    }  
  
    @Test  
    public void test003() {  
        XmlUser xmlUser = new XmlUser("孙三", 3);  
        String resp = userFeignClientV1.xml(xmlUser);  
        System.out.println("xml返回值========\n"+resp);  
    }  
  
  
    @Test  
    public void test004() {  
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();  
        multiValueMap.add("name","李四");  
        multiValueMap.add("age","4");//普通文本必须用String  
        String resp = userFeignClientV1.simplyFormData(multiValueMap);  
        System.out.println("simplyFormData返回值========\n"+resp);  
    }  
  
    @Test  
    public void test005() throws IOException {  
        Path path = Paths.get("/Users/images/老达奇.jpg");  
        MultipartFile file = new MockMultipartFile(  
                "file", path.getFileName().toString(), Files.probeContentType(path), Files.newInputStream(path)  
        );  
        String resp = userFeignClientV1.multipartFormData(file, "周五");  
        System.out.println("multipartFormData返回值========\n"+resp);  
    }  
  
  
    @Test  
    public void test006() throws IOException {  
        Path path1 = Paths.get("/Users/images/rdr2_arthur_morgan.jpg");  
        Path path2 = Paths.get("/Users/images/老达奇.jpg");   
        MultiValueMap<String,Object> map = new LinkedMultiValueMap<>();  
        map.add("files",new FileSystemResource(path1.toFile()));  
        map.add("files",new FileSystemResource(path2.toFile()));  
        map.add("name","吴六");  
        String resp = userFeignClientV1.manyMultipartFormData(map);  
        System.out.println("manyMultipartFormData返回值========\n"+resp);  
    }  
//  
    @Test  
    public void test007() throws IOException {  
        Path path = Paths.get("/Users/images/老达奇.jpg");  
        User user = new User("郑七", 7);  
        MultiValueMap<String,Object> map = new LinkedMultiValueMap<>();  
        map.add("file",new FileSystemResource(path.toFile()));  
        map.add("expireTime","2099-12-12 00:00:00");  
        map.add("user",user);  
        String resp = userFeignClientV1.mixRequestBody(map);  
        System.out.println("mixRequestBody返回值========\n"+resp);  
    }  
}

feign-默认契约

通过配置开启feign的底层注解

核心注解总览

  • @RequestLine
    • 请求处理注解,类比于RequestMapping
    • @RequestLine("POST /json")【请求方法+请求路径】
  • @Headers
    • 显示指定请求头信息
    • @Headers({"Content-Type: application/json"})
  • @Param
    • 标注各类型请求体参数
    • @Param("file") MultipartFile file
配置

开放日志

logging:  
  level:  
    org.example.feign: DEBUG

开启feign功能

@EnableFeignClients(basePackages = "org.example.feign")  
@SpringBootApplication  
public class FeignClient {  
    public static void main(String[] args) {  
        SpringApplication.run(FeignClient.class);  
    }  
}

配置编码器

package org.example.feign;  
  
import feign.Contract;  
import feign.Logger;  
import feign.codec.Encoder;  
import feign.form.spring.SpringFormEncoder;  
import org.springframework.beans.factory.ObjectFactory;  
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;  
import org.springframework.cloud.openfeign.support.SpringEncoder;  
import org.springframework.context.annotation.Bean;  
  

public class FeignFormDataConfig {  
  
    /**  
     * 此配置使 Feign 使用原生注解(@RequestLine, @Param)替代 Spring MVC 注解  
     * @return  
     */  
    @Bean  
    public Contract feignContract() {  
        return new feign.Contract.Default();  
    }  
  
    @Bean  
    public Encoder feignEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {  
        return new SpringFormEncoder(new SpringEncoder(messageConverters));  
    }  
  
    @Bean  
    public Logger.Level feignLoggerLevel() {  
        return Logger.Level.FULL;  
    }  
}
FeignClient demo

FeignClient

package org.example.feign;  
  
import feign.Headers;  
import feign.Param;  
import feign.RequestLine;  
import org.example.entity.User;  
import org.springframework.cloud.openfeign.FeignClient;  
import org.springframework.web.multipart.MultipartFile;  
  

@FeignClient(name = "feign-server",path = "/test",configuration = FeignFormDataConfig.class)  
public interface UserFiegnClientV2 {  
  
    //查询参数 /users?name={name}&age={age}   
    @RequestLine("GET /queryParams?name={name}&age={age}")  
    String urlEncoded(@Param("name") String name,  
                      @Param("age")Integer age);  
  
    //json  
    @RequestLine("POST /json")  
    @Headers({"Content-Type: application/json"})  
    String json(User user);  
  
    //普通表单  
    @RequestLine("POST /urlEncoded")  
    @Headers({"Content-Type: application/x-www-form-urlencoded"})  
    String simplyFormData(@Param("name")String name,@Param("age")Integer age);  
  
    //单文件  
    @RequestLine("POST /multiFormData")  
    @Headers({"Content-Type: multipart/form-data"})  
    String multipartFormData(@Param("file") MultipartFile file,  
                             @Param("name")String name);  
  
    //多文件  
    @RequestLine("POST /manyMultiFormData")  
    @Headers({"Content-Type: multipart/form-data"})  
    String manyMultipartFormData(@Param("files") MultipartFile[] file,  
                                 @Param("name")String name);  
  
    //混合请求体  
    @RequestLine("POST /mixMultiFormData")  
    @Headers({"Content-Type: multipart/form-data"})  
    String mixRequestBody(@Param("file") MultipartFile file,  
                          @Param("expireTime") String expireTime,  
                          @Param("user") String user);//这里user的contentType会变成text/plain,服务端无法用@RequestPart反序列化成对象  
  
  
}

测试demo

package feign;  
  
import cn.hutool.json.JSONUtil;  
import org.example.FeignClient;  
import org.example.entity.User;  
import org.example.feign.UserFiegnClientV2;  
import org.junit.Test;  
import org.junit.runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.test.context.SpringBootTest;  
import org.springframework.mock.web.MockMultipartFile;  
import org.springframework.test.context.junit4.SpringRunner;  
import org.springframework.web.multipart.MultipartFile;  
  
import java.io.IOException;  
import java.nio.file.Files;  
import java.nio.file.Path;  
import java.nio.file.Paths;  
  
/**  
 * @author yu  
 * @version 1.0  
 * @date 2025/7/29 14:07  
 * @Description  
 */  
@SpringBootTest(classes = FeignClient.class)  
@RunWith(SpringRunner.class)  
public class UserFeignClientV2Test {  
    @Autowired  
    private UserFiegnClientV2 userFeignClientV2;  
  
    @Test  
    public void test001() {  
        String resp = userFeignClientV2.urlEncoded("赵大",  1);  
        System.out.println("urlEncoded返回值========\n"+resp);  
    }  
  
    @Test  
    public void test002() {  
        User user = new User("钱二", 2);  
        String resp = userFeignClientV2.json(user);  
        System.out.println("json返回值========\n"+resp);  
    }  
  
    @Test  
    public void test004() {  
        String resp = userFeignClientV2.simplyFormData("孙三", 3);  
        System.out.println("simplyFormData返回值========\n"+resp);  
    }  
  
    @Test  
    public void test005() throws IOException {  
        Path path = Paths.get("/Users/images/老达奇.jpg");  
        MultipartFile file = new MockMultipartFile(  
                "file", path.getFileName().toString(), Files.probeContentType(path), Files.newInputStream(path)  
        );  
        String resp = userFeignClientV2.multipartFormData(file, "周五");  
        System.out.println("multipartFormData返回值========\n"+resp);  
    }  
  
    @Test  
    public void test006() throws IOException {  
        Path path1 = Paths.get("/Users/images/rdr2_arthur_morgan.jpg");  
        Path path2 = Paths.get("/Users/images/老达奇.jpg");  
  
        MultipartFile file1 = new MockMultipartFile(  
                "files", path1.getFileName().toString(), Files.probeContentType(path1), Files.newInputStream(path1)  
        );  
        MultipartFile file2 = new MockMultipartFile(  
                "files", path2.getFileName().toString(), Files.probeContentType(path2), Files.newInputStream(path2)  
        );  
        MultipartFile[] files = new MultipartFile[2];  
        files[0] = file1;  
        files[1] = file2;  
        String resp = userFeignClientV2.manyMultipartFormData(files,"吴六");  
        System.out.println("manyMultipartFormData返回值========\n"+resp);  
    }  
  
    @Test  
    public void test007() throws IOException {  
        Path path = Paths.get("/Users/images/老达奇.jpg");  
        MultipartFile file = new MockMultipartFile(  
                "file", path.getFileName().toString(), Files.probeContentType(path), Files.newInputStream(path)  
        );  
        User user = new User("郑七", 7);  
        String resp = userFeignClientV2.mixRequestBody(file, "2099-12-12 00:00:00", JSONUtil.toJsonStr(user));  
        System.out.println("mixRequestBody返回值========\n"+resp);  
    }  
}

四、feign调用总结

注意点

  1. 注意版本的兼容性,建议老版本升级为springboot2.0+openfeign能解决不少问题
  2. 能用feign提供的编码器就别自定义,默认契约和feign契约基本能解决问题
  3. 尽量加标识注解,不让框架推断,避免出现意料之外的问题或歧义
  4. 可以debug看下springMVC的处理流程流程DispatcherServlet,无需深入每个处理方法,只过主要流程;还有feign的编码器处理,如上面的SpringFormEncoder

问题

现在回过头来看下前言中提到的问题

  1. feign不支持使用多个@RequestPart
    1. 默认契约MVC是不支持的,可以用MutiValueMap代替或者直接使用feign契约
  2. MutiValueMap传递文件参数不支持MultiparFile类型编码
    1. 编码器不支持,可以自定义编码器或者使用FileSystemResource 对象传递
  3. MutiValueMap传递文件参数,中文文件名乱码(springboot1.5+对应的springcloud
    1. 升级boot2.0+openfeign
    2. 使用feign契约传递MultipartFile
    3. 新增一个参数存放中文文件名,在服务端重命名
    4. 自定义编码器处理中文文件名

五、SpringMVC处理流程图

springboot1.5

在这里插入图片描述

springboot2.0

在这里插入图片描述


总结

建议还是使用springboot2.0+openfeign(开启feign契约),能省下不少麻烦,若非同一个eureka那使用feign调用意义不大可以使用Apache的HttpClient或其他的http客户端调用,自己封装工具类,方便以后升级或改造。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jsonformat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值