http请求处理流程优化.
This commit is contained in:
@@ -32,6 +32,7 @@ import fun.asgc.neutrino.core.web.annotation.RequestBody;
|
||||
import fun.asgc.neutrino.core.web.annotation.RequestParam;
|
||||
import fun.asgc.neutrino.core.web.param.HttpContextHolder;
|
||||
import fun.asgc.neutrino.core.web.param.HttpRequestParser;
|
||||
import fun.asgc.neutrino.core.web.param.WebContextHolder;
|
||||
import fun.asgc.neutrino.core.web.router.DefaultHttpRouter;
|
||||
import fun.asgc.neutrino.core.web.router.HttpRouteParam;
|
||||
import fun.asgc.neutrino.core.web.router.HttpRouteResult;
|
||||
@@ -61,59 +62,60 @@ public class HttpRequestHandler {
|
||||
private ApplicationConfig applicationConfig;
|
||||
@Autowired
|
||||
private DefaultHttpRouter defaultHttpRouter;
|
||||
private static volatile String httpContextPath;
|
||||
|
||||
public void handle() {
|
||||
ChannelHandlerContext context = HttpContextHolder.getChannelHandlerContext();
|
||||
HttpRequestParser requestParser = HttpContextHolder.getHttpRequestParser();
|
||||
log.info("HttpRequest method:{} url:{} query:{}", requestParser.getMethod().name(), requestParser.getUrl(), requestParser.getQueryParamMap());
|
||||
|
||||
String routePath = getRoutePath(requestParser.getUrl());
|
||||
HttpMethod httpMethod = HttpMethod.of(requestParser.getMethod().name());
|
||||
HttpRouteResult httpRouteResult = defaultHttpRouter.route(new HttpRouteParam().setMethod(httpMethod).setUrl(routePath));
|
||||
if (null == httpRouteResult) {
|
||||
HttpServerUtil.send404Response(context, requestParser.getUrl());
|
||||
release();
|
||||
return;
|
||||
}
|
||||
if (HttpRouterType.METHOD == httpRouteResult.getType()) {
|
||||
try {
|
||||
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
|
||||
String res = String.valueOf(invokeResult);
|
||||
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
|
||||
res = JSONObject.toJSONString(invokeResult);
|
||||
try {
|
||||
String routePath = requestParser.getRoutePath();
|
||||
HttpMethod httpMethod = HttpMethod.of(requestParser.getMethod().name());
|
||||
HttpRouteResult httpRouteResult = defaultHttpRouter.route(new HttpRouteParam().setMethod(httpMethod).setUrl(routePath));
|
||||
if (null == httpRouteResult) {
|
||||
HttpServerUtil.send404Response(context, requestParser.getUrl());
|
||||
return;
|
||||
}
|
||||
if (HttpRouterType.METHOD == httpRouteResult.getType()) {
|
||||
|
||||
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
|
||||
String res = String.valueOf(invokeResult);
|
||||
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
|
||||
res = JSONObject.toJSONString(invokeResult);
|
||||
}
|
||||
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);
|
||||
return;
|
||||
} else if(HttpRouterType.PAGE == httpRouteResult.getType()) {
|
||||
// 前端页面
|
||||
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(httpRouteResult.getPageLocation()));
|
||||
if (mimeType.startsWith("text/")) {
|
||||
mimeType += ";charset=utf-8";
|
||||
}
|
||||
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(res.getBytes()));
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
|
||||
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(FileUtil.readBytes(httpRouteResult.getPageLocation())));
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, mimeType);
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_LANGUAGE, "zh-CN");
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.SERVER, MetaDataConstant.SERVER_VS);
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.DATE, new Date());
|
||||
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
log.error("Http处理异常", e);
|
||||
} finally {
|
||||
release();
|
||||
} else {
|
||||
HttpServerUtil.send404Response(context, requestParser.getUrl());
|
||||
return;
|
||||
}
|
||||
} else if(HttpRouterType.PAGE == httpRouteResult.getType()) {
|
||||
// 前端页面
|
||||
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(httpRouteResult.getPageLocation()));
|
||||
if (mimeType.startsWith("text/")) {
|
||||
mimeType += ";charset=utf-8";
|
||||
}
|
||||
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(FileUtil.readBytes(httpRouteResult.getPageLocation())));
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, mimeType);
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_LANGUAGE, "zh-CN");
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.SERVER, MetaDataConstant.SERVER_VS);
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.DATE, new Date());
|
||||
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
|
||||
} catch (Throwable e) {
|
||||
exceptionHandler(e);
|
||||
} finally {
|
||||
release();
|
||||
return;
|
||||
} else {
|
||||
// TODO
|
||||
HttpServerUtil.send404Response(context, requestParser.getUrl());
|
||||
release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void exceptionHandler(Throwable e) {
|
||||
log.error("Http处理异常", e);
|
||||
HttpServerUtil.send500Response(HttpContextHolder.getChannelHandlerContext(), e);
|
||||
}
|
||||
|
||||
private Object invoke(Object instance, Method method) throws InvocationTargetException, IllegalAccessException {
|
||||
Object[] params = new Object[method.getParameterCount()];
|
||||
if (method.getParameterCount() > 0) {
|
||||
@@ -155,36 +157,4 @@ public class HttpRequestHandler {
|
||||
private void release() {
|
||||
HttpContextHolder.remove();
|
||||
}
|
||||
|
||||
private String getRoutePath(String url) {
|
||||
String httpContextPath = getHttpContextPath();
|
||||
if (StringUtil.isEmpty(httpContextPath)) {
|
||||
return url;
|
||||
}
|
||||
String res = url.substring(httpContextPath.length());
|
||||
if (StringUtil.isEmpty(res)) {
|
||||
return "/";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private String getHttpContextPath() {
|
||||
return LockUtil.doubleCheckProcessForNoException(
|
||||
() -> null == httpContextPath,
|
||||
this,
|
||||
() -> {
|
||||
httpContextPath = applicationConfig.getHttp().getContextPath();
|
||||
if (StringUtil.isEmpty(httpContextPath)) {
|
||||
httpContextPath = "/";
|
||||
}
|
||||
if (!httpContextPath.startsWith("/")) {
|
||||
httpContextPath = "/" + httpContextPath;
|
||||
}
|
||||
if (httpContextPath.endsWith("/")) {
|
||||
httpContextPath = httpContextPath.substring(0, httpContextPath.length() - 1);
|
||||
}
|
||||
},
|
||||
() -> httpContextPath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import fun.asgc.neutrino.core.context.ApplicationConfig;
|
||||
import fun.asgc.neutrino.core.context.ApplicationRunner;
|
||||
import fun.asgc.neutrino.core.util.*;
|
||||
import fun.asgc.neutrino.core.web.param.HttpContextHolder;
|
||||
import fun.asgc.neutrino.core.web.param.WebContextHolder;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.*;
|
||||
@@ -55,25 +56,17 @@ public class WebApplicationServer implements ApplicationRunner {
|
||||
private EventLoopGroup workerGroup;
|
||||
private ServerBootstrap serverBootstrap;
|
||||
private ChannelFuture channelFuture;
|
||||
private byte[] faviconBytes;
|
||||
|
||||
@Init
|
||||
public void init() {
|
||||
ApplicationConfig.Http http = applicationConfig.getHttp();
|
||||
if (StringUtil.notEmpty(http.getMaxContentLengthDesc()) && null == http.getMaxContentLength()) {
|
||||
http.setMaxContentLength(NumberUtil.descriptionToSize(http.getMaxContentLengthDesc(), 64 * 1024));
|
||||
}
|
||||
|
||||
WebContextHolder.init(applicationConfig.getHttp());
|
||||
this.bossGroup = new NioEventLoopGroup();
|
||||
this.workerGroup = new NioEventLoopGroup();
|
||||
this.serverBootstrap = new ServerBootstrap();
|
||||
this.initFavicon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String[] args) throws Exception {
|
||||
ApplicationConfig.Http http = applicationConfig.getHttp();
|
||||
|
||||
this.serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 1024)
|
||||
@@ -86,7 +79,7 @@ public class WebApplicationServer implements ApplicationRunner {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
pipeline.addLast(new HttpObjectAggregator(http.getMaxContentLength().intValue()));
|
||||
pipeline.addLast(new HttpObjectAggregator(WebContextHolder.getMaxContentLength().intValue()));
|
||||
pipeline.addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext context, FullHttpRequest request) throws Exception {
|
||||
@@ -94,10 +87,10 @@ public class WebApplicationServer implements ApplicationRunner {
|
||||
|
||||
String uri = request.uri();
|
||||
log.debug("http request: {}", uri);
|
||||
if (http.getContextPath().equals("/") || uri.startsWith(http.getContextPath() + "/") || uri.equals(http.getContextPath())) {
|
||||
if (WebContextHolder.getHttpContextPath().equals("/") || uri.startsWith(WebContextHolder.getHttpContextPath() + "/") || uri.equals(WebContextHolder.getHttpContextPath())) {
|
||||
httpRequestHandler.handle();
|
||||
} else if (uri.equals("/favicon.ico") && null != faviconBytes) {
|
||||
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(faviconBytes));
|
||||
} else if (uri.equals("/favicon.ico") && null != WebContextHolder.getFaviconBytes()) {
|
||||
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(WebContextHolder.getFaviconBytes()));
|
||||
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, "image/x-icon");
|
||||
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
|
||||
} else {
|
||||
@@ -107,20 +100,8 @@ public class WebApplicationServer implements ApplicationRunner {
|
||||
});
|
||||
}
|
||||
});
|
||||
channelFuture = this.serverBootstrap.bind(http.getPort()).sync();
|
||||
log.info("HTTP服务启动,端口:{} context-path:{}", http.getPort(), http.getContextPath());
|
||||
}
|
||||
|
||||
private void initFavicon() {
|
||||
if (null == applicationConfig.getHttp().getStaticResource() || CollectionUtil.isEmpty(applicationConfig.getHttp().getStaticResource().getLocations())) {
|
||||
return;
|
||||
}
|
||||
for (String location : applicationConfig.getHttp().getStaticResource().getLocations()) {
|
||||
this.faviconBytes = FileUtil.readBytes(location.concat("favicon.ico"));
|
||||
if (null != faviconBytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
channelFuture = this.serverBootstrap.bind(WebContextHolder.getPort()).sync();
|
||||
log.info("HTTP服务启动,端口:{} context-path:{}", WebContextHolder.getPort(), WebContextHolder.getHttpContextPath());
|
||||
}
|
||||
|
||||
@Destroy
|
||||
|
||||
@@ -43,6 +43,7 @@ public class HttpRequestParser {
|
||||
private String url;
|
||||
private String queryString;
|
||||
private Map<String, String> queryParamMap = null;
|
||||
private String routePath;
|
||||
|
||||
private HttpRequestParser(FullHttpRequest request) {
|
||||
this.request = request;
|
||||
@@ -56,6 +57,7 @@ public class HttpRequestParser {
|
||||
}
|
||||
url = getUrl().substring(0, index);
|
||||
}
|
||||
this.routePath = getRoutePath(url);
|
||||
}
|
||||
|
||||
public static HttpRequestParser create(FullHttpRequest request) {
|
||||
@@ -66,6 +68,10 @@ public class HttpRequestParser {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getRoutePath() {
|
||||
return routePath;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return queryString;
|
||||
}
|
||||
@@ -170,4 +176,16 @@ public class HttpRequestParser {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getRoutePath(String url) {
|
||||
String httpContextPath = WebContextHolder.getHttpContextPath();
|
||||
if (StringUtil.isEmpty(httpContextPath)) {
|
||||
return url;
|
||||
}
|
||||
String res = url.substring(httpContextPath.length());
|
||||
if (StringUtil.isEmpty(res)) {
|
||||
return "/";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.param;
|
||||
|
||||
import fun.asgc.neutrino.core.context.ApplicationConfig;
|
||||
import fun.asgc.neutrino.core.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/7/26
|
||||
*/
|
||||
public abstract class WebContextHolder {
|
||||
private static ApplicationConfig.Http http;
|
||||
private static volatile String httpContextPath;
|
||||
private static volatile byte[] faviconBytes;
|
||||
private static volatile Long maxContentLength;
|
||||
private static volatile Integer port;
|
||||
|
||||
public static void init(ApplicationConfig.Http http) {
|
||||
Assert.notNull(http, "http配置不能为空!");
|
||||
WebContextHolder.http = http;
|
||||
initHttpContextPath();
|
||||
initFavicon();
|
||||
initMaxContentLength();
|
||||
port = http.getPort();
|
||||
}
|
||||
|
||||
public static String getHttpContextPath() {
|
||||
return httpContextPath;
|
||||
}
|
||||
|
||||
public static byte[] getFaviconBytes() {
|
||||
return faviconBytes;
|
||||
}
|
||||
|
||||
public static Long getMaxContentLength() {
|
||||
return maxContentLength;
|
||||
}
|
||||
|
||||
public static Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
private static void initHttpContextPath() {
|
||||
httpContextPath = http.getContextPath();
|
||||
if (StringUtil.isEmpty(httpContextPath)) {
|
||||
httpContextPath = "/";
|
||||
}
|
||||
if (!httpContextPath.startsWith("/")) {
|
||||
httpContextPath = "/" + httpContextPath;
|
||||
}
|
||||
if (httpContextPath.endsWith("/")) {
|
||||
httpContextPath = httpContextPath.substring(0, httpContextPath.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initFavicon() {
|
||||
if (null == http.getStaticResource() || CollectionUtil.isEmpty(http.getStaticResource().getLocations())) {
|
||||
return;
|
||||
}
|
||||
for (String location : http.getStaticResource().getLocations()) {
|
||||
faviconBytes = FileUtil.readBytes(location.concat("favicon.ico"));
|
||||
if (null != faviconBytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void initMaxContentLength() {
|
||||
maxContentLength = http.getMaxContentLength();
|
||||
if (StringUtil.notEmpty(http.getMaxContentLengthDesc()) && null == http.getMaxContentLength()) {
|
||||
maxContentLength = NumberUtil.descriptionToSize(http.getMaxContentLengthDesc(), 64 * 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -41,7 +41,10 @@ public class Test1Controller {
|
||||
|
||||
@GetMapping("hello")
|
||||
public String hello() {
|
||||
System.out.println("拿到参数 a = " + HttpContextHolder.getHttpRequestParser().getParameterForInteger("a"));
|
||||
System.out.println("拿到参数 a = " + HttpContextHolder.getHttpRequestParser().getParameter("a"));
|
||||
if ("aaa".equals(HttpContextHolder.getHttpRequestParser().getParameter("a"))) {
|
||||
throw new RuntimeException("hhhh 异常了!");
|
||||
}
|
||||
return test1Service.hello();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user