springboot 直接转发调用_第十课:springboot 通过RestTemple实现http接口调用

简介

项目中关于RestTemple的简单的get/post请求的方法就不进行过多的介绍,本案例中主要介绍各种请求参数逇传递 表单数据/json格式的数据传递, 请求的响应信息的处理;比如响应的信息是List 如果不做处理的话RestTemple 默认将信息处理成List 格式的数据,类似的这种如何处理;还有碰到页面端或者需要将本地的文件File或者MultipartFile 当成请求参数请求下游的http接口内容; 或者下游的响应回文件给我们 如何实现流传递和下载到本地或者输出到浏览器; 案例里面都会有代码介绍

项目demo下载

项目的结构

代码内容

1.pom.xml

4.0.0

com.khy.boot

boot-restTemple

0.0.1-SNAPSHOT

org.springframework.boot

spring-boot-starter-parent

1.5.4.RELEASE

UTF-8

UTF-8

1.8

com.khy.MainApplication

org.springframework.boot

spring-boot-starter-web

com.alibaba

fastjson

1.2.6

org.apache.commons

commons-lang3

3.4

commons-collections

commons-collections

org.apache.httpcomponents

httpclient

4.5.2

org.springframework.boot

spring-boot-maven-plugin

2.RestTempleConfig.Java

通过当前文件读取application.properties文件中的配置信息配置RestTemple;

@Configuration

@ConfigurationProperties(prefix = "custom.rest.connection")

public class RestTempleConfig {

private Integer connectionRequestTimeout;

private Integer connectTimeout;

private Integer readTimeout;

// 启动的时候要注意,由于我们在controller中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例

@Autowired

private RestTemplateBuilder builder;

// 使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例

@Bean

public RestTemplate restTemplate() {

return builder.build();

}

@Bean

public RestTemplate customRestTemplate() {

HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();

httpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);

httpRequestFactory.setConnectTimeout(connectTimeout);

httpRequestFactory.setReadTimeout(readTimeout);

return new RestTemplate(httpRequestFactory);

}

// get/set .....

}

3.application.properties文件

server.port=8080

custom.rest.connection.connection-request-timeout=3000

custom.rest.connection.connect-timeout=3000

custom.rest.connection.read-timeout=3000

模拟操作的类型

1.模拟的是普通的get方法传递参数服务端通过对应的实体类去接收参数

server端代码

/** * 模拟提供一个get方式请求将参数封装到实体类中的方法; * 然后返回的是json格式的参数内容; * @author khy * @createTime 2020年6月16日下午4:47:03 * @param entity * @return */

@GetMapping("/server/get/entity")

public UserEntity getEntity(UserEntity entity){

System.out.println("server端接受参数" +JSON.toJSONString(entity));

entity.setUserName("serverName");

entity.setPassword("serverPassword");

entity.setCreateTime(new Date());

entity.setAge(10);

entity.setPhone("17777813895");

return entity;

}

postman里面模拟请求

client 端RestTemple请求代码

@Autowired

private RestTemplate restTemplate;

@GetMapping("/client/get/entity")

public UserEntity getEntity(){

UserEntity entity1 = restTemplate.getForObject("http://127.0.0.1:8080/server/get/entity"

+ "?userName=candy&password=123456&phone=11111111111&age=10", UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity1));

// 如果参数比较多可以通过 Map 来传递参数;

Mapparam = new HashMap();

param.put("userName", "candy");

param.put("password", "123456");

param.put("phone", "22222222222");

param.put("age", 10);

//使用map传递参数的在于需要将对于的参数封装到url后面{} 里面的是map里面的指定属性的key值内容;

UserEntity entity2 = restTemplate.getForObject("http://127.0.0.1:8080/server/get/entity"

+ "?userName={userName}&password={password}&phone={phone}&age={age}", UserEntity.class, param);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity2));

return entity2;

}

get方式请求可以通过url将参数拼接上去,也可以将参数放置到Map集合中;但是在url后面要添加指定的key和{map中对于的key}

