博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一个简单的netty springMVC http服务器
阅读量:5260 次
发布时间:2019-06-14

本文共 13881 字,大约阅读时间需要 46 分钟。

说一下最基本的的需求,用netty做网络处理,写一个最基本的http server,它含有以下功能

  1.  能快速处理各种请求GET/POST,并返回对应的内容,此时,它就是一个api服务器
  2.  能处理模板文件,根据模板内容,生成对应的html页面内容
  3.  能处理静态资源文件,例如下载文件,图片,js之类
  4.  必须足够简单

明确了以上目标后,我们可以开始动手设计了,我们需要以下内容

  • MAVEN 实际上其他类似工具gradle等也是可以的(以下所需的java库就无需手动下载了),但这里我们只使用maven
  • freemarker 这其实是一个java库,用于模板生成,当然,用其他例如velocity也是可以的,为了做到足够简单,我们用freemarker
  • netty 也是一个java库
  • spring mvc 同理,一个java库

现在可以开始动手写我们的HTTP服务器了,先说一下基本目录

首先是代码目录

                                                                       

  • 主要是一个controller包,写控制逻辑,
  • global包,关于模板配置的在这里
  • AppConfig.java 配置声明一下这个是spring mvc
  • MyHttp*,MyNetty*这些是关于netty处理http请求的
  • Web*Test.java,里面就一个main方法,用于启动测试

 

 

 

 

 

 

下面是资源目录

  • 一个template目录,存在html模板
  • 一个template/static, 放静态资源
  • 一个spring.xml配置spring一些内容

 

 

 

 

 

 

 

 现在放代码部分

首先是网络处理部分

package com._22evil.web_container;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com._22evil.web_container.global.Global;
/**
* Created by Administrator on 2016/12/28.
*/
public class MyHttpServer {
private final static Logger logger = LoggerFactory.getLogger(MyHttpServer.class);
private final int port;
public MyHttpServer(int port) {
this.port = port;
}
public void launch() throws Exception {
initFreeMarker();
ServerBootstrap server = new ServerBootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
try {
server.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.localAddress(port)
.childHandler(new MyNettyInitalizer());
logger.info("Netty server has started on port : " + port);
server.bind().sync().channel().closeFuture().sync();
}
finally {
group.shutdownGracefully();
}
}
/**
* 初始化模板解析
*/
private void initFreeMarker() throws Exception{
Global.getFreeMarkerConfig();
}
 
}

 

package com._22evil.web_container;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.stream.ChunkedStream;
import io.netty.util.CharsetUtil;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/**
* Created by Administrator on 2016/12/29.
*/
public class MyHttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private final Servlet servlet;
private final ServletContext servletContext;
public MyHttpHandler(Servlet servlet) {
this.servlet = servlet;
this.servletContext = servlet.getServletConfig().getServletContext();
}
private MockHttpServletRequest createServletRequest(FullHttpRequest fullHttpRequest) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(fullHttpRequest.getUri()).build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest(this.servletContext);
servletRequest.setRequestURI(uriComponents.getPath());
servletRequest.setPathInfo(uriComponents.getPath());
servletRequest.setMethod(fullHttpRequest.getMethod().name());
if (uriComponents.getScheme() != null) {
servletRequest.setScheme(uriComponents.getScheme());
}
if (uriComponents.getHost() != null) {
servletRequest.setServerName(uriComponents.getHost());
}
if (uriComponents.getPort() != -1) {
servletRequest.setServerPort(uriComponents.getPort());
}
for (String name : fullHttpRequest.headers().names()) {
servletRequest.addHeader(name, fullHttpRequest.headers().get(name));
}
// 将post请求的参数,添加到HttpServletRrequest的parameter
try {
ByteBuf buf=fullHttpRequest.content();
int readable=buf.readableBytes();
byte[] bytes=new byte[readable];
buf.readBytes(bytes);
String contentStr = UriUtils.decode(new String(bytes,"UTF-8"), "UTF-8");
for(String params : contentStr.split("&")){
String[] para = params.split("=");
if(para.length > 1){
servletRequest.addParameter(para[0],para[1]);
} else {
servletRequest.addParameter(para[0],"");
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (uriComponents.getQuery() != null) {
String query = UriUtils.decode(uriComponents.getQuery(), "UTF-8");
servletRequest.setQueryString(query);
}
for (Map.Entry<String, List<String>> entry : uriComponents.getQueryParams().entrySet()) {
for (String value : entry.getValue()) {
servletRequest.addParameter(
UriUtils.decode(entry.getKey(), "UTF-8"),
UriUtils.decode(value, "UTF-8"));
}
}
} catch (UnsupportedEncodingException ex) {
// shouldn't happen
}
return servletRequest;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
handleHttp(ctx, req);
}
private void handleHttp(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception{
if (!req.getDecoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST);
return;
}
MockHttpServletRequest servletRequest = createServletRequest(req);
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
this.servlet.service(servletRequest, servletResponse);
HttpResponseStatus status = HttpResponseStatus.valueOf(servletResponse.getStatus());
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
for (String name : servletResponse.getHeaderNames()) {
for (Object value : servletResponse.getHeaderValues(name)) {
response.headers().add(name, value);
}
}
// Write the initial line and the header.
ctx.write(response);
InputStream contentStream = new ByteArrayInputStream(servletResponse.getContentAsByteArray());
// Write the content and flush it.
ChannelFuture writeFuture = ctx.writeAndFlush(new ChunkedStream(contentStream));
writeFuture.addListener(ChannelFutureListener.CLOSE);
}
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
ByteBuf content = Unpooled.copiedBuffer(
"Failure: " + status.toString() + "\r\n",
CharsetUtil.UTF_8);
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(
HTTP_1_1,
status,
content
);
fullHttpResponse.headers().add(CONTENT_TYPE, "text/plain; charset=UTF-8");
// Close the connection as soon as the error message is sent.
ctx.write(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
}
}

 

