RestTemplate转发请求

需求

服务端A接收到来自于前端的请求后,要使用RestTemplate将请求转发给服务端B,然后将服务端B的返回转发给前端。
根据请求类型的不同,分为:

  • 常规请求转发
  • 文件上传转发
  • 文件下载转发

常规请求转发

@RequestMapping("/transmit")
public String transmit(HttpServletRequest request, HttpMethod method, @RequestBody(required = false) String body) {
    try {
        // 根据request,构造HttpHeaders
        HttpHeaders headers = new HttpHeaders();
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String name = (String)headerNames.nextElement();
            String value = request.getHeader(name);
            headers.add("Authorization", value);
        }

        // 复制 request 的参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
        // 附加参数值
        Set<String> keySet = parameterMap.keySet();
        for (String key : keySet) {
            String[] value = parameterMap.get(key);
            params.add(key, value[0]);
        }
        // 根据body内容填充requestEntity。对于form-data,body为空但parameterMap有值;对于raw,body不为空。
        HttpEntity<Object> requestEntity = (body!=null && !body.isEmpty())? new HttpEntity<>(body, headers): new HttpEntity<>(params, headers);

        // 构造URI。必须拼接出String url然后创建URI,否则会出现queryString %符号转%25的问题
        String destUrl = "http://192.168.1.98:9098/test";
        if (request.getQueryString()!=null && !request.getQueryString().isEmpty())  sUrl += "?" + request.getQueryString();
        URI destUri = new URI(destUrl);
        // 向服务请求
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.exchange(destUri, method, requestEntity, String.class);
        return responseEntity.getBody();
    } catch (Exception e) {
        e.printStackTrace();
        return "转发失败";
    }
}

其中:

  • 该方法会根据前端的请求自行使用相同的请求方式,例如get/post。
  • get所有参数都是附加在url中的,post所有参数都是附加在body中的。但对于post,会根据请求参数的形式有出入,例如form-data和raw参数附加的位置会有不同。
  • URI的构造务必拼接出String url然后创建URI,否则会出现queryString %符号转%25的问题。直接将url赋给RestTemplate也有相同问题。这是因为RestTemplate的转码与URLEncoder/URLDecoder的转码方式不同。
  • 该方法只能用于常规的请求,不能用于文件转发。

文件上传转发

前端上传的文件需要使用MultipartFile接收。而RestTemplate发送文件需要使用FileSystemResourceByteArrayResource
然而两个类都存在限制:

  • MultipartFile不能直接转为FileSystemResource
  • RestTemplate会调用文件对象的getFilename()方法(方法名中name的n是小写),若失败则会认为这是个常规的参数而非文件。ByteArrayResource并未提供该方法。

有两种转发方案:

  1. 临时文件方案。服务端A接收MultipartFile后存储在本地,然后将本地File转为FileSystemResource。发送FileSystemResource对象。该方案会在本地生成临时文件,额外调用多次I/O,且生成的临时文件需要进行清理。
  2. 派生类方案。创建ByteArrayResource的派生类并添加getFilename()方法,服务端A接收MultipartFile后,转为ByteArrayResource派生对象。该方案不会生成临时文件,推荐

临时文件方案

MultipartFile file; // 接收到的MultipartFile
File tempFile = new File("D://temp/" + file.getOriginalFilename());
file.transferTo(tempFile);
FileSystemResource fileSystemResource = new FileSystemResource(tempFile);
// 放入参数中
MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
params.add("file", fileSystemResource);

然后将params作为参数交给RestTemplate发送即可。

派生类方案

创建ByteArrayResource的派生类:

import org.springframework.core.io.ByteArrayResource;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

public class MultipartFileResource extends ByteArrayResource {

    private String filename;

    public MultipartFileResource(MultipartFile multipartFile) throws IOException {
        super(multipartFile.getBytes());
        this.filename = multipartFile.getOriginalFilename();
    }

    @Override
    public String getFilename() {
        return this.filename;
    }
}

然后使用派生类对MultipartFile进行转换:

MultipartFile file; // 接收到的MultipartFile
ByteArrayResource byteArrayResource = new MultipartFileResource(file);
// 放入参数中
MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
params.add("file", byteArrayResource);

然后将params作为参数交给RestTemplate发送即可。
派生类方案代码实例:

@RequestMapping("/upload")
public String upload(HttpServletRequest request, HttpMethod method, @RequestParam("file") MultipartFile file, @RequestBody(required = false) String body) {
    try {
        // 复制HttpHeaders
        HttpHeaders headers = new HttpHeaders();
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String name = (String)headerNames.nextElement();
            String value = request.getHeader(name);
            logger.info(name + " : " + value);
            headers.add("Authorization", value);
        }

        // 复制 request 的参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<String> keySet = parameterMap.keySet();
        for (String key : keySet) {
            String[] value = parameterMap.get(key);
            params.add(key, value[0]);
        }
        // 文件对象单独附加
        ByteArrayResource resource = new MultipartFileResource(file);
        params.add("file", resource);

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);

        // 构造URI。必须拼接出String url然后创建URI,否则会出现queryString %符号转%25的问题
        String destUrl = "http://192.168.1.98:9098/test";
        if (request.getQueryString()!=null && !request.getQueryString().isEmpty())  destUrl += "?" + request.getQueryString();
        URI destUri = new URI(destUrl);

        // 向服务请求
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.exchange(destUri, method, requestEntity, String.class);
        return responseEntity.getBody();
    } catch (Exception e) {
        e.printStackTrace();
        return new Response<String>().setCode(Response.CODE_FAIL).setMessage(Response.MSG_FAIL).toJSONString();
    }
}

文件下载转发

服务端A收到前端发来的文件下载请求,将该请求转发给服务端B,然后服务端A将服务端B的返回转发给前端。
与前面不同的是下载会返回一个byte[]。注意需要修改HttpServletResponseHttpHeaders来告知前端浏览器创建下载任务。

@RequestMapping("/download/")
public byte[] download(HttpServletRequest request, HttpServletResponse response, HttpMethod method, @RequestBody(required = false) String body) {
    try {
        // 复制HttpHeaders
        HttpHeaders headers = new HttpHeaders();
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String name = (String)headerNames.nextElement();
            String value = request.getHeader(name);
            logger.info(name + " : " + value);
            headers.add("Authorization", value);
        }

        // 复制 request 的参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<String> keySet = parameterMap.keySet();
        for (String key : keySet) {
            String[] value = parameterMap.get(key);
            params.add(key, value[0]);
        }
        // 根据body内容填充requestEntity。对于form-data,body为空但parameterMap有值;对于raw,body不为空。
        HttpEntity<Object> requestEntity = (body!=null && !body.isEmpty())? new HttpEntity<>(body, headers): new HttpEntity<>(params, headers);

        // 构造URI。必须拼接出String url然后创建URI,否则会出现queryString %符号转%25的问题
        String destUrl = "http://192.168.1.98:9098/test";
        if (request.getQueryString()!=null && !request.getQueryString().isEmpty())  destUrl += "?" + request.getQueryString();
        URI destUri = new URI(destUrl);
        // 向服务请求
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<byte[]> responseEntity = restTemplate.exchange(destUri, method, requestEntity, byte[].class);
        // 修改HttpServletResponse的HttpHeaders
        HttpHeaders responseHeaders = responseEntity.getHeaders();
        response.setContentType(responseHeaders.getContentType().toString());
        response.addHeader("Content-Disposition", responseHeaders.get("Content-Disposition").get(0));
        return responseEntity.getBody();
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值