2.模拟的是普通的post方法传递参数服务端通过对应的实体类非json格式

server端代码

/** * 模拟提供一个post方式请求将参数封装到对应的实体类中(非json格式数据) * 然后返回一个对于的json格式数据内容; * @author khy * @createTime 2020年6月17日上午9:15:31 * @param entity * @return */

@PostMapping("/server/post/entity")

public UserEntity postEntity(UserEntity entity){

System.out.println("server端接受参数" +JSON.toJSONString(entity));

entity.setUserName("serverName");

entity.setPassword("serverPassword");

entity.setCreateTime(new Date());

entity.setAge(10);

entity.setPhone("17777813895");

return entity;

}

和普通的get方式请求类似;将参数封装到实体类中非json格式的方式;

postman里面模拟的请求

client端的代码

@PostMapping("/client/post/entity")

public UserEntity postEntity(UserEntity entity){

System.out.println("client接受到的参数"+JSON.toJSONString(entity));

MultiValueMap map = new LinkedMultiValueMap<>();

map.add("userName", "candy");

map.add("password", "123456密码");

map.add("phone", "22222222222");

map.add("age", 10);

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.MULTIPART_FORM_DATA);

HttpEntity> request = new HttpEntity<>(map, headers);

UserEntity entity1 = restTemplate.postForObject("http://127.0.0.1:8080/server/post/entity", request, UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity1));

// 如果参数比较多可以通过 Map 来传递参数;

Mapparam = new HashMap();

param.put("userName", "candy");

param.put("password", "123456密码");

param.put("phone", "22222222222");

param.put("age", 10);

//使用map传递参数的在于需要将对于的参数封装到url后面{} 里面的是map里面的指定属性的key值内容;

UserEntity entity2 = restTemplate.postForObject("http://127.0.0.1:8080/server/post/entity"

+ "?userName={userName}&password={password}&phone={phone}&age={age}", null,UserEntity.class, param);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity2));

return entity2;

}

一种是通过MultiValueMap 将参数名称和实体类对应上;然后将参数和HttpHeaders 设置到 HttpEntity 中去;

另一种也是通过map或者url传递参数一样

3.模拟的是普通的post方法传递参数服务端通过对应的实体类json格式

server 端的代码

/** * 模拟传递的参数是json格式的数据通过@RequestBody封装到实体类中 * 然后返回一个 * @author khy * @createTime 2020年6月17日上午9:26:38 * @param entity * @return */

@PostMapping("/server/postJson/entity")

public UserEntity postEntity1(@RequestBody UserEntity entity){

System.out.println("server端接受参数" +JSON.toJSONString(entity));

entity.setUserName("serverName");

entity.setPassword("serverPassword");

entity.setCreateTime(new Date());

entity.setAge(10);

entity.setPhone("17777813895");

return entity;

}

server端的方法通过@RequestBody 将json格式的参数封装到实体类中

postman中模拟请求

client中代码

@PostMapping("/client/postJson/entity")

public UserEntity postEntity1(UserEntity entity){

System.out.println("client接受到的参数"+JSON.toJSONString(entity));

JSONObject json = new JSONObject();

json.put("userName", "candy");

json.put("password", "123456密码");

json.put("phone", "22222222222");

json.put("age", 10);

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_JSON);

headers.set(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8");

headers.set(HttpHeaders.ACCEPT, "application/json");

HttpEntity request = new HttpEntity<>(json, headers);

UserEntity entity1 = restTemplate.postForObject("http://127.0.0.1:8080/server/postJson/entity", request, UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity1));

//或者直接将JsonObject对象传递

UserEntity entity2 = restTemplate.postForObject("http://127.0.0.1:8080/server/postJson/entity", json, UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity2));

HttpEntity request1 = new HttpEntity<>(entity, headers);

UserEntity entity3 = restTemplate.postForObject("http://127.0.0.1:8080/server/postJson/entity", request1, UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity3));

UserEntity entity4 = restTemplate.postForObject("http://127.0.0.1:8080/server/postJson/entity", entity, UserEntity.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(entity4));

return entity2;

}

如果不定义实体类可以直接通过JSONObject 将参数直接传递

或者通过HttpEntity 将参数传递过(上面的JSONObject 到底层也会封装到HttpEntity 对象中去)

或者直接使用自己定义的实体类对象UserEntity

4.模拟返回的统一格式的json格式数据或者List集合(默认会被转成LinkedHashMap格式数据)

server端代码

/** * 模拟返回一个list集合的实体对象内容; * @author khy * @createTime 2020年6月17日上午10:35:11 * @param entity * @return */

@PostMapping("/server/post/listEntity")

public List listEntity(@RequestBody UserEntity entity){

System.out.println("server端接受参数" +JSON.toJSONString(entity));

List list = new ArrayList(){{

UserEntity userEntity = null;

for (int i = 1; i <= 5; i++) {

userEntity = new UserEntity();

userEntity.setUserName("serverName"+i);

userEntity.setPassword("serverPassword"+i);

userEntity.setCreateTime(new Date());

userEntity.setAge(10);

add(userEntity);

}

}};

System.out.println("server端相应的结果" +JSON.toJSONString(list));

return list;

}

/** * 模拟返回的是固定格式的参数内容 * @author khy * @createTime 2020年6月18日下午2:12:25 * @param entity * @return */

@PostMapping("/server/post/jsonEntity")

public JsonResponse> jsonEntity(@RequestBody UserEntity entity){

System.out.println("server端接受参数" +JSON.toJSONString(entity));

JsonResponse> jsonResponse = JsonResponse.init();

List list = new ArrayList(){{

UserEntity userEntity = null;

for (int i = 1; i <= 5; i++) {

userEntity = new UserEntity();

userEntity.setUserName("serverName"+i);

userEntity.setPassword("serverPassword"+i);

userEntity.setCreateTime(new Date());

userEntity.setAge(10);

add(userEntity);

}

}};

jsonResponse.success(list);

System.out.println("server端相应的结果" +JSON.toJSONString(list));

return jsonResponse;

}

server端两个简单的方法一个返回的是List,

另一个返回的是 JsonResponse

client 端代码内容

@PostMapping("/client/post/listEntity")

public List listEntity(UserEntity entity){

System.out.println("client接受到的参数"+JSON.toJSONString(entity));

JSONObject json = new JSONObject();

json.put("userName", "candy");

json.put("password", "123456密码");

json.put("phone", "22222222222");

json.put("age", 10);

//如果默认不进行处理的话这里返回的是List> 格式的数据并不是我们需要的;

List result = restTemplate.postForObject("http://127.0.0.1:8080/server/post/listEntity", json, List.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(result));

JsonResponse jsonResponse = restTemplate.postForObject("http://127.0.0.1:8080/server/post/jsonEntity", json, JsonResponse.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(jsonResponse));

//如果在服务端需要获取到带有对应class 结果值需要处理一下

JsonResponse> jsonResponses = postForObject(

"http://127.0.0.1:8080/server/post/jsonEntity", json,

new ParameterizedTypeReference>>() {});

System.out.println("client获取到相应信息内容"+JSON.toJSONString(jsonResponses));

return jsonResponses.getData();

}

public JsonResponse postForObject(String url, Object params,

ParameterizedTypeReference> responseType) {

return exchangeAsList(HttpMethod.POST, url, params, responseType);

}

public JsonResponse exchangeAsList(HttpMethod method, String url, Object params,

ParameterizedTypeReference> responseType) {

HttpEntity requestEntity = new HttpEntity(params);

ResponseEntity> exchange = restTemplate.exchange(url, method, requestEntity, responseType);

int codeValue = exchange.getStatusCodeValue();

if(codeValue == 200){

return exchange.getBody();

}else{

return null;

}

}

默认我们不做设置restTemple返回的 list中的类型是LinkedHashMap格式的内容;而不是我们定义的 UserEntity对象集合; 大部分项目中我们都有统一的返回json格式的内容;

比如本案例中的JsonResponse;

要解决这种问题一般需要在服务端直接先返回json格式的字符串;然后客户端获取字符串在处理;(这个方式肯定是不靠谱的服务端不愿意修改啊)

ParameterizedTypeReference 指定返回的类型内容;

完美的解决了响应的json格式参数不能直接转成我们指定的类型内容;

5. 模拟post请求带有文件参数的

经常会碰到管理后台上传了一个文件到我们本地,但是我们缺不需要处理直接转给下面的接口;通过restTemple如何将带有文件内容请求出去;

server端的代码内容

/** * 模拟将上传的文件我们生产指定的文件下面; * @author khy * @createTime 2020年6月17日下午2:00:58 * @param file * @param entity * @return * @throws UnsupportedEncodingException */

@PostMapping("/server/post/upload")

public JsonResponse upload(MultipartFile file, FileEntity entity) throws UnsupportedEncodingException{

JsonResponse jsonResponse = JsonResponse.init();

if(null == file){

jsonResponse.fail("文件不能为空", false);

return jsonResponse;

}

System.out.println("上传文件的entity参数"+JSON.toJSONString(entity));

System.out.println("上传文件的名称:"+file.getOriginalFilename());

try(InputStream inputStream = file.getInputStream();

BufferedInputStream in = new BufferedInputStream(inputStream);

FileOutputStream stream = new FileOutputStream(entity.getPath()+File.separator+entity.getFileName());

BufferedOutputStream out = new BufferedOutputStream(stream);) {

int i = 0;

byte[] b = new byte[1024];

while ((i = in.read(b)) != -1) {

out.write(b, 0, i);

}

jsonResponse.success(true);

} catch (Exception e) {

e.printStackTrace();

}

return jsonResponse;

}

将请求传递过来的文件下载到指定的文件夹下

postman模拟的请求

client端的代码内容;

/** * 模拟页面有上传功能到当前请求中再将文件转发到server端 * @author khy * @createTime 2020年6月17日下午2:54:36 * @param file * @return * @throws IOException */

@PostMapping("/client/post/upload")

public JsonResponse jsonEntity(MultipartFile file) throws IOException{

MultiValueMap param = new LinkedMultiValueMap<>();

ByteArrayResource arrayResource = new ByteArrayResource(file.getBytes()) {

public String getFilename() {

// return file.getOriginalFilename();//这种默认是乱码的;

// TODO 尝试了好几种字符集内容但是还是没有找到解决方案 有解决成功的请联系我一下

//所以自定义成非中文的文件传递过去就行;或者文件名不影响不传递也是可以的 或者通过下面的额外参数传递过去

return file.getOriginalFilename();

};

};

String filename2 = arrayResource.getFilename();

param.add("file", arrayResource);

param.add("fileName", "2222.png");

param.add("size", arrayResource.contentLength());

param.add("path", "D:\\data");

JsonResponse jsonResponse = restTemplate.postForObject("http://127.0.0.1:8080/server/post/upload", param, JsonResponse.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(jsonResponse));

return jsonResponse;

}

通过ByteArrayResource 将传递过来的 MultipartFile 转换一下;然后在getFilename的方法中返回一个文件名(这个在服务端获取的文件名;但是我试了一下中文会乱码,尝试了好几种编码修改都没测试成功,如果对文件名要求不高,可在这里自定义一个非中文的文件名返回;将文件真实的名称当成额外额参数传递过去);

/** * 模拟将本地的文件通过 restTemple请求传递给下游(应用场景比较少) * @author khy * @createTime 2020年6月17日下午3:37:10 * @return * @throws IOException */

@PostMapping("/client/post/uploadLocl")