package com._22evil.web_container;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Created by Administrator on 2016/12/29.
*/
public class MyNettyInitalizer extends ChannelInitializer<SocketChannel> {
private final DispatcherServlet dispatcherServlet;
public MyNettyInitalizer() throws Exception{
MockServletContext servletContext = new MockServletContext();
MockServletConfig servletConfig = new MockServletConfig(servletContext);
AnnotationConfigWebApplicationContext wac = new AnnotationConfigWebApplicationContext();
wac.setServletContext(servletContext);
wac.setServletConfig(servletConfig);
wac.register(AppConfig.class);
wac.refresh();
this.dispatcherServlet = new DispatcherServlet(wac);
this.dispatcherServlet.init(servletConfig);
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// if (sslCtx != null) {
// pipeline.addLast(sslCtx.newHandler(ch.alloc()));
// }
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
pipeline.addLast("handler", new MyHttpHandler(this.dispatcherServlet));
}
}

 

package com._22evil.web_container;
/**
* Created by shangguyi on 11/3/16.
* config
*/
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
@ImportResource({"classpath*:/spring.xml"})
public class AppConfig {
}

 

global配置,这里由于环境测试的问题增加了很多无用代码

package com._22evil.web_container.global;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import java.io.File;
public class Global {
private static volatile Configuration free_marker_cfg;
public static Configuration getFreeMarkerConfig() {
if (null == free_marker_cfg ) {
synchronized(Configuration.class) {
if (null == free_marker_cfg) {
try {
free_marker_cfg = new Configuration(Configuration.VERSION_2_3_28);
File file = new File("template");
if (file.exists()) {
 
} else {
file = new File("");
String path = file.getCanonicalPath();
file = new File(path + "\\src\\main\\resources\\template");
}
free_marker_cfg.setDirectoryForTemplateLoading(file);
free_marker_cfg.setDefaultEncoding("UTF-8");
free_marker_cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return free_marker_cfg;
}
}

 

普通url控制,随便写了几个测试

package com._22evil.web_container.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import freemarker.template.Template;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import org.springframework.web.bind.annotation.PathVariable;
import com._22evil.web_container.global.Global;
@Controller("/a")
public class HelloController {
@RequestMapping(value = "abcd", produces = "text/html;charset=UTF-8") //方法1 强制设置为UTF-8,(spring 默认编码不是这个)
public @ResponseBody String abc() {
return "abc你好啊";
}
@RequestMapping("123")
public @ResponseBody String abc1() {
return "a123你好";
}
@RequestMapping("/hello1")
public @ResponseBody String hello() {
return "hello";
}
@RequestMapping("/abc1/{name}")
public @ResponseBody
String abc(@PathVariable String name) {
return name + new Date().toString();
}
@RequestMapping("/a1")
public @ResponseBody
String a(HttpServletRequest request) {
String name = request.getParameter("name");
System.out.println(name);
String str = "hhh heloo ww" + name;
return str;
}
@RequestMapping("/b1")
public @ResponseBody
String b() {
return "this is b";
}
@RequestMapping("/c1")
public @ResponseBody
String c() {
return "this is c";
}
@RequestMapping(value = "/testabc", produces = "text/html;charset=UTF-8")
public void testabc(HttpServletRequest request, HttpServletResponse response) {
try {
System.err.println("-----testabc----");
Map<String, Object> root = new HashMap<>();
root.put("username", "zhujisna");
root.put("age", 25);
// Stu stu = new Stu();
// stu.setAddr("GZ");
// stu.setTel("13467952"); // 方法不可行
Map<String, Object> stu = new HashMap<>();
stu.put("addr", "GZ");
stu.put("tel", "1186459513");
root.put("stu", stu);
response.setCharacterEncoding("UTF-8"); //不加入这句,中文就是?显示
Template temp = Global.getFreeMarkerConfig().getTemplate("testabc.html");
temp.process(root, response.getWriter()); // 此时得到的便是testabc.html内容
 
response.setContentType("text/html; charset=" + temp.getEncoding()); //中文乱码问题
// 测试下sesstion问题, 每次session都会不同,建议用spring-session上传到redis
System.out.println(request.getSession());
Object id = request.getSession().getAttribute("id");
if (id == null) {
request.getSession().setAttribute("id", "1234");
System.out.println("=== session 没有id");
} else {
System.out.println("====session id = " + id);
}
} catch (Exception e) {
e.printStackTrace();
}
}
static class Stu {
String tel;
String addr;
public void setTel(String val) {
this.tel = val;
}
public void setAddr(String val) {
this.addr = val;
}
public String getTel() {
return tel;
}
public String getAddr() {
return addr;
}
}
}

 

静态文件控制

package com._22evil.web_container.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.File;
import java.io.FileInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 处理静态资源
*/
@Controller
public class StaticFileController {
@RequestMapping(value = {"/static/*/*", "/static/*"})
public void solveStatic(HttpServletRequest request, HttpServletResponse response) throws Exception{
// 备注 如果文件是文本类型而且有中文,依旧是乱码
response.setCharacterEncoding("UTF-8");
String url = request.getRequestURI();
 
File file = new File("template/static");
if (file.exists()) {
} else {
file = new File("");
String filePath = file.getCanonicalPath();
file = new File (filePath + "\\src\\main\\resources\\template\\" + url.toString());
if (file.exists()) {
FileInputStream in = new FileInputStream(file);
byte[] data = new byte[4096];
int c;
while ( (c = in.read(data)) > 0) {
response.getOutputStream().write(data, 0, c);
}
in.close();
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}
}

 

至此,已经完成了一个基本的架子。

下面是一些跑的结果

访问api例子

      

 

 

访问模板例子 

 

代码地址: 

转载于:https://www.cnblogs.com/guiyetingfeng/p/9155738.html

你可能感兴趣的文章
【Flink】数据流编程模型
查看>>
应用Python来计算排列中的逆序数个数
查看>>
2017前端面试题总结
查看>>
Http GetPost网络请求
查看>>
SWIFT国际资金清算系统
查看>>
关于拷贝构造函数与赋值构造函数的深刻解析
查看>>
Sping注解:注解和含义
查看>>
站立会议第四天
查看>>
用原生JS获取非行间样式
查看>>
toolbox类
查看>>
如何快速掌握一门技术
查看>>
利用AMPScript获取Uber用户数据的访问权限
查看>>
vagrant 同时设置多个同步目录
查看>>
python接口自动化28-requests-html爬虫框架
查看>>
爬虫学习笔记(一)初识爬虫
查看>>
生成随机数的模板
查看>>
SpringMVC文件上传
查看>>
hdu 2093
查看>>
纸上谈兵: 树, 二叉树, 二叉搜索树[转]
查看>>
Mysql 数据库操作
查看>>