HttpForwardHandler
ForwardHandler 简介
ForwardHandler
是 Tio-Boot 中用于请求转发的处理器,通常用于与第三方系统的集成。当请求在经过 Tio-Boot 的其他处理器(如 httpRequestInterceptor
、httpRequestRouter
、httpControllerRouter
)后仍未能处理并生成响应时,ForwardHandler
将接管该请求并转发到指定的目标系统(如其他 API 服务或微服务)。
Tio-Boot 中请求处理顺序
在 Tio-Boot 中,HTTP 请求按照如下顺序依次进行处理:
- httpRequestInterceptor:请求拦截器,用于执行请求的预处理工作,比如用户鉴权、权限验证和日志记录等操作。
- httpRequestRouter:请求路由器,根据请求的 URL 路径进行路由,决定将请求转发到相应的业务逻辑进行处理。
- httpGroovyRoutes:支持 Groovy 路由的处理器,可执行动态的 Groovy 脚本来响应请求。
- httpControllerRouter:控制器路由器,将请求交由具体的 Controller 进行业务处理。
- forwardHandler:请求转发处理器,当上面的步骤均未处理请求时,
forwardHandler
会将请求转发到外部的第三方系统处理。 - staticResourceHandler:静态资源处理器,负责处理诸如 CSS、JavaScript、图片等静态资源请求。
- notFoundHandler:处理 404 未找到的情况,若请求未能找到对应的处理路径,将返回 404 响应。
ForwardHandler 的用途
ForwardHandler
主要用于以下场景:
- 与外部 API 集成:当请求无法在 Tio-Boot 内部处理时,可将其转发到其他系统进行处理。
- 扩展已有系统:通过转发处理扩展现有系统的处理能力,避免对现有系统进行大幅度的改动。
当请求经过 Tio-Boot 的常规处理器后依然未返回 HttpResponse
,则请求会被 ForwardHandler
接管并转发给外部系统。例如,将请求转发到微服务网关或其他 API 服务器以继续处理业务逻辑。
示例:使用 ForwardHandler 集成第三方系统
下列示例展示了如何通过 ForwardHandler
将未处理的请求转发至第三方系统。在此示例中,未被 Tio-Boot 处理的请求将被转发至 http://192.168.3.9:8080
进行处理。
代码示例
1. 定义转发处理器
该类实现了 IHttpRequestHandler
接口,负责将请求转发至指定的第三方系统。TioHttpProxy.reverseProxy
方法用于将请求转发至目标服务器并将返回的响应写入当前请求的 HttpResponse
中。
package com.litongjava.maxkb.httphandler;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.maxkb.service.AppForwardRequestService;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.boot.http.forward.TioHttpProxy;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.handler.IHttpRequestHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MyRequestForwardHandler implements IHttpRequestHandler {
// 定义目标服务器 URL
private String remoteServerUrl = "http://192.168.3.9:8080";
@Override
public HttpResponse handle(HttpRequest httpRequest) throws Exception {
// 获取当前请求的 HttpResponse 对象
HttpResponse httpResponse = TioRequestContext.getResponse();
// 获取转发请求服务,处理自定义的逻辑,如请求头的处理或参数转换等
AppForwardRequestService forwardRequestService = Aop.get(AppForwardRequestService.class);
// 记录转发的请求信息,方便调试
log.info("forward:{},{}", httpRequest.getRequestLine().toString(), httpRequest.logstr());
// 使用 TioHttpProxy 将请求转发到远程服务器
TioHttpProxy.reverseProxy(remoteServerUrl, httpRequest, httpResponse, true, forwardRequestService);
// 返回最终的 HttpResponse
return httpResponse;
}
}
2. 在服务器配置中设置 ForwardHandler
通过在 Tio-Boot 的配置类中注册自定义的 ForwardHandler
,可以将未处理的请求转发到指定的服务器。
package com.litongjava.maxkb.config;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.maxkb.httphandler.MyRequestForwardHandler;
import com.litongjava.tio.boot.server.TioBootServer;
@AConfiguration
public class TioServerConfig {
// 初始化配置
@Initialization
public void config() {
// 设置自定义的转发处理器
TioBootServer.me().setForwardHandler(new MyRequestForwardHandler());
}
}
代码说明
MyRequestForwardHandler
:该类实现了IHttpRequestHandler
接口,负责接收未被 Tio-Boot 内置处理器处理的请求,并通过TioHttpProxy.reverseProxy
方法将其转发到目标服务器。AppForwardRequestService
:这是一个自定义的请求服务类,可以根据需求对转发请求进行处理,比如调整请求头、记录请求信息或处理响应体。此类中的saveRequest
和saveResponse
方法用于将请求和响应数据保存到数据库,以便后续分析或排查问题。- 日志记录:通过
log.info
记录每个请求的转发情况,便于在开发和生产环境中进行调试。
TioHttpProxy.reverseProxy
方法
该方法的签名如下:
public static void reverseProxy(String targetUrl, HttpRequest httpRequest, HttpResponse httpResponse, RequestProxyCallback callback)
targetUrl
:目标服务器的 URL。httpRequest
:当前的HttpRequest
对象,包含了请求的所有信息。httpResponse
:当前的HttpResponse
对象,用于返回响应给客户端。callback
:用于在请求和响应处理时执行自定义操作的回调接口。
自定义的请求处理服务
AppForwardRequestService
实现了 RequestProxyCallback
接口,提供自定义的请求和响应保存逻辑。
package com.litongjava.maxkb.service;
import java.util.Map;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Record;
import com.litongjava.tio.boot.http.forward.RequestProxyCallback;
import com.litongjava.tio.http.common.HeaderName;
import com.litongjava.tio.http.common.HeaderValue;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.RequestLine;
import com.litongjava.tio.utils.hutool.ZipUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AppForwardRequestService implements RequestProxyCallback {
// 保存请求信息
public void saveRequest(long id, String ip, HttpRequest httpRequest) {
StringBuffer stringBuffer = new StringBuffer();
RequestLine requestLine = httpRequest.getRequestLine();
stringBuffer.append(requestLine.toString()).append("\n");
Map<String, String> headers = httpRequest.getHeaders();
String contentType = httpRequest.getContentType();
// 处理不同类型的请求体
if (contentType != null) {
if (contentType.startsWith("application/json")) {
stringBuffer.append(httpRequest.getBodyString());
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
Map<String, Object[]> params = httpRequest.getParams();
for (Map.Entry<String, Object[]> e : params.entrySet()) {
stringBuffer.append(e.getKey() + ": " + e.getValue()[0]).append("\n");
}
} else if (contentType.startsWith("application/from-data")) {
Map<String, Object[]> params = httpRequest.getParams();
for (Map.Entry<String, Object[]> e : params.entrySet()) {
Object value = e.getValue()[0];
if (value instanceof String) {
stringBuffer.append(e.getKey()).append(":").append(e.getValue()[0]).append("\n");
} else {
stringBuffer.append(e.getKey()).append(":").append("binary \n");
}
}
}
}
// 将请求信息保存到数据库
Record record = Record.by("id", id).set("ip", ip).set("method", requestLine.getMethod().toString())
.set("uri", requestLine.getPath()).set("request_header", headers)
.set("request_body", stringBuffer.toString());
boolean saveResult = Db.save("sys_http
_forward_statistics", record, new String[] { "request_header" });
if (!saveResult) {
log.error("Failed to save request information to database: {}", id);
}
}
// 保存响应信息
@Override
public void saveResponse(long id, long elapsed, int statusCode, Map<HeaderName, HeaderValue> headers,
HeaderValue contentEncoding, byte[] body) {
Record record = Record.by("id", id).set("elapsed", elapsed).set("response_status", statusCode);
if (body != null && body.length > 0) {
if (HeaderValue.Content_Encoding.gzip.equals(contentEncoding)) {
record.set("response_body", new String(ZipUtil.unGzip(body)));
} else {
record.set("response_body", new String(body));
}
}
boolean updateResult = Db.update("sys_http_forward_statistics", record);
if (!updateResult) {
log.error("Failed to update response information in database: {}", id);
}
}
}
结论
通过以上的代码和示例,展示了如何在 Tio-Boot 项目中使用 ForwardHandler
来将请求转发至第三方系统,并对请求和响应数据进行自定义处理。该方案适用于与外部系统集成以及扩展现有项目的处理能力。