RestTemplate
大约 5 分钟javaspring
概述
微信后台使用自己制作的 httpUtils 发送模板消息在 windows 服务器上会有中文乱码的问题,使用 spring 自带的 RestTemplate 可以解决
案例
依赖
springboot 项目的依赖项 spring-boot-starter-web 中自带,还有 Jackson
代码
在项目根目录中创建目录 config 作为所有配置类的根目录,并创建下面配置类
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);
factory.setConnectTimeout(15000);
return factory;
}
}
使用案例
@SpringBootTest
class DemoApplicationTests {
@Resource
private RestTemplate restTemplate;
@Test
void getTest() {
String str = restTemplate.getForObject("http://localhost:8888/get?name=zs", String.class);
System.out.println(str);
}
@Test
void getRestTest() {
String name = "ls";
String str = restTemplate.getForObject("http://localhost:8888/getName/" + name, String.class);
System.out.println(str);
}
@Test
void postTest() {
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.set("name", "zs");
String str = restTemplate.postForObject("http://localhost:8888/post", map, String.class);
System.out.println(str);
}
@Test
void postBodyTest() {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HashMap<String, Object> map = new HashMap<>();
map.put("name", "zs");
map.put("age", 23);
String stu = JSON.toJSONString(map);
HttpEntity<String> formEntity = new HttpEntity<String>(stu, headers);
String str = restTemplate.postForObject("http://localhost:8888/postBody", formEntity, String.class);
System.out.println(str);
}
@Test
void putTest() {
restTemplate.put("http://localhost:8888/put?name=zs", null);
}
@Test
void deleteTest() {
restTemplate.delete("http://localhost:8888/delete?name=zs");
}
// 返回带有泛型的类型,如下代码,请求后返回的类型是:JuheRes<DayInfo>
// 初次使用在 showabe 项目中 HolidayUtils # getDayInfo 方法中
void generalType(){
ResponseEntity<JuheRes<DayInfo>> response = restTemplate.exchange(
url, HttpMethod.GET, null,
new ParameterizedTypeReference<JuheRes<DayInfo>>() {}
);
JuheRes<DayInfo> res = response.getBody();
}
}
自己试验的代码
@Resource
private RestTemplate restTemplate;
public String rtPost(String url, String params) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HashMap<String, Object> map = new HashMap<>();
map.put("name", "zs");
map.put("age", 23);
ObjectMapper mapper = new ObjectMapper();
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String stu = ow.writeValueAsString(map);
HttpEntity<String> formEntity = new HttpEntity<String>(params, headers);
String str = restTemplate.postForObject(url, formEntity, String.class);
return str;
}
初次应用在样品管理微信后台项目 https://gitee.com/chanchaw/wechatbetrade 的方法 HttpUtils # rtPost
封装工具
根据上面介绍配置 RestTemplate 后做了如下封装工具
package com.xdf.wxpad.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author chanchaw
* @create 2024-09-02 8:58
*/
@Component
public class RestTemplateUtils {
private static final ObjectMapper mapper = new ObjectMapper();
@Resource
private RestTemplate restTemplate;
public <T> T postJson(String url, Map<String, Object> param, Class<T> responseType) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
ObjectMapper mapper = new ObjectMapper();
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String paramJsonString = ow.writeValueAsString(param);
HttpEntity<String> formEntity = new HttpEntity<>(paramJsonString, headers);
String responseJsonString = restTemplate.postForObject(url, formEntity, String.class);
return mapper.readValue(responseJsonString, responseType);
}
/**
* post方法发送 formData 数据的请求,使用案例:
* MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
* map.put("appid", new ArrayList<>(Arrays.asList(DevMaterial.VISITOR_MINI.APPID)));
* map.put("secret", new ArrayList<>(Arrays.asList(DevMaterial.VISITOR_MINI.APPSECRET)));
* map.put("js_code", new ArrayList<>(Arrays.asList(code)));
* map.put("grant_type", new ArrayList<>(Arrays.asList("authorization_code")));
* MiniGetUserProfileRes res = restTemplateUtils.postForm("https://xxx", map,MiniGetUserProfileRes.class);
* 初次使用在项目 https://gitee.com/chanchaw/visitor 中的 WechatUtils.java # getUserProfile 中
* @param url
* @param param
* @param responseType
* @return
* @param <T>
* @throws JsonProcessingException
*/
public <T> T postForm(String url, MultiValueMap<String, Object> param, Class<T> responseType) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, Object>> reqParam = new HttpEntity<>(param, headers);
ResponseEntity<String> res = restTemplate.postForEntity(url, reqParam, String.class);
String bodyString = res.getBody();
return mapper.readValue(bodyString, responseType);
}
}
调用方法
@Autowired
private RestTemplateUtils restTemplateUtils;
@PostMapping("/postJsonFieldInfo")
public JsonResult postJsonFieldInfo() throws JsonProcessingException {
PostJson postJson = new PostJson();
postJson.setUrl("https://染厂云域名/zknoteb/dbManager/fieldInfoList");
postJson.setRetClass(ZkRes.class);
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> dbConfig = new HashMap<>();
dbConfig.put("dbType","mysql");
dbConfig.put("ip","49.234.149.119");
dbConfig.put("port","16033");
dbConfig.put("userName","root");
dbConfig.put("passWord","!XDFserver20211001@");
dbConfig.put("dbName","zhike");
map.put("dbConfig",dbConfig);
map.put("dbName","zhike");
map.put("tableName","z_thing");
ZkRes res = restTemplateUtils.postJson(postJson.getUrl(), map, ZkRes.class);
return JsonResult.ok(res);
}
错误与提示
响应实体对象反序列化失败
import Exc.NotAllowedErrorException;
import Exc.RunTimeErrorException;
import account.entity.*;
import account.entity.smartwarehouse.ResponseSendYFH;
import account.entity.smartwarehouse.SmartWarehouseYFH;
import account.entity.smartwarehouse.SmartWarehouseYFHPallet;
import account.enums.Response;
import account.service.GConfigOnevarService;
import account.service.PalletSWService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RequestMapping(value = "/sendYFH2SmartWarehouse", method = RequestMethod.POST)
public ResponseSendYFH sendYFH2SmartWarehouse(@RequestBody SmartWarehouseYFH record) throws Exception {
GConfigOnevar configOnevar = configOnevarService.findById("WEB应用_推送预发货单给立库系统");
if(configOnevar == null) throw new RunTimeErrorException("请在配置文件中设置【WEB应用_推送预发货单给立库系统】对应的URL");
String url = Optional.ofNullable(configOnevar.getbValue()).orElse("");
if(url.length() == 0) throw new RunTimeErrorException("请在配置文件中设置【WEB应用_推送预发货单给立库系统】对应的URL");
if(record == null) throw new RunTimeErrorException("空数据无法发送给立库系统!");
List<SmartWarehouseYFHPallet> data = record.getData();
if(data == null || data.size() == 0) throw new RunTimeErrorException("空数据无法发送给立库系统!");
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
ObjectMapper mapper = new ObjectMapper();
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String jsonData = "";
try {
jsonData = ow.writeValueAsString(record);
} catch (JsonProcessingException e) {
logger.info("预发货单推送到立库系统时出现异常,推送的参数是:" + record.toString() + ",响应的异常信息是:" + e.getMessage());
throw new RuntimeException(e);
}
HttpEntity<String> formEntity = new HttpEntity(jsonData, headers);
logger.info("推送预发货单给立库系统,url:" + url + ",推送的参数是:" + record.toString());
// 在白玉兰对接立库系统中,由于对方响应来的结果是 JSON 字符串,而不是 JSON 序列化后的结果,导致下面通过指定 ResponseSendYFH.class
// 进行反序列化失败,特别为该类制作了 “自定义反序列化器”,看本方法下面的类
ResponseEntity<ResponseSendYFH> res = restTemplate.postForEntity(url, formEntity, ResponseSendYFH.class);
ResponseSendYFH responseBody = null;
if (res.getStatusCode().is2xxSuccessful()) {
logger.info("推送预发货单给立库系统收到的响应结果:" + res.toString());
responseBody = res.getBody();
logger.info("推送预发货单给立库系统收到的响应数据:" + responseBody.toString());
if (responseBody != null) {
// 处理成功的响应
System.out.println("Response: " + responseBody);
} else {
System.out.println("Response body is null");
logger.info("推送预发货单给立库系统收到的响应数据:NULL");
}
} else {
logger.info("推送预发货单给立库系统收到失败响应码:: " + res.getStatusCode());
}
logger.info("推送预发货单给立库系统封装收到的响应结果即将返回给前端,数据是:" + responseBody.toString());
return responseBody;
}
自定义反序列化器
package account.config;
import account.entity.smartwarehouse.ResponseSendYFH;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class ResponseSendYFHDeserializer extends JsonDeserializer<ResponseSendYFH> {
@Override
public ResponseSendYFH deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
System.out.println(node);
// 由于对方响应来的是字符串,所以需要再次通过 readTree 读取为 JsonNode
// 没有下面 if 判断直接 node.get("success") 得到的是空
// 如果node是一个字符串而不是对象,我们需要先解析字符串
if (node.isTextual()) {
// 再次解析字符串内容
ObjectMapper mapper = new ObjectMapper();
node = mapper.readTree(node.asText());
}
JsonNode success1 = node.get("success");
System.out.println(success1);
boolean success = success1.asBoolean();
System.out.println(success);
JsonNode message1 = node.get("message");
System.out.println(message1);
String message = message1.asText();
System.out.println(message);
// 根据你的需求自定义反序列化逻辑
return new ResponseSendYFH(success, message);
}
}
