cc.shacocloud.mirage.web.HandlerExecutionChain Maven / Gradle / Ivy
package cc.shacocloud.mirage.web;
import cc.shacocloud.mirage.utils.FutureUtils;
import cc.shacocloud.mirage.web.exception.HttpRequestBindingException;
import cc.shacocloud.mirage.web.http.MediaType;
import cc.shacocloud.mirage.web.util.RoutingContextUtils;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import lombok.Setter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 请求响应处理器
*
* 为整个请求响应出入口的处理器类
*/
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final VertxInvokeHandler invokeHandler;
@Nullable
private Set mediaTypes;
@Nullable
private List interceptors;
@Setter
@Nullable
protected HandlerExceptionResolverComposite exceptionResolverComposite;
public static final Buffer EMPTY = Buffer.buffer(Unpooled.EMPTY_BUFFER);
public HandlerExecutionChain(VertxInvokeHandler invokeHandler,
RequestMappingInfo requestMapping,
@Nullable HandlerInterceptorComposite interceptorComposite) {
this.invokeHandler = invokeHandler;
if (interceptorComposite != null) {
this.interceptors = interceptorComposite.match(requestMapping.getPaths());
}
String[] produces = requestMapping.getProduces();
if (!ObjectUtils.isEmpty(produces)) {
this.mediaTypes = new HashSet<>(MediaType.parseMediaTypes(Arrays.asList(produces)));
}
}
/**
* 处理请求,请求的入口方法
*
* @param ctx 请求上下文
*/
public void handle(io.vertx.ext.web.RoutingContext ctx) {
RoutingContext routingContext = createVertXRoutingContext(ctx);
routingContext.put(RoutingContext.RECEIVE_REQUEST_TIME, System.currentTimeMillis());
if (logger.isDebugEnabled()) {
logger.debug("路由器收到待处理请求:" + routingContext.normalisedPath());
logger.debug("当前请求实际绑定的处理器方法:" + this.invokeHandler);
}
if (!CollectionUtils.isEmpty(mediaTypes)) {
routingContext.put(VertxInvokeHandler.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
HttpRequest request = routingContext.request();
HttpResponse response = routingContext.response();
AtomicInteger interceptorIndex = new AtomicInteger(0);
// 注册响应释放后的处理
ctx.addEndHandler(res -> triggerAfterCompletion(request, response, interceptorIndex, res.cause()));
try {
// 执行拦截器的 preHandle 方法
applyPreHandle(request, response, interceptorIndex)
.compose(goOn -> {
if (goOn) { // 如果拦截器返回true则继续调用具体方法
return invokeHandler.invokeAndHandleRoutingContext(routingContext);
}
return Future.succeededFuture();
})
.onFailure(cause -> processExceptionHandler(request, response, cause))
.onSuccess(res -> processResult(request, response, res));
} catch (Exception e) {
processExceptionHandler(request, response, e);
}
}
/**
* 委派给 {@link #processExceptionHandler} 方法执行,用于{@link io.vertx.ext.web.Route#failureHandler(Handler)}方法使用
*/
void processException(io.vertx.ext.web.RoutingContext ctx) {
RoutingContext routingContext = createVertXRoutingContext(ctx);
processExceptionHandler(routingContext.request(), routingContext.response(), ctx.failure());
}
/**
* 处理异常情况
*/
void processExceptionHandler(HttpRequest request, HttpResponse response, Throwable cause) {
if (logger.isDebugEnabled()) {
logger.debug("请求 '" + request.context().normalisedPath() + "' 处理发生例外,进行异常解析处理逻辑...", cause);
}
Future resFuture;
if (this.exceptionResolverComposite != null) {
resFuture = this.exceptionResolverComposite.resolveException(request, response, this.invokeHandler, cause)
.compose(res -> {
if (res == null)
return Future.failedFuture(new HttpRequestBindingException("找不到对应的异常处理程序,无法处理当前异常信息: ", cause));
return Future.succeededFuture(res);
});
} else {
resFuture = Future.failedFuture(new HttpRequestBindingException("未设置异常处理程序,无法处理当前异常信息: ", cause));
}
resFuture.onComplete(ar -> {
if (ar.failed()) {
exceptionThatCannotBeResolved(response, ar.cause());
} else {
processResult(request, response, ar.result());
}
});
}
/**
* 处理结果
*
* @see VertxInvokeHandler#resultHandle(Object, HttpResponse)
*/
void processResult(HttpRequest request, HttpResponse response, Object result) {
if (!response.ended()) {
Future resultFuture;
if (!(result instanceof Buffer)) {
resultFuture = this.invokeHandler.resultHandle(result, response);
} else {
resultFuture = Future.succeededFuture((Buffer) result);
}
resultFuture.onComplete(res -> {
if (res.succeeded()) {
Buffer resResult = res.result();
if (resResult == null) resResult = EMPTY;
response.end(resResult);
if (logger.isDebugEnabled()) {
RoutingContext routingContext = request.context();
long receiveRequestTime = routingContext.get(RoutingContext.RECEIVE_REQUEST_TIME, System::currentTimeMillis);
long handleTime = System.currentTimeMillis() - receiveRequestTime;
logger.debug(routingContext.normalisedPath() + " 请求处理完成,耗时:" + handleTime + "ms,响应结果: " + LogFormatUtils.formatValue(resResult, false));
}
} else {
exceptionThatCannotBeResolved(response, res.cause());
}
});
} else {
if (logger.isWarnEnabled() && result != null) {
logger.warn("响应已经被关闭,丢弃结果: [" + LogFormatUtils.formatValue(result, true) + "]");
}
}
}
/**
* 无法解决的异常
*/
protected void exceptionThatCannotBeResolved(HttpResponse response, Throwable cause) {
HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR;
response.setStatusCode(responseStatus.code());
response.setStatusMessage(responseStatus.reasonPhrase());
if (logger.isErrorEnabled()) {
logger.error("", cause);
}
response.end();
}
/**
* 执行匹配上的拦截器的 preHandle 方法
*/
Future applyPreHandle(HttpRequest request,
HttpResponse response,
AtomicInteger interceptorIndex) {
if (!ObjectUtils.isEmpty(this.interceptors)) {
// 按序执行拦截器 preHandle
return FutureUtils.sequential(this.interceptors,
(interceptor, termination) -> interceptor.preHandle(request, response, this.invokeHandler).onSuccess(b -> termination.set(!b)),
interceptorIndex,
true);
}
return Future.succeededFuture(true);
}
/**
* 在当前响应完全写入网络时调用
*/
void triggerAfterCompletion(HttpRequest request,
HttpResponse response,
AtomicInteger interceptorIndex,
@Nullable Throwable cause) {
if (!ObjectUtils.isEmpty(this.interceptors) && interceptorIndex.get() != 0) {
// 减少1,因为自增从0开始,否者将导致执行未执行的拦截器
interceptorIndex.decrementAndGet();
// 逆序执行拦截器 afterCompletion
Future interceptorFuture = FutureUtils.sequential(this.interceptors,
(interceptor, termination) -> interceptor.afterCompletion(request, response, this.invokeHandler, cause),
interceptorIndex,
false);
interceptorFuture.onFailure(ex -> logger.error("HandlerInterceptor.afterCompletion 抛出例外!", ex));
}
}
protected RoutingContext createVertXRoutingContext(io.vertx.ext.web.RoutingContext ctx) {
return RoutingContextUtils.createVertXRoutingContext(ctx);
}
}