public JsonResponse jsonEntity() throws IOException{

MultiValueMap param = new LinkedMultiValueMap<>();

FileSystemResource resource = new FileSystemResource(new File("C:/Users/Administrator/Desktop/333.png"));

param.add("file", resource);

param.add("fileName", "3333.png");

param.add("size", resource.contentLength());

param.add("path", "D:\\data");

JsonResponse jsonResponse = restTemplate.postForObject("http://127.0.0.1:8080/server/post/upload", param, JsonResponse.class);

System.out.println("client获取到相应信息内容"+JSON.toJSONString(jsonResponse));

return jsonResponse;

}

下面的这种是将本地或者其他指定的文件当成参数传递到下游的请求中去,通过FileSystemResource 将文件设置进去通过MultiValueMap将所有的文件参数和额外的参数传递过去就行;

6.模拟的是下载类型的方法

可能是页面导出按钮调用client端的请求然后通过client再去请求server端将文件导出;

server端的代码

/** * 模拟导出csv格式的文件数据内容;(是服务端给处理-->client端重定向到服务端接口) * @author khy * @createTime 2020年6月17日下午3:45:58 * @return * @throws UnsupportedEncodingException */

@GetMapping("/server/post/downLoad")

public JsonResponse downLoad(HttpServletResponse response,HttpServletRequest request,Integer num) throws UnsupportedEncodingException{

JsonResponse jsonResponse = JsonResponse.init();

if(null == num || num.equals(0)){

jsonResponse.fail("下载数量不能为空", false);

return jsonResponse;

}

StringBuffer sb = new StringBuffer("姓名,年龄,密码,班级\n");

for (int i = 1; i <= num; i++) {

sb.append("小康康"+i).append(",")

.append("10").append(",").

append("password"+i%10).append(",")

.append("计算机"+i%2).append(",")

.append("\n");

}

System.out.println(sb.toString());

String fileName = "导出学生信息.csv";

try (OutputStream toClient = new BufferedOutputStream(response.getOutputStream())){

String userAgent = request.getHeader("User-Agent");

if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {

fileName = java.net.URLEncoder.encode(fileName, "UTF-8");

} else {

// 非IE浏览器的处理:

fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");

}

response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));

response.setContentType("multipart/form-data");

response.setCharacterEncoding("UTF-8");

response.setContentType("application/octet-stream");

response.flushBuffer();

toClient.write(sb.toString().getBytes("GBK"));

toClient.flush();

toClient.close();

} catch (IOException e) {

e.printStackTrace();

}

jsonResponse.success("执行成功");

return jsonResponse;

}

server端模拟的是导出的是csv格式的文件内容;

client端通过重定向完成下载内容

/** * 上游请求到本地在通过本地的重定向返回下游链接直接调用下载内容 * @author khy * @createTime 2020年6月17日下午4:05:02 * @param request * @param response * @throws IOException */

@GetMapping("/client/post/downLoad")

public void downLoad(HttpServletRequest request,HttpServletResponse response) throws IOException{

String num = request.getParameter("num");

response.sendRedirect("http://localhost:8080/server/post/downLoad?num"+num);

}

clinet端通过重定向到server端调用下载的功能完成下载内容;将server段需要参数通过url传递参数的形式拼接;

7模拟的是通过返回流完成下载的

server端通过response 返回流然后在client端获取直接在通过response响应会页面进行下载

server端代码内容

/** * 模拟的是server端只返回对应的流 * @author khy * @createTime 2020年6月17日下午4:16:59 * @param response * @param request * @param num * @return * @throws UnsupportedEncodingException */

@GetMapping("/server/post/downLoad1")

public void downLoad1(HttpServletResponse response,HttpServletRequest request,Integer num) throws UnsupportedEncodingException{

StringBuffer sb = new StringBuffer("姓名,年龄,密码,班级\n");

for (int i = 1; i <= num; i++) {

sb.append("小康康"+i).append(",")

.append("10").append(",").

append("password"+i%10).append(",")

.append("计算机"+i%2).append(",")

.append("\n");

}

System.out.println(sb.toString());

response.setCharacterEncoding("utf-8");

response.setContentType("application/octet-stream");

try(OutputStream os = new BufferedOutputStream(response.getOutputStream());){

os.write(sb.toString().getBytes("GBK"));

} catch (IOException e1) {

e1.printStackTrace();

}

}

client 端代码

/** * 模拟通过调用的接口返回流来操作 * @author khy * @createTime 2020年6月17日下午5:09:53 * @param request * @param response * @throws IOException */

@GetMapping("/client/post/downLoad1")

public void downLoad1(HttpServletRequest request,HttpServletResponse response) throws IOException{

String num = request.getParameter("num");

HttpHeaders headers = new HttpHeaders();

HttpEntity httpEntity =new HttpEntity(null, headers);

ResponseEntity responseEntity = restTemplate.exchange(

"http://localhost:8080/server/post/downLoad1?num="+num, HttpMethod.GET, httpEntity, byte[].class);

byte[] result = (byte[])responseEntity.getBody();

String fileName = "导出学生信息.csv";

String userAgent = request.getHeader("User-Agent");

if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {

fileName = java.net.URLEncoder.encode(fileName, "UTF-8");

} else {

// 非IE浏览器的处理:

fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");

}

response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));

response.setContentType("multipart/form-data");

response.setCharacterEncoding("UTF-8");

response.setContentType("application/octet-stream");

try (InputStream in = new ByteArrayInputStream(result);

OutputStream toClient = new BufferedOutputStream(response.getOutputStream())){

int len = 0;

byte[] buf = new byte[1024];

while ((len = in.read(buf, 0, 1024)) != -1) {

toClient.write(buf, 0, len);

}

} catch (IOException e) {

e.printStackTrace();

}

}

client 通过请求获取到的字节数组然后通过response输出到浏览器端

7 直接通过server端返回字节数组或者String

server端代码内容

@PostMapping("/server/post/downLoad2")

public JsonResponse downLoad2(Integer num) throws UnsupportedEncodingException{

JsonResponse jsonResponse = JsonResponse.init();

StringBuffer sb = new StringBuffer("姓名,年龄,密码,班级\n");

for (int i = 1; i <= num; i++) {

sb.append("小康康"+i).append(",")

.append("10").append(",").

append("password"+i%10).append(",")

.append("计算机"+i%2).append(",")

.append("\n");

}

System.out.println(sb.toString());

jsonResponse.success(sb.toString().getBytes("GBK"));

return jsonResponse;

}

将对于的字符串转成byte[]数组输出;

client 端的代码

@GetMapping("/client/post/downLoad2")

public void downLoad2(HttpServletRequest request,HttpServletResponse response) throws IOException{

String num = request.getParameter("num");

HttpHeaders headers = new HttpHeaders();

HttpEntity httpEntity =new HttpEntity(null, headers);

JsonResponse jsonResponses = postForObject(

"http://localhost:8080/server/post/downLoad2?num="+num, httpEntity,

new ParameterizedTypeReference>() {});

byte[] result = jsonResponses.getData();

String fileName = "导出学生信息.csv";

String userAgent = request.getHeader("User-Agent");

if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {

fileName = java.net.URLEncoder.encode(fileName, "UTF-8");

} else {

// 非IE浏览器的处理:

fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");

}

response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));

response.setContentType("multipart/form-data");

response.setCharacterEncoding("UTF-8");

response.setContentType("application/octet-stream");

try (InputStream in = new ByteArrayInputStream(result);

OutputStream toClient = new BufferedOutputStream(response.getOutputStream())){

int len = 0;

byte[] buf = new byte[1024];

while ((len = in.read(buf, 0, 1024)) != -1) {

toClient.write(buf, 0, len);

}

} catch (IOException e) {

e.printStackTrace();

}

}

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
评论

打赏作者

weixin_31642531

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值