如何用Spring Boot实现部分代理?已完成Rest Mock测试服务器基础代码
我已经搭建了一个用于REST接口测试的Mock服务器,完成了控制器、请求映射和实体类的编写,代码如下:
@RestController public class GreetingController { private static final String template = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); @RequestMapping("/greeting") public GreetingResponse greeting(@RequestParam(value="name", defaultValue="World") String name) { return new GreetingResponse(counter.incrementAndGet(), String.format(template, name)); } }
现在我想实现部分代理功能——也就是让服务器既能处理自己定义的Mock接口(比如上面的/greeting),又能把其他特定的请求转发到真实的后端服务器。请问接下来该怎么实现?
嘿,你已经把Mock服务器的核心部分搞定了,要实现这种“Mock+代理”混合的需求其实很简单,核心思路就是让你的服务根据请求的路径、参数或者其他规则,决定是返回Mock数据,还是把请求转发到真实后端。下面给你两种常用的实现方式,你可以根据场景选择:
方式一:直接在控制器中添加代理逻辑(适合简单场景)
如果你的需求比较明确——比如固定某些路径走Mock,其他走代理,直接在现有控制器或者新增一个代理控制器里写逻辑就行:
- 首先,注入
RestTemplate(或者Spring 5+推荐的WebClient,异步场景更友好),用来转发请求到真实服务器。记得先在配置类里定义这个Bean:
@Configuration public class AppConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
- 新增一个代理控制器,处理需要转发的请求。比如我们要把所有
/api/**开头的请求转发到真实服务器:
@RestController public class ProxyController { private final RestTemplate restTemplate; // 真实后端的基础地址,建议放到配置文件里,方便修改 private static final String REAL_BACKEND_BASE_URL = "https://your-real-service.com"; // 构造注入RestTemplate public ProxyController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } // 处理GET类型的代理请求 @GetMapping("/api/**") public ResponseEntity<Object> proxyGet(HttpServletRequest request) throws URISyntaxException { // 截取请求路径,拼接成真实后端的完整URL String requestPath = request.getRequestURI().substring("/api".length()); URI targetUri = new URI(REAL_BACKEND_BASE_URL + requestPath); // 携带原请求的头信息和参数转发 HttpHeaders headers = new HttpHeaders(); request.getHeaderNames().forEach(header -> headers.add(header, request.getHeader(header))); HttpEntity<Void> requestEntity = new HttpEntity<>(headers); return restTemplate.exchange(targetUri, HttpMethod.GET, requestEntity, Object.class); } // 同理,处理POST类型的请求(需要读取原请求体) @PostMapping("/api/**") public ResponseEntity<Object> proxyPost(HttpServletRequest request) throws IOException, URISyntaxException { String requestPath = request.getRequestURI().substring("/api".length()); URI targetUri = new URI(REAL_BACKEND_BASE_URL + requestPath); // 读取原请求的请求体 String requestBody = new BufferedReader(new InputStreamReader(request.getInputStream())) .lines().collect(Collectors.joining("\n")); // 携带头信息和请求体转发 HttpHeaders headers = new HttpHeaders(); request.getHeaderNames().forEach(header -> headers.add(header, request.getHeader(header))); HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers); return restTemplate.exchange(targetUri, HttpMethod.POST, requestEntity, Object.class); } // 可以继续添加PUT、DELETE等其他HTTP方法的代理逻辑 }
这样配置后,访问/greeting会返回你的Mock数据,所有/api开头的请求就会自动转发到真实后端。
方式二:用Spring Cloud Gateway实现灵活路由(推荐复杂场景)
如果你的需求更复杂——比如需要动态调整代理规则、添加请求过滤、限流熔断等,Spring Cloud Gateway是更优的选择,它专门用来做API网关,能轻松实现“部分Mock、部分代理”的路由逻辑:
- 先添加Spring Cloud Gateway的Maven依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
- 在
application.yml里配置路由规则:
spring: cloud: gateway: routes: # 第一条路由:处理Mock接口,转发到本地的GreetingController - id: mock-greeting-route uri: lb://localhost:${server.port} # 指向本地服务 predicates: - Path=/greeting/** # 匹配/greeting开头的路径 # 第二条路由:代理到真实后端服务器 - id: proxy-real-backend-route uri: https://your-real-service.com # 真实后端地址 predicates: - Path=/api/** # 匹配/api开头的路径 # 可选:添加过滤器,比如给请求加个标识头,方便后端识别是代理过来的 filters: - AddRequestHeader=X-Proxy-Source, Mock-Server
配置完成后,Gateway会自动根据请求路径转发:/greeting走本地Mock,/api/**直接转发到真实后端。而且Gateway支持更多高级功能,比如负载均衡、重试、熔断等,适合复杂的测试场景。
额外小技巧
如果需要更灵活的判断逻辑(比如根据请求参数决定是Mock还是代理),可以在控制器里加个简单的判断:
@GetMapping("/dynamic-path") public ResponseEntity<?> handleDynamicRequest(@RequestParam(required = false) String useMock) { if ("true".equals(useMock)) { // 返回Mock数据 return ResponseEntity.ok(new GreetingResponse(1L, "Hello Mock User!")); } else { // 转发到真实后端 URI targetUri = new URI(REAL_BACKEND_BASE_URL + "/dynamic-path"); return restTemplate.exchange(targetUri, HttpMethod.GET, null, Object.class); } }
这样用户可以通过useMock=true参数来切换Mock和真实请求,测试起来更灵活。
内容的提问来源于stack exchange,提问作者DmitryJS

