参考jsoncat框架,集成netty web服务器至CommandFast中,可以处理http的POST类型报文请求。
目录
程序效果
截图1.响应json报文请求
响应报文内容没有进行调整,将原有查询磁盘空间功能的部分结果填充到了响应报文中,重点测试请求、应答是否正常。
实现过程
1.使用框架视角
截图2.业务代码开发流程图
步骤一、添加@RestController注解的类。其注解内容为第一级URI标签,如服务器端口使用8080,则第一级标签之前的内容为“http://127.0.0.1:8080”,这里注解中的值如果填“/CurrentDiskInfo”,则使用“http://127.0.0.1:8080/CurrentDiskInfo”能访问到该类。
步骤二、添加@PostMapping注解的方法。其注解内容为第二级URI标签,如果填“/work”,则使用“http://127.0.0.1:8080/CurrentDiskInfo/work”能访问到该方法。
步骤三、实现相关的业务功能。
2.框架视角
截图3.框架功能生效流程图
步骤一、在原有加载类的位置,添加@RestController标签类的加载。
步骤二、在生成类实例之前,添加加载路由步骤,用于将@RestController、@PostMapping注解的类与方法,与URI路径的对应关系存放起来,便于netty映射调用。
步骤三、在原有生成类实例的位置,添加生成@RestController类的实例。
步骤四、启动应用的位置,将原有控制台相关的提示与响应部分,替换为netty启动代码。
样例代码
1.使用框架视角
@RestController("/CurrentDiskInfo")
public class CurrentDiskInfo implements CommandFastPro {
@Autowired
MemDisk memDisk;
public void work() {
memDisk.getDiskInfo();
memDisk.getMemInfo();
}
@PostMapping
public String work1(@RequestBody TestDao testDao) {
memDisk.getDiskInfo();
memDisk.getMemInfo();
String resp;
resp = memDisk.getDiskInfo()+memDisk.getMemInfo();
return resp;
}
}
利用@RestController、@PostMapping注解即可,放在入口的类及方法上。
2.框架视角
截图4.netty相关文件清单
主要麻烦在报文转换与映射部分,http请求进来后,先转换成json格式,传递给根据URI匹配的对应方法进行处理。返回响应前,将String格式的报文转换成json格式。
(1)pom.xml新增依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
(2)netty自定义处理的入口类
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private static final AsciiString CONNECTION = AsciiString.cached("Connection");
private static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive");
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) {
//获取http类型(GET/POST)
RequestHandler requestHandler = RequestHandlerFactory.get(fullHttpRequest.method());
FullHttpResponse fullHttpResponse;
try {
//处理请求
fullHttpResponse = requestHandler.handle(fullHttpRequest);
} catch (Throwable e) {
String requestPath = UrlUtil.getRequestPath(fullHttpRequest.uri());
fullHttpResponse = FullHttpResponseFactory.getErrorResponse(requestPath, e.toString(), HttpResponseStatus.INTERNAL_SERVER_ERROR);
}
//根据连接拼接不同头部
boolean keepAlive = HttpUtil.isKeepAlive(fullHttpRequest);
if (!keepAlive) {
//返回应答
ctx.write(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
} else {
fullHttpResponse.headers().set(CONNECTION, KEEP_ALIVE);
ctx.write(fullHttpResponse);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
}
(3)处理大致流程
public class PostRequestHandler implements RequestHandler {
@Override
public FullHttpResponse handle(FullHttpRequest fullHttpRequest) {
String requestUri = fullHttpRequest.uri();
String requestPath = UrlUtil.getRequestPath(requestUri);
//根据URI路径匹配方法
MethodDetail methodDetail = RouteMethodMapper.getMethodDetail(requestPath, HttpMethod.POST);
Method targetMethod = methodDetail.getMethod();
if (targetMethod == null) {
return null;
}
String contentType = this.getContentType(fullHttpRequest.headers());
//解析http请求POST的json入参
List<Object> targetMethodParams = new ArrayList<>();
if (contentType.equals("application/json")) {
String json = fullHttpRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
methodDetail.setJson(json);
Parameter[] targetMethodParameters = targetMethod.getParameters();
for (Parameter parameter : targetMethodParameters) {
ParameterResolver parameterResolver = ParameterResolverFactory.get(parameter);
if (parameterResolver != null) {
Object param = parameterResolver.resolve(methodDetail, parameter);
targetMethodParams.add(param);
}
}
} else {
throw new IllegalArgumentException("only receive application/json type data");
}
//根据方法名在BeanFactory工厂中查找类实例
String beanName = BeanHelper.getBeanName(methodDetail.getMethod().getDeclaringClass());
Object targetObject = BeanFactory.BEANS.get(beanName);
//传递参数至方法中,处理请求
return FullHttpResponseFactory.getSuccessResponse(targetMethod, targetMethodParams, targetObject);
}
private String getContentType(HttpHeaders headers) {
String typeStr = headers.get("Content-Type");
String[] list = typeStr.split(";");
return list[0];
}
}
(4)json报文解析
public class RequestBodyParameterResolver implements ParameterResolver {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object resolve(MethodDetail methodDetail, Parameter parameter) {
Object param = null;
RequestBody requestBody = parameter.getDeclaredAnnotation(RequestBody.class);
if (requestBody != null) {
try {
param = OBJECT_MAPPER.readValue(methodDetail.getJson(), parameter.getType());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return param;
}
}
工程文件
1.链接:https://pan.baidu.com/s/16eyPnxxNg7fQvMbvY-ObFw
2.提取码:ocyb
参考资料
1.jsoncat源码及相关教程 (微信公众号“JavaGuide”有系列教程)
源代码:https://github.com/Snailclimb/jsoncat