精华内容
下载资源
问答
  • restTemple
    2020-05-06 10:23:36

    通过RestTemple进行的post请求。其中的参数是file类型,
    工具 idea 项目 mevan 技术 sringboot

    
    ```java
    package com.guangda.CardApply;
    
    import com.guangda.untils.HttpsClientRequestFactory;
    import net.sf.json.JSONObject;
    import org.apache.commons.io.FileUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.util.LinkedMultiValueMap;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.client.RestTemplate;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.*;
    
    /**
     * 身份证orc验证
     * 前台传递,照片。
     */
    @RestController
    public class SFZIdcardApply {
        @Autowired
        RestTemplate restTemplate;
    
        @RequestMapping(value = "/idcard/SFZapply", method = RequestMethod.POST)
        @ResponseBody
        public JSONObject idcardApply(@RequestParam("myFile") MultipartFile myFile,String fileCategory) throws IOException {
    //    public JSONObject idcardApply() throws MalformedURLException {
            if (Objects.isNull(myFile) || myFile.isEmpty()) {//判断非空
                // logger.error("文件为空");
                return null;
            }
            //第三方地址
            String url=" https:";
            //必须转换一下,否则请求的时候会报错
            URL url1=new URL(url);
            //第三方要求的参数
            boolean formSdk=false;
            Set<String> tempFilePath = new HashSet<>();
            //第三方接口生成的token
            String token=new ThirdApply().apply().getJSONObject("data").get("token").toString();
            RestTemplate template = new RestTemplate(new HttpsClientRequestFactory());
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
            if (null != myFile && !myFile.isEmpty()) {
                //生成临时的文件,完成传输后再删除
                File tempFile = new File( UUID.randomUUID().toString()+myFile.getOriginalFilename());
                tempFilePath.add(tempFile.getAbsolutePath());
                FileUtils.copyInputStreamToFile(myFile.getInputStream(), tempFile);
                FileSystemResource fileSystemResource = new FileSystemResource(tempFile);
                params.add("fileData", fileSystemResource);
            }
            params.add("formSdk", formSdk);
            params.add("fileCategory", fileCategory);
            params.add("token", token);
            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(params,headers);
            ResponseEntity<JSONObject> response = template.postForEntity(url1.toString(), requestEntity, JSONObject.class);
            return response.getBody();
        }
    }
    
    
    
    更多相关内容
  • RestTemple

    2019-10-24 10:59:05
    简介 Spring Web 提供的轻量级Http Client,用于简化Http 调用 使用 public static void main(String[] args) { RestTemplate restTemplate = new RestTemplate(); // 用HTTP 的get方法去请求,并... String forOb...

    简介

    Spring Web 提供的轻量级Http Client,用于简化Http 调用
    

    使用

    public static void main(String[] args) {
            RestTemplate restTemplate = new RestTemplate();
            // 用HTTP 的get方法去请求,并返回一个对象
            String forObj = restTemplate.getForObject(
                    "http://localhost:8090/users/1",
                    String.class
            );
            System.out.println(forObj);
    
    		ResponseEntity<String> forEntity = restTemplate.getForEntity(
                    "http://localhost:8090/users/{id}",
                    String.class, 1
            );
            System.out.println(forEntity.getBody()); // 获取响应内容
            // 200 OK
            // 500 
            System.out.println(forEntity.getStatusCode());	// 获取状态码
        }
    
    
    
    

    乱码问题

    get 请求有乱码问题,
    restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(StandardCharsets.UTF_8));
    设置编码格式就好。

     public static void main(String[] args) {
            RestTemplate restTemplate = new RestTemplate();
            // 设置编码格式
            restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(StandardCharsets.UTF_8));
            // 用HTTP 的get方法去请求,并返回一个对象
            String forObj = restTemplate.getForObject(
                    "http://localhost:8090/users/1",
                    String.class
            );
            System.out.println(forObj);
        }
    

    post 暂时没遇到乱码问题

    展开全文
  • 简介项目中关于RestTemple的简单的get/post请求的方法就不进行过多的介绍,本案例中主要介绍各种请求参数逇传递 表单数据/json格式的数据传递, 请求的响应信息的处理;比如响应的信息是List 如果不做处理的话...

    简介

    项目中关于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();

    }

    }

    展开全文
  • 最近遇到一个棘手的问题,有个需求需要写一些调用外部API接口的方法,因为没有SDK,所以我只能手动的去调用,...我采用的使用springboot的restTemple来去调用接口,具体的实现是先定义一个controller接口,然后再在s

    最近遇到一个棘手的问题,有个需求需要写一些调用外部API接口的方法,因为没有SDK,所以我只能手动的去调用,框架采用的是springboot,Java语言编写,但是却出现了当调用异常,包括参数错误,token无效等直接抛出异常400,并没有获取到API接口的异常信息,只有我们自己抛出的异常信息。这样接口的调用方肯定不知道接口调用异常原因。

    一、RestTemple实现API调用

    我采用的使用springboot的restTemple来去调用接口,具体的实现是先定义一个controller接口,然后再在service层进行逻辑处理。

    @Autowired
    private RestTemplate restTemplate=new RestTemplate();
    
    //service层代码
    public String updateProduct(ProductParam productParam) {
        //spliceupdateProductUrl方法是拼接接口参数
        String url = spliceupdateProductUrl(productParam,"--此处填写API的接口地址--");
        //调用restTemple的get请求方法,直接返回response地址
        String response = restTemplate.getForObject(url,String.class);
        return response;
    }

    成功的结果:我们要求都是返回这种格式

    {"message":"","code":0,"data":{}}

    失败的结果:

    {

        "timestamp": "2020-05-29T07:44:38.031+0000",

        "status": 500,

        "error": "Internal Server Error",

        "message": "400 Bad Request,

        "path": "/updateProduct"

    }

    直接抛出异常,message是调用接口的异常信息,这里缺少一部分说明,为什么我会知道呢?因为我试过直接在postman上调用API的时候,相同的url和参数,postman是有说明的,如下:

    {"message":"111 is an invalid id","code":1000,"data":2014}

    二、追踪源代码寻找问题

    我首先是通过debug追踪源码的方式去找问题所在:

    第一步:RestTemple.class

    第二步:RestTemple.class

    第三步:RestTemple.class

    第四步:RestTemple.class

    第五步:RestTemple.class

    第六步:ResponseErrorHandler.class

    第七步:ResponseErrorHandler.class的默认处理器,继续追踪

    第八步:DefaultResponseErrorHandler.class

    第九步:DefaultResponseErrorHandler.class,这里我发现一个问题,算是机缘巧合,我发现spring不同版本存在Restemple的

    DefaultResponseErrorHandler源码存在差异,在下面这个方法的差异

    2.2.1.RELEASE版本的DefaultResponseErrorHandler.class

    2.2.6.RELEASE版本的DefaultResponseErrorHandler.class

    三、尝试解决问题,不断调试

    从我们不断的追踪源码发现了一些问题,这个时候我们可以升级一下spring的版本,再次运行上述代码

    成功的结果:我们要求都是返回这种格式

    {"message":"","code":0,"data":{}}

    失败的结果:

    {

        "timestamp": "2020-05-29T07:44:38.031+0000",

        "status": 500,

        "error": "Internal Server Error",

        "message": "400 Bad Request: [{"message":"111 is an invalid id","code":1000,"data":2014}],

        "path": "/updateProduct"

    }

    这个时候我们已经可以打印出了api接口的message,但是发现返回的结果是抛出的异常信息,默认的errorhandler是通过封装接口的异常信息到自己的异常中,然后再抛出异常,因此,我们可以选择捕获异常的message。

    捕获异常

    @Override
    public String updateProduct(ProductParam productParam) {
        String url = spliceupdateProductUrl(productParam,"--api接口url--");
        try {
            String response = restTemplate.getForObject(url,String.class);
            return response;
        }catch (Exception e){
            return e.getMessage();
        }
    
    }

     

    抛出异常运行结果

    400 Bad Request: [{"message":"111 is an invalid id","code":1000,"data":2014}]

    这个时候我们只要json字符串怎么办?

    {"message":"111 is an invalid id","code":1000,"data":2014}

    两种处理方法:

    一 自定义异常处理器

    就可以看到我们上面发现的自定义异常处理器了,我们可以自定义异常处理器,重写几个debug的时候走过的DefaultErrorHandle的方法,来自定义Message,不仅如此,我们也可以自定义一些其他的操作,例如编码格式转换等等。

    debug找到打印信息的地方,进入getErrorMessage方法看:

     

    第一步:写一个异常处理器

    public class ReturnErrorJsonHandler extends DefaultResponseErrorHandler {
    
        //调用父类方法就行
        public boolean hasError(ClientHttpResponse response) throws IOException {
            return super.hasError(response);
        }
        
        //重写该方法调用重写的getErrorMessage方法
        public void handleError(ClientHttpResponse response) throws IOException {
            HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
            if (statusCode == null) {
                byte[] body = this.getResponseBody(response);
                String message = this.getErrorMessage( body, this.getCharset(response));
                throw new UnknownHttpStatusCodeException(message, response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), body, this.getCharset(response));
            } else {
                this.handleError(response, statusCode);
            }
        }
        
        //重写该方法,让它返回我们要的格式
        private String getErrorMessage( @Nullable byte[] responseBody, @Nullable Charset charset) {
            if (ObjectUtils.isEmpty(responseBody)) {
                return "[no body]";
            } else {
                charset = charset == null ? StandardCharsets.UTF_8 : charset;
                int maxChars = 200;
                if (responseBody.length < maxChars * 2) {
                    //主要是这里
                    String message=  new String(responseBody, charset);
                    //Unicode 转换为汉字
                    return UnicodeToString(message);
                } else {
                    try {
                        Reader reader = new InputStreamReader(new ByteArrayInputStream(responseBody), charset);
                        CharBuffer buffer = CharBuffer.allocate(maxChars);
                        reader.read(buffer);
                        reader.close();
                        buffer.flip();
                        return buffer.toString() + "... (" + responseBody.length + " bytes)";
                    } catch (IOException var9) {
                        throw new IllegalStateException(var9);
                    }
                }
            }
        }
    
    
        /**
         * 将 Unicode 转换为汉字
         * */
        public String UnicodeToString(String str) {
            Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
            Matcher matcher = pattern.matcher(str);
            char ch;
            while (matcher.find()) {
                ch = (char) Integer.parseInt(matcher.group(2), 16);
                str = str.replace(matcher.group(1), ch + "");
            }
            return str;
        }
    
        //重写该方法调用重写的getErrorMessage方法
        protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
            String statusText = response.getStatusText();
            HttpHeaders headers = response.getHeaders();
            byte[] body = this.getResponseBody(response);
            Charset charset = this.getCharset(response);
            String message = this.getErrorMessage(body, charset);
            switch(statusCode.series()) {
                case CLIENT_ERROR:
                    throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
                case SERVER_ERROR:
                    throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
                default:
                    throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
            }
        }
    
    
    }

    uincode码和uincode转换为汉字参考链接:  https://blog.csdn.net/iteye_17867/article/details/82541722

    第二步:RestTemple放入自定义处理器

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate=new RestTemplate();
        restTemplate.setErrorHandler(new ReturnErrorJsonHandler());
        return restTemplate;
    }

     

    二 捕获HttpClientErrorException

    运行结果:现在即使是异常我们也可以直接抛出json数据了

    {"message":"111 is an invalid id","code":1000,"data":2014} 

    四、总结

    每当我们解决一个问题,都需要去做一个总结,从这次解决的手段来看,如果你不想要升级springboot的版本,你可能使用第二种方法,因为旧版本的异常类是没有封装message的。同时,我们发现,会看源码真的很重要,特别有利于我们去解决很多问题,debug追踪源码是很好的一种方法,发现问题,找到问题在寻求解决问题的方式。

     

    展开全文
  • restTemple中的超时

    2017-11-08 11:52:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • 关于RestTemple发送微信请求遇到的坑

    千次阅读 2018-02-08 10:32:36
    一、发送请求中文乱码RestTemple默认字符编码为ISO-8859-1,而我们需要使用UTF-8。不然getForObject得到的数据中文乱码,或者postForObject发送的数据乱码。二、数据转换Map是因为微信接口文档虽说返回的是 Json ...
  • 第四章:服务消费者(RestTemple+Ribbon+Feign) 前言 上两章节,介绍了下关于注册中心-Eureka的使用及高可用的配置示例,本章节开始,来介绍下服务和服务之间如何进行服务调用的,同时会讲解下几种不同方式的服务...
  • 部署配置中心 假如说订单服务,以后需要做水平拓展。(什么是水平拓展?) 水平拓展:一台服务器处理性能有限,可以部署多台,这就是水平拓展 水平拓展的情况下,当某一天我一台服务器的属性做了改动,所有...
  • boot-restTemple.rar,boot-restTemple,.gitignore,.project,pom.xml,target,test-classes,classes,META-INF,maven,com.khy.boot,boot-restTemple,pom.xml,pom.properties,MANIFEST.MF,com,khy,ApplicationMain.class...
  • 一、先用postman发送请求看是否成功。...二、检查url中是否有特殊字符需要保留的...,% 等都会在被 restTemple执行的时候进行URLencode。public &lt;T&gt; T execute(String url, HttpMethod method, @Nullable...
  •  第四章:服务消费者(RestTemple+Ribbon+Feign)     一点知识 在 SpringCloud 体系中,我们知道服务之间的调用是通过 http 协议进行调用的。而注册中心的主要目的就是维护这些服务的服务列表。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 437
精华内容 174
关键字:

restTemple