Java+SpringBoot实现接口代理转发

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。

RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端库。RestTemplate的请求方式包括了以下几种:

  • getForEntity
  • getForObject
  • postForEntity
  • put
  • delete

场景需求

本次的一个需求场景是:

用Java+Springboot服务器实现接口代理的二次转发,即接口接受到请求后,需要将请求转发到另一处服务器上,并将请求返回的结果返回给客户端

背景:Java 服务器作为客户端的上游服务器,需要负责返回所有请求数据,即使不是自己提供的功能,也要负责请求对应服务器,并将正确结果返回。

目的:这样做可以实现接口服务统一,也能解决前端跨域问题。

在调研了Java发送Http请求的多种方法后,选择了采用上述RestTemplate工具来实现。

为了更好的给出模拟场景,给出返回结果,因此本文首先设计一个简单的服务器来返回结果,而不是转发到目前互联网上的某一地址,比如百度、微博等(不易观察返回结果)

源码

首先给出一个简易服务器的实现源码。基于Socket来实现,等待客户端连接,并在有客户端连接后,返回特定的数据结构。

Server

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Server {


    public static void main(String[] args)
    {
        ServerSocket ss = null;
        Socket s = null;

        List<Socket> listSockets = new ArrayList<Socket>();

        // 1. 创建ServerSocket类型的对象并提供端口号
        try {

                ss = new ServerSocket(9999);

                // 2. 等待客户端的连接请求,调用accept方法
            while(true) {
                System.out.println("等待客户端的连接请求....");
                s = ss.accept();
                // 阻塞,直到有客户端连接
                InetAddress inetAddress = s.getInetAddress();
                System.out.println("客户端" + inetAddress + "已连接");

                // 测试样例 返回一个Map类型对象给客户端
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("code", 200);
                map.put("message", "测试样例message");
                map.put("success", true);

                String str = JSONObject.toJSONString(map);

                OutputStream outputStream = s.getOutputStream();
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
                BufferedWriter bufferedWriter =new BufferedWriter(outputStreamWriter);
				
                // 由于需要响应http请求,所以需要加上HTTP标识
                bufferedWriter.write("HTTP/1.1 200 OK\n");
                bufferedWriter.write("Content-Type:text/html;charset:utf-8\n");
                bufferedWriter.write("\n");
                bufferedWriter.write(str);
                bufferedWriter.flush();
                s.close();
                ss.close();

            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

有了服务器后,我们就可以测试我们的接口代理二次转发功能了。想法就是接口在收到请求后,根据请求的url,对其中的某些部分进行替换(因为转发的url跟请求的url是有关系的),然后再将新的url发送Http请求到新的服务器上获取返回结果。

接口代理二次转发源码:

@RestController
@RequestMapping(value = "/proxy")
public class ProxyCheck{

    @RequestMapping(value="/restTemplate", method = RequestMethod.GET)
    public Map<String, Object> apiProxyGet (HttpServletRequest request, HttpServletResponse res) throws UnsupportedEncodingException {
        // 获取请求url
        String url = request.getRequestURL().toString();
        // 获取url中携带的参数,即/resTemplate?后面的内容
        String query = request.getQueryString();


        String target = "http://127.0.0.1:9999";
		
        // 新的url拼接上旧url后的请求参数
        if (query != null && !query.equals("") && !query.equals("null")) {
            target = target + "?" + query;
        }
        
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        
        // 获取到请求头
        Enumeration<String> headerNames = request.getHeaderNames();

        HttpHeaders headers = new HttpHeaders();
        Map<String, String> headerMap = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String header = request.getHeader(headerName);
            headerMap.put(headerName, header);
            headers.add(headerName, header);
        }

		// 构造HttpEntity,新请求会携带本次请求的请求头
        HttpEntity entity = new HttpEntity<String>(headers);

        ResponseEntity<String> response = restTemplate.getForEntity(target, String.class, entity);

        Map<String, Object> list = JSONArray.parseObject(response.getBody(), Map.class);
		
        // 当需要分页时,返回分页信息
        res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        res.setHeader("Content-Range", String.valueOf(list.size()));
        res.setHeader("Access-Control-Expose-Headers", "Content-Range");

        return list;

    }

有了上述代码后,我们便可以进行测试。测试利用postman来发送请求。

首先启动Server,然后根据Controller定义的请求路径,在postman中发送请求,可以看到服务端和postman的返回结果为:

Server:
在这里插入图片描述

Postman返回结果:
在这里插入图片描述

可以看到我们在服务端定义的测试样例数据,经过接口的二次转发,成功进行了返回,实现了我们最开始的需求场景。本次的测试样例虽然简单,但是背景是在企业开发需求中真实遇到的,对场景进行了简化,但关键技术仍然进行了展示。最后代码中给出了携带请求头和需要分页时的解决方法,在某些场景下仍然受用。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