web支持全局异常处理、全局返回处理.

This commit is contained in:
aoshiguchen
2022-07-29 07:27:12 +08:00
parent 7c6dd2e974
commit fab19e90c9
11 changed files with 347 additions and 11 deletions
@@ -13,6 +13,7 @@
package fun.asgc.neutrino.core.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import fun.asgc.neutrino.core.web.HttpResponseEntry;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
@@ -28,19 +29,27 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
public class HttpServerUtil {
public static void send404Response(ChannelHandlerContext context, String url) {
HttpResponseEntry responseEntry = new HttpResponseEntry(HttpResponseStatus.NOT_FOUND.code(), HttpResponseStatus.NOT_FOUND.reasonPhrase(), String.format("未找到指定资源: %s", url));
String res = JSON.toJSONString(responseEntry);
String res = String.format("404 未找到指定资源: %s", url);
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND, Unpooled.wrappedBuffer(res.getBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
}
public static void send500Response(ChannelHandlerContext context, Throwable throwable) {
HttpResponseEntry responseEntry = new HttpResponseEntry(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), HttpResponseStatus.INTERNAL_SERVER_ERROR.reasonPhrase(), String.format("服务异常: %s", ExceptionUtils.getStackTrace(throwable)));
String res = JSON.toJSONString(responseEntry);
String res = String.format("500 服务异常: %s", ExceptionUtils.getStackTrace(throwable));
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer(res.getBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
}
public static void send200Response(ChannelHandlerContext context, Object o) {
String res = String.valueOf(o);
if (null != o && !TypeUtil.isNormalBasicType(o.getClass())) {
res = JSONObject.toJSONString(o);
}
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(res.getBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
}
}
@@ -33,10 +33,7 @@ import fun.asgc.neutrino.core.web.annotation.RequestParam;
import fun.asgc.neutrino.core.web.context.HttpContextHolder;
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
import fun.asgc.neutrino.core.web.context.WebContextHolder;
import fun.asgc.neutrino.core.web.interceptor.HandlerInterceptor;
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistration;
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
import fun.asgc.neutrino.core.web.interceptor.MappedInterceptor;
import fun.asgc.neutrino.core.web.interceptor.*;
import fun.asgc.neutrino.core.web.router.DefaultHttpRouter;
import fun.asgc.neutrino.core.web.router.HttpRouteParam;
import fun.asgc.neutrino.core.web.router.HttpRouteResult;
@@ -92,6 +89,11 @@ public class HttpRequestHandler {
}
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
if (null != WebContextHolder.getAdviceHandler()) {
invokeResult = WebContextHolder.getAdviceHandler().advice(context, requestParser, routePath, httpRouteResult.getMethod(), invokeResult);
}
String res = String.valueOf(invokeResult);
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
res = JSONObject.toJSONString(invokeResult);
@@ -126,7 +128,10 @@ public class HttpRequestHandler {
return;
}
} catch (Throwable e) {
exceptionHandler(e);
Object res = exceptionHandler(e);
if (null != res) {
HttpServerUtil.send200Response(context, res);
}
} finally {
release();
}
@@ -170,9 +175,21 @@ public class HttpRequestHandler {
return result;
}
private void exceptionHandler(Throwable e) {
private Object exceptionHandler(Throwable e) {
ExceptionHandlerRegistry exceptionHandlerRegistry = WebContextHolder.getExceptionHandlerRegistry();
if (CollectionUtil.isEmpty(exceptionHandlerRegistry.getExceptionHandlerList())) {
log.error("Http处理异常", e);
HttpServerUtil.send500Response(HttpContextHolder.getChannelHandlerContext(), e);
return null;
}
for (RestControllerExceptionHandler exceptionHandler : exceptionHandlerRegistry.getExceptionHandlerList()) {
if (exceptionHandler.support(e)) {
return exceptionHandler.handle(HttpContextHolder.getChannelHandlerContext(), HttpContextHolder.getHttpRequestParser(), e);
}
}
log.error("Http处理异常", e);
HttpServerUtil.send500Response(HttpContextHolder.getChannelHandlerContext(), e);
return null;
}
private Object invoke(Object instance, Method method) throws InvocationTargetException, IllegalAccessException {
@@ -21,7 +21,9 @@
*/
package fun.asgc.neutrino.core.web.config;
import fun.asgc.neutrino.core.web.interceptor.ExceptionHandlerRegistry;
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler;
/**
*
@@ -33,4 +35,12 @@ public interface WebMvcConfigurer {
default void addInterceptors(InterceptorRegistry registry) {
}
default void addExceptionHandler(ExceptionHandlerRegistry registry) {
}
default RestControllerAdviceHandler adviceHandler() {
return null;
}
}
@@ -31,7 +31,9 @@ import fun.asgc.neutrino.core.context.ApplicationConfig;
import fun.asgc.neutrino.core.util.*;
import fun.asgc.neutrino.core.web.annotation.RestController;
import fun.asgc.neutrino.core.web.config.WebMvcConfigurer;
import fun.asgc.neutrino.core.web.interceptor.ExceptionHandlerRegistry;
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler;
import java.util.List;
import java.util.stream.Collectors;
@@ -51,6 +53,8 @@ public class WebContextHolder {
private static volatile Integer port;
private static List<BeanWrapper> controllerBeanWrapperList;
private static volatile InterceptorRegistry interceptorRegistry;
private static volatile ExceptionHandlerRegistry exceptionHandlerRegistry;
private static volatile RestControllerAdviceHandler adviceHandler;
@Autowired
private ApplicationConfig applicationConfig;
@Autowired
@@ -126,6 +130,7 @@ public class WebContextHolder {
private void initInterceptorRegistry() {
interceptorRegistry = new InterceptorRegistry();
exceptionHandlerRegistry = new ExceptionHandlerRegistry();
List<BeanWrapper> beanWrapperList = applicationBeanFactory.beanWrapperList();
if (CollectionUtil.isEmpty(beanWrapperList)) {
return;
@@ -134,7 +139,11 @@ public class WebContextHolder {
if (CollectionUtil.isEmpty(webMvcConfigurerList)) {
return;
}
webMvcConfigurerList.forEach(webMvcConfigurer -> webMvcConfigurer.addInterceptors(interceptorRegistry));
webMvcConfigurerList.forEach(webMvcConfigurer -> {
webMvcConfigurer.addInterceptors(interceptorRegistry);
webMvcConfigurer.addExceptionHandler(exceptionHandlerRegistry);
adviceHandler = webMvcConfigurer.adviceHandler();
});
}
public static List<BeanWrapper> getControllerBeanWrapperList() {
@@ -144,4 +153,12 @@ public class WebContextHolder {
public static InterceptorRegistry getInterceptorRegistry() {
return interceptorRegistry;
}
public static ExceptionHandlerRegistry getExceptionHandlerRegistry() {
return exceptionHandlerRegistry;
}
public static RestControllerAdviceHandler getAdviceHandler() {
return adviceHandler;
}
}
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.interceptor;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author: aoshiguchen
* @date: 2022/7/29
*/
public class ExceptionHandlerRegistry {
private final List<RestControllerExceptionHandler> exceptionHandlerList = new ArrayList<>();
public void addExceptionHandler(RestControllerExceptionHandler exceptionHandler) {
exceptionHandlerList.add(exceptionHandler);
}
public List<RestControllerExceptionHandler> getExceptionHandlerList() {
return exceptionHandlerList;
}
}
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.interceptor;
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Method;
/**
*
* @author: aoshiguchen
* @date: 2022/7/29
*/
public interface RestControllerAdviceHandler {
/**
* 返回结果处理
* @param context
* @param requestParser
* @param route
* @param targetMethod
* @param res
* @return
*/
Object advice(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod, Object res);
}
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.interceptor;
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
import io.netty.channel.ChannelHandlerContext;
/**
* 异常处理
* @author: aoshiguchen
* @date: 2022/7/29
*/
public interface RestControllerExceptionHandler {
/**
* 是否支持处理该异常
* @param e
* @return
*/
default boolean support(Throwable e) {
return true;
}
/**
* 异常处理
* @param e
* @return
*/
Object handle(ChannelHandlerContext context, HttpRequestParser requestParser, Throwable e);
}
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.test1;
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler;
import io.netty.channel.ChannelHandlerContext;
import java.lang.reflect.Method;
/**
*
* @author: aoshiguchen
* @date: 2022/7/29
*/
public class GlobalAdviceHandler implements RestControllerAdviceHandler {
@Override
public Object advice(ChannelHandlerContext context, HttpRequestParser requestParser, String route, Method targetMethod, Object res) {
if (res instanceof JsonResult) {
return res;
}
return new JsonResult<>()
.setCode(0)
.setData(res);
}
}
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.test1;
import fun.asgc.neutrino.core.web.context.HttpRequestParser;
import fun.asgc.neutrino.core.web.interceptor.RestControllerExceptionHandler;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.lang3.exception.ExceptionUtils;
/**
*
* @author: aoshiguchen
* @date: 2022/7/29
*/
public class GlobalExceptionHandler implements RestControllerExceptionHandler {
@Override
public Object handle(ChannelHandlerContext context, HttpRequestParser requestParser, Throwable e) {
if (e instanceof UserNotLoginException) {
return new JsonResult<>()
.setCode(101)
.setMsg("账号未登录")
.setStack(ExceptionUtils.getStackTrace(e));
}
return new JsonResult<>()
.setCode(1)
.setMsg("系统异常")
.setStack(ExceptionUtils.getStackTrace(e));
}
}
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.test1;
import lombok.Data;
import lombok.experimental.Accessors;
/**
*
* @author: aoshiguchen
* @date: 2022/7/29
*/
@Accessors(chain = true)
@Data
public class JsonResult<T> {
private Integer code;
private String msg;
private T data;
private String stack;
}
@@ -23,7 +23,9 @@ package fun.asgc.neutrino.core.web.test1;
import fun.asgc.neutrino.core.annotation.Configuration;
import fun.asgc.neutrino.core.web.config.WebMvcConfigurer;
import fun.asgc.neutrino.core.web.interceptor.ExceptionHandlerRegistry;
import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry;
import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler;
/**
*
@@ -38,4 +40,14 @@ public class WebConfigurerAdapter implements WebMvcConfigurer {
registry.addInterceptor(new BaseAuthInterceptor())
.addPathPatterns("/**").excludePathPatterns("/**/*.html", "/**/*.js", "/**/*.ico");
}
@Override
public void addExceptionHandler(ExceptionHandlerRegistry registry) {
registry.addExceptionHandler(new GlobalExceptionHandler());
}
@Override
public RestControllerAdviceHandler adviceHandler() {
return new GlobalAdviceHandler();
}
}