All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.shacocloud.mirage.restful.HandlerExecutionChain Maven / Gradle / Ivy

package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.annotation.PathPatterns;
import cc.shacocloud.mirage.restful.exception.HttpRequestBindingException;
import cc.shacocloud.mirage.restful.http.MediaType;
import cc.shacocloud.mirage.restful.util.PathUtil;
import cc.shacocloud.mirage.restful.util.RoutingContextUtils;
import cc.shacocloud.mirage.utils.FutureUtils;
import cc.shacocloud.mirage.utils.LogFormatUtils;
import cc.shacocloud.mirage.utils.PathMatcher;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.collection.CollUtil;
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 lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 请求响应处理器
 * 

* 为整个请求响应出入口的处理器类 */ @Slf4j public class HandlerExecutionChain { public static final Buffer EMPTY = Buffer.buffer(Unpooled.EMPTY_BUFFER); private final VertxInvokeHandler invokeHandler; /** * 异常解析组合处理程序 */ @Setter @Nullable protected HandlerExceptionResolverComposite exceptionResolverComposite; @Nullable private Set mediaTypes; /** * 当前接口匹配的拦截器,如果接口路径模式为正则模式当前参数为空,使用 {@link #interceptorMappingInfos} */ @Nullable private List interceptors; /** * 当接口模式为正则模式时,该属性生效,在运行时间进行接口拦截器匹配 */ @Nullable private List interceptorMappingInfos; /** * 路径匹配器 */ @Setter private PathMatcher pathMatcher = PathUtil.DEFAULT_PATH_MATCHER; public HandlerExecutionChain(VertxInvokeHandler invokeHandler, RequestMappingInfo requestMapping, @Nullable HandlerInterceptorComposite interceptorComposite) { this.invokeHandler = invokeHandler; if (interceptorComposite != null) { // 如果是正则模式在运行时匹配拦截器 if (PathPatterns.REGEX.equals(requestMapping.getPathPatterns())) { this.interceptorMappingInfos = interceptorComposite.getInterceptorMappings(); } else { this.interceptors = interceptorComposite.match(requestMapping.getPaths()); } } String[] produces = requestMapping.getProduces(); if (ArrayUtil.isNotEmpty(produces)) { this.mediaTypes = new HashSet<>(MediaType.parseMediaTypes(Arrays.asList(produces))); } } /** * 处理请求,请求的入口方法 * * @param ctx 请求上下文 */ public void handle(io.vertx.ext.web.RoutingContext ctx) { final RoutingContext routingContext = createVertXRoutingContext(ctx); routingContext.put(RoutingContext.RECEIVE_REQUEST_TIME, System.currentTimeMillis()); if (log.isDebugEnabled()) { log.debug("路由器收到待处理请求:{} {}", routingContext.request().method(), routingContext.normalizedPath()); log.debug("当前请求实际绑定的处理器方法:" + this.invokeHandler); } if (CollUtil.isNotEmpty(mediaTypes)) { routingContext.put(VertxInvokeHandler.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes); } AtomicInteger interceptorIndex = new AtomicInteger(0); // 注册响应释放后的处理 ctx.addEndHandler(res -> { if (log.isDebugEnabled()) { long receiveRequestTime = routingContext.get(RoutingContext.RECEIVE_REQUEST_TIME, System::currentTimeMillis); long handleTime = System.currentTimeMillis() - receiveRequestTime; log.debug(routingContext.normalizedPath() + " 请求处理完成,耗时:" + handleTime + "ms"); } triggerAfterCompletion(routingContext, interceptorIndex, res.cause()); }); try { // 执行拦截器的 preHandle 方法 applyPreHandle(routingContext, interceptorIndex) .compose(goOn -> { if (goOn) { // 如果拦截器返回true则继续调用具体方法 return invokeHandler.invokeAndHandleRoutingContext(routingContext); } return Future.succeededFuture(); }) .onFailure(cause -> processExceptionHandler(routingContext, cause)) .onSuccess(res -> processResult(routingContext, res)); } catch (Exception e) { processExceptionHandler(routingContext, 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, ctx.failure()); } /** * 处理异常情况 */ void processExceptionHandler(@NotNull RoutingContext context, Throwable cause) { final HttpRequest request = context.request(); final HttpResponse response = context.response(); if (log.isDebugEnabled()) { log.debug("请求 '" + request.context().normalizedPath() + "' 处理发生例外,进行异常解析处理逻辑..."); } Future resFuture; if (this.exceptionResolverComposite != null) { resFuture = this.exceptionResolverComposite.resolveException(request, response, this.invokeHandler, cause) .compose(res -> { if (res == HandlerExceptionResolver.SKIP) 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 { Object result = ar.result(); if (result != HandlerExceptionResolver.FINISH) { processResult(context, result); } } }); } /** * 处理结果 * * @see VertxInvokeHandler#resultHandle(Object, HttpResponse) */ void processResult(@NotNull RoutingContext context, Object result) { final HttpResponse response = context.response(); 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 (!response.ended()) { if (resResult == null) resResult = EMPTY; response.end(resResult); } else { if (log.isWarnEnabled() && resResult != null) { log.warn("响应已经被关闭,丢弃结果: [" + LogFormatUtils.formatValue(result, true) + "]"); } } } else { processExceptionHandler(context, res.cause()); } }); } else { if (log.isWarnEnabled() && result != null) { log.warn("响应已经被关闭,丢弃结果: [" + LogFormatUtils.formatValue(result, true) + "]"); } } } /** * 无法解决的异常 */ protected void exceptionThatCannotBeResolved(@NotNull HttpResponse response, Throwable cause) { // 如果当前状态码还是成功状态码则改为 500 int statusCode = response.getStatusCode(); if (200 <= statusCode && statusCode <= 300) { HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR; } if (log.isErrorEnabled()) { log.error("无法解决的异常信息:", cause); } response.end(); } /** * 执行请求的预处理方法,如果当前请求存在映射的拦截器则优先使用,如果没有则尝试匹配拦截器映射信息 */ Future applyPreHandle(RoutingContext context, AtomicInteger interceptorIndex) { // 优先执行匹配好的拦截器 if (CollUtil.isNotEmpty(this.interceptors)) { return applyPreHandle(context, interceptorIndex, this.interceptors); } // 尝试匹配拦截器映射信息 else if (CollUtil.isNotEmpty(this.interceptorMappingInfos)) { // 匹配当前路径的拦截器 List matchInterceptorList = PathUtil.match(this.interceptorMappingInfos, this.pathMatcher, context.normalizedPath()); if (CollUtil.isNotEmpty(matchInterceptorList)) { return applyPreHandle(context, interceptorIndex, matchInterceptorList); } } return Future.succeededFuture(true); } /** * 执行匹配上的拦截器的 preHandle 方法 */ Future applyPreHandle(@NotNull RoutingContext context, AtomicInteger interceptorIndex, List interceptors) { // 将当前请求匹配的拦截器挂载在请求上下文上 context.put(RoutingContext.REQUEST_INTERCEPTORS, interceptors); final HttpRequest request = context.request(); final HttpResponse response = context.response(); return FutureUtils.sequential(interceptors, (interceptor, termination) -> { // 拦截器前置处理 return interceptor.preHandle(request, response, this.invokeHandler) .onSuccess(b -> termination.set(!b)); }, interceptorIndex, true); } /** * 在当前响应完全写入网络时调用 */ void triggerAfterCompletion(@NotNull RoutingContext context, @NotNull AtomicInteger interceptorIndex, @Nullable Throwable cause) { List interceptors = context.get(RoutingContext.REQUEST_INTERCEPTORS); if (CollUtil.isNotEmpty(interceptors) && interceptorIndex.get() != 0) { // 减少1,因为自增从0开始,否者将导致执行未执行的拦截器 interceptorIndex.decrementAndGet(); final HttpRequest request = context.request(); final HttpResponse response = context.response(); // 逆序执行拦截器 afterCompletion FutureUtils.sequential(interceptors, (interceptor, termination) -> { // 处理拦截器后置方法 return interceptor.afterCompletion(request, response, this.invokeHandler, cause) .recover(error -> { if (log.isWarnEnabled()) { log.warn("执行拦截器 afterCompletion 抛出例外!", error); } return Future.succeededFuture(); }); }, interceptorIndex, false); } } protected RoutingContext createVertXRoutingContext(io.vertx.ext.web.RoutingContext ctx) { return RoutingContextUtils.createVertXRoutingContext(ctx); } }