SpringBoot 服务器实现接口代理转发,还可以对数据进行二次处理哦 |
您所在的位置:网站首页 › 前端代理访问接口 › SpringBoot 服务器实现接口代理转发,还可以对数据进行二次处理哦 |
今天要实现一个功能: “用 Java+SpringBoot 服务器实现接口代理转发,对数据进行二次处理,然后返回给客户端。 ”背景:Java 服务器作为客户端的上游服务器,需要负责返回所有请求数据,即使不是自己提供的功能,也要负责请求对应服务器,并将正确结果返回。 目的:这样做可以实现接口服务统一,也能解决前端跨域问题。 这里列举两个场景。 场景一:调用其他服务器,将结果直接返回给客户端要实现的功能是 浏览器请求:https://10.28.10.11:8081/proxy/homeList 希望请求转发到 http://10.28.11.20:4000/homeList 源码: package com.maomao.apm.controller; import au.com.bytecode.opencsv.CSVReader; import com.pinganfu.dochelper.annotation.PAFDoc; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @RestController public class ProxyController extends BaseController { private String targetAddr = "https://10.28.10.11:4000"; /** * 代理所有请求 * * @param request * @param response * @throws Exception */ @RequestMapping(value = "/proxy/**", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public void proxy(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException { // String url = URLDecoder.decode(request.getRequestURL().toString(), "UTF-8"); URI uri = new URI(request.getRequestURI()); String path = uri.getPath(); String query = request.getQueryString(); String target = targetAddr + path.replace("/proxy", ""); if (query != null && !query.equals("") && !query.equals("null")) { target = target + "?" + query; } URI newUri = new URI(target); // 执行代理查询 String methodName = request.getMethod(); HttpMethod httpMethod = HttpMethod.resolve(methodName); if (httpMethod == null) { return; } ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(newUri, httpMethod); Enumeration headerNames = request.getHeaderNames(); // 设置请求头 while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); Enumeration v = request.getHeaders(headerName); List arr = new ArrayList(); while (v.hasMoreElements()) { arr.add(v.nextElement()); } delegate.getHeaders().addAll(headerName, arr); } StreamUtils.copy(request.getInputStream(), delegate.getBody()); // 执行远程调用 ClientHttpResponse clientHttpResponse = delegate.execute(); response.setStatus(clientHttpResponse.getStatusCode().value()); // 设置响应头 clientHttpResponse.getHeaders().forEach((key, value) -> value.forEach(it -> { response.setHeader(key, it); })); StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream()); } } “原理是代理服务器(当前代码所在服务器)在接收到客户端的请求时,新建一个请求去请求真实服务器地址,并且把客户端请求地址里 “proxy” 关键字删除。等真实服务器返回数据后,把数据直接返回给客户端。 ”这个方法做到了统一,假设是 A 客户端请求 B 服务器实际获取 C 服务器的内容,这段代码可以做到 B 服务器请求 c 服务器的所有接口都能代理。只需要客户端请求时,在接口里加上 “proxy” 字样。 场景二:调用其他服务器,将结果进行处理后返回给客户端场景一可以解决大部分代理问题。但是如果你需要对 C 服务器返回的数据进行二次加工,只需要做一点点改动。 这里举例一个 B 服务器获取 c 服务器的 CSV 文件,并将返回的 CSV 文件流转换成 Json 返回给客户端。 要实现的功能是 浏览器请求:https://10.28.10.11:8081/proxy/homedetail.csv 希望请求转发到 http://10.28.11.187:8088/homedetail.csv 最后解析 homedetail.csv 并返回 json 数组 源码: package com.maomao.apm.controller; import au.com.bytecode.opencsv.CSVReader; import com.pinganfu.dochelper.annotation.PAFDoc; import org.apache.logging.log4j.LogManager; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @RestController public class ProxyController extends BaseController { private String targetAddrUba = "http://10.28.11.187:8088"; /** * 代理ubaReport请求所有请求 * * @param request * @param response * @return * @throws Exception */ @RequestMapping(value = "/ubaProxy/**") public List proxyUba(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException { // String url = URLDecoder.decode(request.getRequestURL().toString(), "UTF-8"); URI uri = new URI(request.getRequestURI()); String path = uri.getPath(); String query = request.getQueryString(); String target = targetAddrUba + path.replace("/ubaProxy", ""); if (query != null && !query.equals("") && !query.equals("null")) { target = target + "?" + query; } URI newUri = new URI(target); // 执行代理查询 String methodName = request.getMethod(); HttpMethod httpMethod = HttpMethod.resolve(methodName); if (httpMethod == null) { return null; } ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(newUri, httpMethod); Enumeration headerNames = request.getHeaderNames(); // 设置请求头 while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); Enumeration v = request.getHeaders(headerName); List arr = new ArrayList(); while (v.hasMoreElements()) { arr.add(v.nextElement()); } delegate.getHeaders().addAll(headerName, arr); } StreamUtils.copy(request.getInputStream(), delegate.getBody()); // 执行远程调用 ClientHttpResponse clientHttpResponse = delegate.execute(); // response.setStatus(clientHttpResponse.getStatusCode().value()); // 设置响应头 // clientHttpResponse.getHeaders().forEach((key, value) -> value.forEach(it -> { // response.setHeader(key, it); // })); CSVReader csvReader = new CSVReader(new InputStreamReader(clientHttpResponse.getBody(), "utf-8")); List stringsList = csvReader.readAll(); return stringsList; } }pom.xml net.sf.opencsv opencsv 2.3 org.json json 20090211这里用了一个 “ubaProxy” 作为该请求功能的关键字。原理跟场景一相同。只是在返回数据之后对数据进行了CSV转Json的操作。具体代码: CSVReader csvReader = new CSVReader(new InputStreamReader(clientHttpResponse.getBody(), "utf-8")); List stringsList = csvReader.readAll(); return stringsList; “注意,上面代码里将response.setHeader()相关代码注释掉了。原因是经过二次加工返回的数据,与加工前的数据长度是不一样的,如果复用加工前的 reponse header,会导致content-length数值不准确。如果加工后的数据比原始数据多,而content-length数值还是加工前的数值,会导致返回给客户端的数据不完整。如果不填,默认会返回所有数据。 ”参考文档:《Java+springboot 实现 nginx 反向代理功能》《JAVA:将 CSV 文件转换成 JSON》 path 不断重复打印,导致内存溢出中间还遇到在打印代码中的path时,path的值一直在循环重复,控制台不停地打印,看起来像死循环了。 最后服务器撑不住,可能回报内存溢出的异常。 java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3664 解决办法网上有很多答案是说把 JVM 的内存设置得更大一些: 手动设置Heap size 修改TOMCAT_HOME/bin/catalina.bat,在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行: Java代码 set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m或将application.properties里面的大小值设置得更大一些。 //src/main/resources/application.properties #将文件写入磁盘的阈值。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。 spring.servlet.multipart.file-size-threshold=2KB #设置单个文件的大小, spring.servlet.multipart.max-file-size=100MB #最大请求大小。值可以使用后缀“MB”或“KB”分别表示兆字节或千字节。 spring.servlet.multipart.max-request-size=100MB但是我这里不是因为内存设置不够引起的。 出问题的原因是类名上面使用了@Controller注解,把它改成@RestController就好了。 原理是@Controller注解会认为请求的是一个页面,而不是数据,如果没找到页面,会继续转发往下找,如果一直找不到,就死循环了。而@RestController注解只关心请求的内容,请求到内容就直接返回,请求不到内容也返回空数据,不会进行转发。 作者:"毛毛" 来源链接: https://maomao.ink/index.php/IT/1644.html |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |