

- package com.webserver.annotations;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Controller {
- }
- package com.webserver.annotations;
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RequestMapping {
- String value();
- }
- package com.webserver.controller;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.core.ClientHandler;
- import com.webserver.entity.Article;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectOutputStream;
- import java.net.URISyntaxException;
- @Controller
- public class ArticleController {
- private static File rootDir;
- private static File staticDir;
-
- //表示articles目录
- private static File articleDir;
- static{
- try {
- rootDir = new File(
- ClientHandler.class.getClassLoader()
- .getResource(".").toURI()
- );
- staticDir = new File(rootDir,"static");
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
-
- articleDir = new File("articles");
- if(!articleDir.exists()){
- articleDir.mkdirs();
- }
- }
-
- @RequestMapping("/myweb/writeArticle")
- public void writeArticle(HttpServletRequest request, HttpServletResponse response){
- //1获取表单数据
- String title = request.getParameter("title");
- String author = request.getParameter("author");
- String content = request.getParameter("content");
- if(title==null||author==null||content==null){
- File file = new File(staticDir,"/myweb/article_info_error.html");
- response.setContentFile(file);
- return;
- }
-
- //2写入文件
- File articleFile = new File(articleDir,title+".obj");
- if(articleFile.exists()){//文件存在,说明重复的标题,不能发表(需求过于严苛,后期数据库可通过ID避免该问题).
- File file = new File(staticDir,"/myweb/have_article.html");
- response.setContentFile(file);
- return;
- }
-
- Article article = new Article(title,author,content);
- try (
- FileOutputStream fos = new FileOutputStream(articleFile);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- ){
- oos.writeObject(article);
- File file = new File(staticDir,"/myweb/article_success.html");
- response.setContentFile(file);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
-
- //3响应页面
-
-
-
- }
- }
- package com.webserver.controller;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
- import qrcode.QRCodeUtil;
-
- import javax.imageio.ImageIO;
- import java.awt.*;
- import java.awt.image.BufferedImage;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.util.Random;
-
- @Controller
- public class ToolsController {
- @RequestMapping("/myweb/createQR")
- public void createQR(HttpServletRequest request, HttpServletResponse response){
- String line = request.getParameter("content");
- try {
- response.setContentType("image/jpeg");
- QRCodeUtil.encode(
- line, //二维码上显示的文字
- "./logo.jpg", //二维码中间的logo图
- response.getOutputStream(),//生成的二维码数据会通过该流写出
- true);//是否压缩logo图片的尺寸
-
- System.out.println("二维码已生成");
- } catch (Exception e) {
- e.printStackTrace();
- }
-
-
- }
-
- @RequestMapping("/myweb/random.jpg")
- public void createRandomImage(HttpServletRequest request,HttpServletResponse response){
- response.setContentType("image/jpeg");
-
- //1创建一张空白图片,同时指定宽高 理解为:搞一张白纸,准备画画
- BufferedImage image = new BufferedImage(
- 70,30,BufferedImage.TYPE_INT_RGB
- );
-
- //2通过图片获取绘制该图片的画笔,通过这个画笔就可以往该图片上绘制了
- Graphics g = image.getGraphics();
-
- Random random = new Random();
- //3随机生成个颜色涂满整张画布
- Color color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- g.fillRect(0,0,70,30);
-
- //4向画布上绘制文字
- String line = "abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- g.setFont(new Font(null,Font.BOLD,20));
- for(int i=0;i<4;i++) {
- color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- String str = line.charAt(random.nextInt(line.length())) + "";
- g.drawString(str, i*15+5, 18+ random.nextInt(11)-5);
- }
-
- //5随机生成5条干扰线
- for(int i=0;i<5;i++){
- color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- g.drawLine(random.nextInt(71), random.nextInt(31),
- random.nextInt(71), random.nextInt(31));
- }
-
-
- try {
- ImageIO.write(image,"jpg",
- response.getOutputStream());
- System.out.println("图片已生成");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
-
- public static void main(String[] args) {
- //1创建一张空白图片,同时指定宽高 理解为:搞一张白纸,准备画画
- BufferedImage image = new BufferedImage(
- 70,30,BufferedImage.TYPE_INT_RGB
- );
-
- //2通过图片获取绘制该图片的画笔,通过这个画笔就可以往该图片上绘制了
- Graphics g = image.getGraphics();
-
- Random random = new Random();
- //3随机生成个颜色涂满整张画布
- Color color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- g.fillRect(0,0,70,30);
-
- //4向画布上绘制文字
- String line = "abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- g.setFont(new Font(null,Font.BOLD,20));
- for(int i=0;i<4;i++) {
- color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- String str = line.charAt(random.nextInt(line.length())) + "";
- g.drawString(str, i*15+5, 18+ random.nextInt(11)-5);
- }
-
- //5随机生成5条干扰线
- for(int i=0;i<5;i++){
- color = new Color(
- random.nextInt(256),
- random.nextInt(256),
- random.nextInt(256));
- g.setColor(color);//设置画笔颜色
- g.drawLine(random.nextInt(71), random.nextInt(31),
- random.nextInt(71), random.nextInt(31));
- }
-
-
- try {
- ImageIO.write(image,"jpg",
- new FileOutputStream("./random.jpg"));
- System.out.println("图片已生成");
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- // public static void main(String[] args) {
- // try {
- // String line = "扫你妹!";
- // //参数1:二维码上显示的文字,参数2:二维码图片生成的位置
- QRCodeUtil.encode(line,"./qr.jpg");
- //
- // //参数1:二维码上显示的文字,参数2:二维码中间的logo图,
- // //参数3:二维码生成的位置,参数4:是否压缩logo图片的尺寸
- QRCodeUtil.encode(line,"./logo.jpg",
- "./qr.jpg",true);
- //
- // QRCodeUtil.encode(
- // line, //二维码上显示的文字
- // "./logo.jpg", //二维码中间的logo图
- // new FileOutputStream("./qr.jpg"),//生成的二维码数据会通过该流写出
- // true);//是否压缩logo图片的尺寸
- //
- // System.out.println("二维码已生成");
- // } catch (Exception e) {
- // e.printStackTrace();
- // }
- // }
- }
- package com.webserver.controller;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.entity.User;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
- import org.apache.log4j.Logger;
-
- import java.io.*;
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * 处理与用户相关的业务操作
- */
- @Controller
- public class UserController {
- private static Logger logger = Logger.getLogger(UserController.class);
-
- //表示users目录
- private static File userDir;
-
- static {
- userDir = new File("users");
- if (!userDir.exists()) {
- userDir.mkdirs();
- }
- }
-
- /**
- * 生成展示所有用户信息的动态页面
- *
- * @param request
- * @param response
- */
- @RequestMapping("/myweb/showAllUser")
- public void showAllUser(HttpServletRequest request, HttpServletResponse response) {
- /*
- 1:通过读取users目录下所有的obj文件,进行反序列化
- 得到所有的注册用户信息(若干的User对象)将他们存入一个List集合备用
- 2:拼接一个HTML页面,并将所有用户信息拼接到table表格中用于展示给用户
- 3:将拼接好的HTML代码作为正文给浏览器响应回去
- */
- //1
- List<User> userList = new ArrayList<>();
- /*
- 1.1:通过File的listFiles方法,从userDir目录下获取所有的obj文件
- 要使用文件过滤器
- 1.2:遍历每一个obj文件,并利用对象流与文件流的连接,将obj文件中的
- User对象反序列化出来
- 1.3:将反序列化的User对象存入userList集合中
- */
- //1.1
- File[] subs = userDir.listFiles(f -> f.getName().endsWith(".obj"));
- //1.2
- for (File userFile : subs) {
- try (
- FileInputStream fis = new FileInputStream(userFile);
- ObjectInputStream ois = new ObjectInputStream(fis);
- ) {
- User user = (User) ois.readObject();
- //1.3
- userList.add(user);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-
- System.out.println(userList);
-
- //2将数据拼接到html生成页面
-
- response.setContentType("text/html");
-
- PrintWriter pw = response.getWriter();
- pw.println("<!DOCTYPE html>");
- pw.println("<html lang=\"en\">");
- pw.println("<head>");
- pw.println("<meta charset=\"UTF-8\">");
- pw.println("<title>用户列表</title>");
- pw.println("</head>");
- pw.println("<body>");
- pw.println("<center>");
- pw.println("<h1>用户列表</h1>");
- pw.println("<table border=\"1\">");
- pw.println("<tr>");
- pw.println("<td>用户名</td>");
- pw.println("<td>密码</td>");
- pw.println("<td>昵称</td>");
- pw.println("<td>年龄</td>");
- pw.println("</tr>");
- for (User user : userList) {
- pw.println("<tr>");
- pw.println("<td>" + user.getUsername() + "</td>");
- pw.println("<td>" + user.getPassword() + "</td>");
- pw.println("<td>" + user.getNickname() + "</td>");
- pw.println("<td>" + user.getAge() + "</td>");
- pw.println("</tr>");
- }
- pw.println("</table>");
- pw.println("</center>");
- pw.println("</body>");
- pw.println("</html>");
- System.out.println("页面已生成");
-
-
-
-
-
- }
-
- @RequestMapping("/myweb/login")
- public void login(HttpServletRequest request, HttpServletResponse response) {
- //1获取用户输入的登录信息
- String username = request.getParameter("username");
- String password = request.getParameter("password");
- if (username == null || password == null) {
- response.sendRedirect("/myweb/login_info_error.html");
- return;
- }
-
- //2
- File userFile = new File(userDir, username + ".obj");
- if (userFile.exists()) {//用户名输入正确
- try (
- FileInputStream fis = new FileInputStream(userFile);
- ObjectInputStream ois = new ObjectInputStream(fis);
- ) {
- //读取该注册用户信息
- User user = (User) ois.readObject();
- if (user.getPassword().equals(password)) {//密码正确
- //登录成功
- response.sendRedirect("/myweb/login_success.html");
- return;
- }
- } catch (IOException | ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-
- //如果程序走到这里,情况1:用户名没有输入正确,文件不存在
- // 情况2:用户名对了,但是密码不对
- response.sendRedirect("/myweb/login_fail.html");
-
-
- }
-
- @RequestMapping("/myweb/reg")
- public void reg(HttpServletRequest request, HttpServletResponse response) {
- logger.info("开始处理注册");
- /*
- 1:获取用户在注册页面上输入的注册信息
- 2:将注册信息保存起来
- 3:给用户回馈一个注册结果页面
- */
- //1
- //调用getParameter时传入的参数应当与页面上表单中输入框的名字一致(输入框name属性的值)
- String username = request.getParameter("username");
- String password = request.getParameter("password");
- String nickname = request.getParameter("nickname");
- String ageStr = request.getParameter("age");
- logger.info(username + "," + password + "," + nickname + "," + ageStr);
- /*
- 添加一个判断,要求:如果上述四个信息有null值或者年龄不是数字
- 立刻给用户响应一个错误提示页面:reg_info_error.html
- 该页面居中显示一行字:注册信息输入有误,请重新注册。
- 正则表达式:[0-9]+
- */
- if (username == null || password == null || nickname == null || ageStr == null ||
- !ageStr.matches("[0-9]+")) {
- /*
- http://localhost:8088/myweb/reg?username=fancq&password=123456&nickname=fcq&age=22
- 给浏览器发送的响应内容:
- HTTP/1.1 302 Moved Temporarily(CRLF)
- Location: /myweb/reg_info_error.html(CRLF)(CRLF)
- 浏览器接收到该响应后,根据状态代码302得知服务器希望他自动再发起一次请求
- 来请求指定的位置
- 指定的位置是哪里?浏览器根据响应头Location得知位置
- 此时得到的Location位置为:/myweb/reg_info_error.html
- 由于浏览器之前的请求(发起注册):
- http://localhost:8088/myweb/reg?username=fancq&password=123456&nickname=fcq&age=22
- 因此浏览器理解本次重新请求的路径就是:
- http://localhost:8088/myweb/reg_info_error.html
- */
- response.sendRedirect("/myweb/reg_info_error.html");
- return;
- }
-
- int age = Integer.parseInt(ageStr);
- System.out.println(username + "," + password + "," + nickname + "," + age);
-
- //2
- User user = new User(username, password, nickname, age);
- File userFile = new File(userDir, username + ".obj");
- /*
- 判断重名,如果是已注册用户,则响应页面:have_user.html告知
- 该页面居中显示一行字:该用户已存在,请重新注册。
- */
- if (userFile.exists()) {//该文件存在则说明是重复用户
- response.sendRedirect("/myweb/have_user.html");
- return;
- }
-
-
- try (
- FileOutputStream fos = new FileOutputStream(userFile);
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- ) {
- //这里序列化的是注册用户信息,因此是user对象!!!!!!!!!!!!
- oos.writeObject(user);
-
- //3
- response.sendRedirect("/myweb/reg_success.html");
- } catch (IOException e) {
- e.printStackTrace();
- logger.error(e.getMessage(), e);
- }
-
-
- }
-
- }
- package com.webserver.core;
-
- import com.webserver.http.EmptyRequestException;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.*;
- import java.lang.reflect.InvocationTargetException;
- import java.net.Socket;
- import java.net.URISyntaxException;
- import java.nio.charset.StandardCharsets;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
-
- /**
- * 该线程任务是负责与一个客户端进行一次HTTP交互
- * 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
- * 1:解析请求(接受浏览器发送过来的请求内容)
- * 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
- * 3:发送响应(将处理结果发送给浏览器)
- *
- *
- */
- public class ClientHandler implements Runnable{
- private Socket socket;
- public ClientHandler(Socket socket){
- this.socket = socket;
- }
-
- public void run() {
- try {
- //1解析请求
- HttpServletRequest request = new HttpServletRequest(socket);
- HttpServletResponse response = new HttpServletResponse(socket);
-
- //2处理请求
- DispatcherServlet servlet = new DispatcherServlet();
- servlet.service(request,response);
-
- //3发送响应
- response.response();
-
-
-
- } catch (IOException e) {
- e.printStackTrace();
- } catch (EmptyRequestException e) {
-
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } finally {
- try {
- //按照HTTP协议要求,一次交互后要断开连接
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
-
-
-
-
-
- }
- package com.webserver.core;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
- import com.webserver.controller.ArticleController;
- import com.webserver.controller.ToolsController;
- import com.webserver.controller.UserController;
- import com.webserver.http.HttpServletRequest;
- import com.webserver.http.HttpServletResponse;
-
- import java.io.File;
- import java.io.IOException;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.net.URISyntaxException;
- import java.nio.file.Files;
-
- /**
- * 负责处理请求环节
- */
- public class DispatcherServlet {
- private static File rootDir;
- private static File staticDir;
-
- static {
- try {
- rootDir = new File(
- ClientHandler.class.getClassLoader()
- .getResource(".").toURI()
- );
- staticDir = new File(rootDir, "static");
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- }
-
- public void service(HttpServletRequest request, HttpServletResponse response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
- String path = request.getRequestURI();
- //path:/myweb/index.html
- //判断当前请求是否为请求业务
- HandlerMapping.MethodMapping mm = HandlerMapping.getMethod(path);
- if(mm!=null){
- Method method = mm.getMethod();
- Object controller = mm.getController();
- method.invoke(controller,request,response);
- return;
- }
-
- File file = new File(staticDir, path);
- System.out.println("资源是否存在:" + file.exists());
- if (file.isFile()) {
- //将请求的实际文件设置到response的正文上等待响应
- response.setContentFile(file);
- } else {
- file = new File(staticDir, "/root/404.html");
- //将404内容设置到response上等待响应
- response.setStatusCode(404);
- response.setStatusReason("NotFound");
- response.setContentFile(file);
- }
-
- }
- }
- package com.webserver.core;
-
- import com.webserver.annotations.Controller;
- import com.webserver.annotations.RequestMapping;
-
- import java.io.File;
- import java.lang.reflect.Method;
- import java.net.URISyntaxException;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 使用当前类维护请求路径与对应的Controller中处理该请求的方法
- * 使得DispatcherServlet在处理请求时判断是否为业务可以使用当前类完成。
- */
- public class HandlerMapping {
- public static Map<String,MethodMapping> mapping = new HashMap();
- static{
- initMapping();
- }
- private static void initMapping(){
- try {
- File rootDir = new File(HandlerMapping.class.getClassLoader().
- getResource(".").toURI());
- File dir = new File(rootDir,"/com/webserver/controller");
- File[] files = dir.listFiles(f->f.getName().endsWith(".class"));
- for(File file : files){
- String fileName = file.getName();
- String className = fileName.substring(0,fileName.indexOf("."));
- Class cls = Class.forName("com.webserver.controller."+className);
- //判断当前类是否为Controller(是否被@Controller标注)
- if(cls.isAnnotationPresent(Controller.class)){
- //将该Controller实例化
- Object controller = cls.newInstance();
- //获取该类中所有方法
- Method[] methods = cls.getDeclaredMethods();
- for(Method method : methods){
- //判断当前方法是否为处理业务的方法(是否被@RequestMapping标注)
- if(method.isAnnotationPresent(RequestMapping.class)){
- //获取注解@RequestMapping
- RequestMapping rm = method.getAnnotation(RequestMapping.class);
- /*
- 比如:
- @Controller
- public class UserController{
- @RequestMapping("/myweb/reg")
- public void reg(HttpServletRequest request,HttpServletResponse response){...}
- ...
- }
- Object controller : UserController对象
- Method method : reg()方法
- String value : "/myweb/reg"
- */
- String value = rm.value();//该方法处理的请求路径
- MethodMapping mm = new MethodMapping(method,controller);
- mapping.put(value,mm);
- }
- }
- }
- }
- } catch (URISyntaxException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 根据请求路径获取处理该请求的Controller以及对应的方法
- * @param uri
- * @return
- */
- public static MethodMapping getMethod(String uri){
- return mapping.get(uri);
- }
-
- public static void main(String[] args) {
- mapping.forEach(
- (k,v)->{
- System.out.println("请求路径"+k);
- System.out.println("对应的处理方法:"+v.getMethod().getName());
- }
- );
- }
-
-
- public static class MethodMapping{
- //method.invoke(controller,...);
- private Method method; //方法对象
- private Object controller; //方法所属的Controller对象
-
- public MethodMapping(Method method, Object controller) {
- this.method = method;
- this.controller = controller;
- }
-
- public Method getMethod() {
- return method;
- }
-
- public Object getController() {
- return controller;
- }
- }
- }
-
-
-
-
-
-
-
- package com.webserver.core;
-
- import java.io.IOException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.concurrent.Executor;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- /**
- * WebServer主类
- * WebServer是一个Web容器,实现了Tomcat的基础功能。
- * 通过本次项目我们的目标是:
- * 1:理解Tomcat底层工作原理
- * 2:理解HTTP协议的规定
- * 3:理解SpringBoot
- */
- public class WebServerApplication {
- private ServerSocket serverSocket;
- private ExecutorService threadPool;
-
- /**
- * 初始化WebServer
- */
- public WebServerApplication(){
- try {
- System.out.println("正在启动服务端...");
- /*
- 当端口被其他程序占用时,这里会抛出异常:
- java.net.BindException:address already in use:JVM
- 解决:
- 1:杀死该java进程(推荐)
- 2:重启电脑(不推荐)
- 3:更换端口(通常重启后还不行,说明被其他程序占用该端口了)
- */
- serverSocket = new ServerSocket(8088);
- threadPool = Executors.newFixedThreadPool(50);
-
- System.out.println("服务端启动完毕!");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 服务端开始工作的方法
- */
- public void start(){
- try {
- while(true) {
- System.out.println("等待客户端连接...");
- Socket socket = serverSocket.accept();
- System.out.println("一个客户端连接了!");
- //启动一个线程处理该客户端交互
- ClientHandler clientHandler = new ClientHandler(socket);
- threadPool.execute(clientHandler);
- }
-
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public static void main(String[] args) {
- WebServerApplication webServerApplication = new WebServerApplication();
- webServerApplication.start();
- }
- }
- package com.webserver.entity;
-
- import java.io.Serializable;
-
- public class Article implements Serializable {
- public static final long serialVersionUID = 1L;
-
- private String title;
- private String author;
- private String content;
-
- public Article(String title, String author, String content) {
- this.title = title;
- this.author = author;
- this.content = content;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getAuthor() {
- return author;
- }
-
- public void setAuthor(String author) {
- this.author = author;
- }
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
- }
- package com.webserver.entity;//实体
-
- import java.io.Serializable;
-
- /**
- * User的每一个实例用于表示一个注册用户信息
- */
- public class User implements Serializable {
- public static final long serialVersionUID = 1L;
- private String username;
- private String password;
- private String nickname;
- private int age;
-
- public User(){}
-
- public User(String username, String password, String nickname, int age) {
- this.username = username;
- this.password = password;
- this.nickname = nickname;
- this.age = age;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getNickname() {
- return nickname;
- }
-
- public void setNickname(String nickname) {
- this.nickname = nickname;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", nickname='" + nickname + '\'' +
- ", age=" + age +
- '}';
- }
- }
-
-
-
-
-
-
-
- package com.webserver.http;
-
- /**
- * 空请求异常
- * 当HttpServletRequest解析请求过程中发现本次是空请求则会抛出该异常
- */
- public class EmptyRequestException extends Exception{
- public EmptyRequestException() {
- }
-
- public EmptyRequestException(String message) {
- super(message);
- }
-
- public EmptyRequestException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public EmptyRequestException(Throwable cause) {
- super(cause);
- }
-
- public EmptyRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
- }
-
-
-
-
-
-
- package com.webserver.http;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.net.Socket;
- import java.net.URLDecoder;
- import java.nio.charset.StandardCharsets;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 请求对象
- * 该类的每一个实例用于表示客户端发送过来的一个HTTP请求
- * 每个请求由三部分构成:
- * 请求行,消息头,消息正文
- */
- public class HttpServletRequest {
- private Socket socket;
-
- //请求行的相关信息
- private String method;//请求方式
- private String uri;//抽象路径
- private String protocol;//协议版本
-
- private String requestURI;//uri中的请求部分,"?"左侧的内容
- private String queryString;//uri中的参数部分,"?"右侧的内容
- private Map<String,String> parameters = new HashMap<>();//每一组参数 key:参数名 value:参数值
-
-
- //用一个Map保存所有的消息头,其中key:消息头名字,value:消息头的值
- private Map<String,String> headers = new HashMap<>();
-
- public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
- this.socket = socket;
- //1.1解析请求行
- parseRequestLine();
- //1.2解析消息头
- parseHeaders();
- //1.3解析消息正文
- parseContent();
- }
-
- //解析请求行
- private void parseRequestLine() throws IOException, EmptyRequestException {
- String line = readLine();
- if(line.isEmpty()){//读取请求行是空串,则说明本次为空请求
- throw new EmptyRequestException();
- }
-
- System.out.println(line);
- //将请求行按照空格拆分为三部分,并赋值给三个变量
- String[] data = line.split("\\s");
- method = data[0];
- uri = data[1];
- protocol = data[2];
- parseURI();//进一步解析uri
- //测试路径:http://localhost:8088/myweb/index.html
- System.out.println("method:"+method);//method:GET
- System.out.println("uri:"+uri);//uri:/myweb/index.html
- System.out.println("protocol:"+protocol);//protocol:HTTP/1.1
- }
- //进一步解析URI
- private void parseURI(){
- /*
- uri有两种情况:
- 1:不含有参数的
- 例如: /myweb/index.html
- 直接将uri的值赋值给requestURI即可.
- 2:含有参数的
- 例如:/myweb/reg?username=fancq&password=123456&nickname=chuanqi&age=22
- 将uri中"?"左侧的请求部分赋值给requestURI
- 将uri中"?"右侧的参数部分赋值给queryString
- 将参数部分首先按照"&"拆分出每一组参数,再将每一组参数按照"="拆分为参数名与参数值
- 并将参数名作为key,参数值作为value存入到parameters中。
- */
- String[] data = uri.split("\\?");
- requestURI = data[0];
- if(data.length>1){
- queryString = data[1];
- parseParameters(queryString);
- }
- System.out.println("requestURI:"+requestURI);
- System.out.println("queryString:"+queryString);
- System.out.println("parameters:"+parameters);
- }
- //解析参数
- private void parseParameters(String line){
- //将参数部分中的%XX内容转换为对应字符
- try {
- line = URLDecoder.decode(line,"UTF-8");
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
-
- //首先按照"&"拆分出每一组参数
- String[] data = line.split("&");
- //遍历拆分后的数组取出每一组参数
- for(String para : data){//username=fancq
- //将每一组参数按照"="拆分为参数名和参数值
- String[] paraArray = para.split("=");
- //将参数名和参数值分别以key,value形式存入parameters(Map中)
- parameters.put(paraArray[0], paraArray.length>1?paraArray[1]:null);
- }
- }
-
- //解析消息头
- private void parseHeaders() throws IOException {
- while(true) {
- String line = readLine();
- if(line.isEmpty()){//读取单独读取了CRLF则停止循环
- break;
- }
- System.out.println(line);
- String[] data = line.split(":\\s");
- headers.put(data[0].toLowerCase(),data[1]);
-
- }
- System.out.println(headers);
- }
- //解析消息正文
- private void parseContent() throws IOException {
- /*
- 1:如何确定含有正文?
- post请求才含有正文
- 判断请求方式是否为post请求
- 2:如果存在正文,则要先将正文读取回来。读取多少个字节呢?
- 通过消息头:Content-Length得知正文长度以便读取
- 3:如何判定该正文的所有字节表示什么内容呢?
- 通过消息头:Content-Type得知
- 这里我们仅处理一种:form表单提交的用户输入信息
- Content-Type: application/x-www-form-urlencoded
- 如果是上面的类型,则正文是一个字符串,内容就是原GET请求提交时
- 在抽象路径中"?"右侧的内容。
- 格式如:
- username=xxxxx&password=xxxx&....
- 因此当我们将正文转换为字符串后,要拆分参数来初始化:parameters
- 实际这里将来要写分支判断不同的类型,来进行不同的处理。
- */
- if("post".equalsIgnoreCase(method)){
- //获取Content-Length
- String contentLength = getHeader("content-length");
- if(contentLength!=null){//确保存在正文长度
- //将长度转换为int值
- int length = Integer.parseInt(contentLength);
- System.out.println("正文长度:"+length);
-
- //根据正文长度将正文内容读取回来
- InputStream in = socket.getInputStream();
- byte[] data = new byte[length];
- in.read(data);
-
- //根据Content-Type的类型来对正文数据进行解析
- String contentType = getHeader("content-type");
- //将来根据不同的值对正文进行不同的解析操作,这里仅处理form提交的数据
- if("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)){
- String line = new String(data, StandardCharsets.ISO_8859_1);
- System.out.println("正文内容:"+line);
- parseParameters(line);
- }
- //这里后期继续else if分支判断其他类型...
-
- }
- }
-
- }
-
- /**
- * 将读取一行字符串的操作定义成方法进行重用
- * 通常复用代码的方法中如果需要处理异常时,通常都是直接抛出给调用者处理
- * @return
- */
- private String readLine() throws IOException {
- /*
- socket对象无论调用多少次getInputStream和getOutputStream
- 获取回来的始终是同一条输入或输出流
- */
- InputStream in = socket.getInputStream();
- int d ;
- char cur='a',pre='a';//cur表示本次读取的字符,pre表示上次读取的字符
- StringBuilder builder = new StringBuilder();
- while((d = in.read()) != -1){
- cur = (char)d;//本次读取到的字符
- if(pre==13&&cur==10){//是否连续读取了回车+换行
- break;
- }
- builder.append(cur);
- pre = cur;//在进行下一轮读取前,要将本次读取的字符记作"上次读取的字符"
- }
- return builder.toString().trim();//获取拼接好的一行字符串内容
- }
-
- public String getMethod() {
- return method;
- }
-
- public String getUri() {
- return uri;
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- public String getHeader(String name) {
- return headers.get(name);
- }
-
- public String getRequestURI() {
- return requestURI;
- }
-
- public String getQueryString() {
- return queryString;
- }
-
- /**
- * 根据参数名获取对应的参数值
- * @param name
- * @return
- */
- public String getParameter(String name) {
- //paratemers key:参数名(页面表单中输入框的名字) value:参数值(输入框上用户输入的信息)
- return parameters.get(name);
- }
- }
- package com.webserver.http;
-
- import java.io.*;
- import java.net.Socket;
- import java.nio.charset.StandardCharsets;
- import java.nio.file.Files;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
-
- /**
- * 响应对象
- * 该类的每一个实例用于表示发送给客户端(浏览器)的一个HTTP响应内容
- * 每个响应由三部分构成:
- * 状态行,响应头,响应正文
- */
- public class HttpServletResponse {
- private Socket socket;
-
- //状态行相关信息
- private int statusCode = 200;//状态代码,默认为200
- private String statusReason = "OK";//状态描述,默认为OK
-
- //响应头相关信息
- //key:响应头名字 value:响应头的值
- private Map<String,String> headers = new HashMap<>();
-
-
- //响应正文相关信息
- private File contentFile;//响应正文对应的实体文件
- private byte[] contentData;//以一组字节作为正文内容(通常动态数据使用)
-
- private ByteArrayOutputStream baos;
-
-
-
-
- public HttpServletResponse(Socket socket){
- this.socket = socket;
- }
-
- /**
- * 将当前响应对象内容按照标准的HTTP响应格式发送给浏览器
- */
- public void response() throws IOException {
- //发送前的准备工作
- sendBefore();
- //3.1发送状态行
- sendStatusLine();
- //3.2发送响应头
- sendHeaders();
- //3.3将文件的所有字节作为正文内容发送给浏览器
- sendContent();
- }
- //响应发送前的准备工作
- private void sendBefore(){
- if(baos!=null){//如果baos不为null,则说明用过这个流写出过动态数据
- contentData = baos.toByteArray();//将内部的字节数组获取(动态数据)
- addHeader("Content-Length",contentData.length+"");
- }
- }
-
-
- //发送状态行
- private void sendStatusLine() throws IOException {
- println("HTTP/1.1" + " " + statusCode + " " + statusReason);
- }
- //发送响应头
- private void sendHeaders() throws IOException {
- /*
- headers
- KEY VALUE
- Content-Type text/html
- Content-Length 2323
- ... ...
- */
- //遍历headers将所有的响应头发送给浏览器
- Set<Entry<String,String>> entrySet = headers.entrySet();
- for(Entry<String,String> e : entrySet){
- String key = e.getKey();
- String value = e.getValue();
- //println("Content-Type: text/html");
- println(key + ": " + value);
- }
-
- //单独发送回车+换行表示响应头部分发送完毕了
- println("");
- }
- //发送响应正文
- private void sendContent() throws IOException {
- if(contentData!=null){//是否有动态数据作为正文
- OutputStream out = socket.getOutputStream();
- out.write(contentData);
-
- }else if(contentFile!=null) {
- //try()意味着编译后有finally用于遍历流。所以这不违背try不能单独定义的原则
- try (
- FileInputStream fis = new FileInputStream(contentFile);
- ) {
- OutputStream out = socket.getOutputStream();
- byte[] buf = new byte[1024 * 10];//10kb
- int len;//记录每次实际读取到的字节数
- while ((len = fis.read(buf)) != -1) {
- out.write(buf, 0, len);
- }
- }
- }
- }
-
-
-
-
- private void println(String line) throws IOException {
- OutputStream out = socket.getOutputStream();
- byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
- out.write(data);//发送状态行内容
- out.write(13);//发送回车符
- out.write(10);//发送换行符
- }
-
- /**
- * 重定向到uri指定的路径
- * @param uri
- */
- public void sendRedirect(String uri){
- //1设置状态代码位302
- statusCode = 302;
- statusReason = "Moved Temporarily";
-
- //2添加必要的响应头Location指定浏览器再次请求的位置
- addHeader("Location",uri);
- }
-
-
- public int getStatusCode() {
- return statusCode;
- }
-
- public void setStatusCode(int statusCode) {
- this.statusCode = statusCode;
- }
-
- public String getStatusReason() {
- return statusReason;
- }
-
- public void setStatusReason(String statusReason) {
- this.statusReason = statusReason;
- }
-
- public File getContentFile() {
- return contentFile;
- }
-
- public void setContentFile(File contentFile) {
- this.contentFile = contentFile;
- /*
- 根据正文文件类型来设置Content-Type用于告知浏览器该正文类型以便其理解.
- 但是在HTTP协议中规定:如果发送响应时,不包含Content-Type响应头时,则是
- 让浏览器自行理解正文类型.
- */
- try {
- //该方法会自动分析文件对应的Content-Type值,若无法识别会返回null
- //http://localhost:8088/TeduStore/index.html
- /*
- 比如:file表示的是 index.html 文件
- 该方法返回值为:"text/html"
- file表示的是 jquery.js 文件
- 该方法那会的值为:"application/javascript"
- */
- String type = Files.probeContentType(contentFile.toPath());
- if(type!=null) {
- addHeader("Content-Type", type);
- }
- } catch (IOException e) {
- }
- addHeader("Content-Length",contentFile.length()+"");
-
-
- }
-
- /**
- * 添加一个响应头
- * @param name
- * @param value
- */
- public void addHeader(String name,String value){
- headers.put(name,value);
- }
-
- public OutputStream getOutputStream(){
- if(baos==null){
- baos = new ByteArrayOutputStream();
- }
- return baos;
- }
-
- public PrintWriter getWriter(){
- /*
- 进行流连接,创建一个PrintWrtier并最终连接到属性baos上
- 将该PrintWriter返回给外界使用。
- */
- OutputStream out = getOutputStream();//baos
- OutputStreamWriter osw = new OutputStreamWriter(out,StandardCharsets.UTF_8);
- BufferedWriter bw = new BufferedWriter(osw);
- return new PrintWriter(bw,true);
-
- // return new PrintWriter(
- // new BufferedWriter(
- // new OutputStreamWriter(
- // getOutputStream(),StandardCharsets.UTF_8
- // )
- // ),true
- // );
- }
-
- /**
- * 添加响应头Content-Type以及对应的值
- * @param mime
- */
- public void setContentType(String mime){
- addHeader("Content-Type",mime);
- }
-
- }
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>失败</title>
- </head>
- <body>
- <center>
- <h1>文章输入信息有误,请<a href="/myweb/writeArticle.html">重新发表</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>成功</title>
- </head>
- <body>
- <center>
- <h1>发表成功!</h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>课程表</title>
- </head>
- <body>
- <center>
- <h1>课程表</h1>
- <table border="1">
- <tr>
- <td> </td>
- <td>星期一</td>
- <td>星期二</td>
- <td>星期三</td>
- <td>星期四</td>
- <td>星期五</td>
- </tr>
- <tr>
- <td rowspan="4">上午</td>
- <td>语文</td>
- <td>数学</td>
- <td>英语</td>
- <td>体育</td>
- <td>生物</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- <tr>
- <td colspan="6" align="center"><a href="http://www.bilibili.com">午休,bilibili一下</a></td>
- </tr>
- <tr>
- <td rowspan="4">下午</td>
- <td>语文</td>
- <td>数学</td>
- <td>英语</td>
- <td>体育</td>
- <td>生物</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- <tr>
- <td>生物</td>
- <td>数学</td>
- <td>英语</td>
- <td>语文</td>
- <td>体育</td>
- </tr>
- </table>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>二维码生成器</title>
- </head>
- <body>
- <center>
- <h1>二维码生成器</h1>
- <form action="/myweb/createQR" method="post">
- <table border="1">
- <tr>
- <td>内容</td>
- <td><input type="text" name="content"></td>
- </tr>
- <tr>
- <td align="center" colspan="2"><input type="submit" value="生成"></td>
- </tr>
- </table>
- </form>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>失败</title>
- </head>
- <body>
- <center>
- <h1>该文章已经存在了,请<a href="/myweb/writeArticle.html">重新发表</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>失败</title>
- </head>
- <body>
- <center>
- <h1>该用户已存在,请<a href="/myweb/reg.html">重新注册</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>我的首页</title>
- </head>
- <body>
- <!--
- <h1>-<h6>:标题,分为1-6级标题。标题独占一行
- <center>:居中,该标签在HTML5中已经不再建议使用了。没学习样式前临时使用
- <input>:输入组件。它是一套组件,用来在页面上获取用户输入的。
- 属性type决定着该组件的样式。
- 常见的值有:
- text:文本框
- button:按钮
- checkbox:多选框
- radio:单选框
- submit:提交按钮
- password:密码框
- <a>:超链接,标签中间的文本是超链接对应的文字,属性href用于指定跳转的路径
- <br>:换行
- <table>标签:表格。属性border用于指定边框。
- <table>标签中包含<tr>标签用于表示行
- <tr>标签中包含<td>标签用于表示列
- <td>标签中常见属性:
- align:对其方式。left左对齐,right右对齐,center剧中对其
- colspan:跨列合并列,合并是从左向右合并列
- rowspan:跨行合并列,合并是从上向下合并列
- <img>:图片标签,src属性用于指定图片的路径
- 在页面上我们经常会使用路径去定位页面上要使用的额外资源,比如CSS文件,JS文件
- 图片文件等。
- 例如图片:
- <img src=""> 这里的src就是用于指定图片的路径的
- 路径分两种:相对路径和绝对路径
- 相对路径常用的:"./",即:当前目录
- 在浏览器中和我们在java源代码中"./"位置不是相同的!!!!!!
- 原因是页面是被浏览器理解的。
- 在页面上"./"的位置浏览器理解的是:URL地址当中抽象路径里的最后一个"/"的位置
- 例如:
- 请求inde.html时,我们在地址栏上输入的路径为:
- http://localhost:8088/myweb/index.html
- 在这个页面上我们使用了<img src="logo.png"> 注:不写"./"默认就是从"./"开始
- 此时浏览器判定该图片的实际位置是哪里,就是根据请求当前页面的URL地址决定:
- 当前URL地址中抽象路径部分为:/myweb/index.html
- 因此"./"理解的就是这里最后一个"/"的位置为:/myweb/
- ^
- ./的位置
- 所以确定了图片的抽象路径部分为:/myweb/logo.png
- 因此浏览器实际请求图片的路径为:
- http://localhost:8088/myweb/logo.png
- 绝对路径:"/",即:根
- 在页面上"/"的位置浏览器理解的是:URL地址当中抽象路径里的第一个"/"的位置
- 同样,如果在当前页面上<img src="/myweb/logo.png">
- 此时浏览器理解的"/myweb/logo.png"发先路径从"/"开始,即:从根开始。
- 请求当前页面路径:http://localhost:8088/myweb/index.html
- ^
- 就是抽象路径的根
- 因此该图片实际请求位置:
- http://localhost:8088/myweb/logo.png
- 相对路径存在定位不准确情况,常见于服务端转发404时
- 例如:请求一个不存在的页面
- http://localhost:8088/myweb/123.html
- 服务端发现该页面不存在后,就响应了root目录下的404.html
- 而404页面上我们指定图片如果为:<img src="404.png">
- 那么浏览器在得到404页面时,理解的404图片实际位置是哪里?
- 由于浏览器是请求 http://localhost:8088/myweb/123.html看到的404页面
- 因此浏览器会参考该地址中抽象路径部分:/myweb/123.html来分析404图片位置
- 由于"./404.png"中的"./"是当前目录,因此浏览器认为该图片的抽象路径应当为:
- "/myweb/404.png"于是请求了:http://localhost:8088/myweb/404.png
- 为了定位准确,我们选取绝对路径中的根"/"
- <img src="/root/404.png">
- -->
- <center>
- <!--<h1>百度</h1>-->
- <img src="/myweb/logo.png"><br>
- <input type="text" size="32">
- <input type="button" value="百度一下" onclick="alert('点你妹啊!')">
- <br>
- <a href="/myweb/reg.html">注册</a>
- <a href="/myweb/login.html">登录</a>
- <a href="/myweb/showAllUser">用户列表</a>
- <br>
- <a href="/myweb/writeArticle.html">发表文章</a>
- <br>
- <a href="/myweb/createQR.html">二维码生成器</a>
- <br>
- <a href="http://www.taobao.com">淘宝</a>
- <br>
- <table border="1">
- <tr>
- <td>第一列</td>
- <td>第二列</td>
- <td colspan="2" align="center">第三列</td>
- </tr>
- <tr>
- <td rowspan="2">第一列</td>
- <td>第二列</td>
- <td>第三列</td>
- <td>第四列</td>
- </tr>
- <tr>
- <td>第二列</td>
- <td>第三列</td>
- <td>第四列</td>
- </tr>
- </table>
- </center>
-
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>用户登录</title>
- </head>
- <body>
- <center>
- <h1>用户登录</h1>
- <form action="/myweb/login" method="post">
- <table border="1">
- <tr>
- <td>用户名</td>
- <td><input name="username" type="text"></td>
- </tr>
- <tr>
- <td>密码</td>
- <td><input name="password" type="password"></td>
- </tr>
- <tr>
- <td colspan="2" align="center"><input type="submit" value="登录"></td>
- </tr>
- </table>
- </form>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>失败</title>
- </head>
- <body>
- <center>
- <h1>登录失败,用户名或密码不正确!请<a href="/myweb/login.html">重新登录</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>错误</title>
- </head>
- <body>
- <center>
- <h1>登录信息输入有误,请<a href="/myweb/login.html">重新登录</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>成功</title>
- </head>
- <body>
- <center>
- <h1>登录成功,欢迎回来!</h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>用户注册</title>
- </head>
- <body>
- <center>
- <h1>用户注册</h1>
- <!--
- 表单组件<form>
- 表单用于将用于在页面上输入的信息提交给服务端使用的组件。
- 表单上有两个重要的属性:
- action:用于指定表单提交的路径
- method:用于指定表单提交的方式,该方式有两个可选值
- GET:地址栏形式提交,表单数据会被拼接到URL的抽象路径中
- POST:打包提交,表单数据会被包含在请求的消息正文中。
- 当表单数据含有用户隐私信息或附件时则需要使用POST形式提交表单。
- method属性不指定时,默认为GET形式提交表单。
- 注意:所有输入组件只有被包含在form表单中并且必须使用name属性为该组件取名字才会被提交!!
- 输入组件取的名字可以任意定义,但是不能有中文(英文数字)。
- http://localhost:8088/myweb/reg?username=fancq&password=123456&nickname=chuanqi&age=22
- 表单提交后的URL中抽象路径:
- /myweb/reg?username=fancq&password=123456&nickname=chuanqi&age=22
- URL地址中抽象路径部分可以使用"?"分隔为两块,分别是请求部分和参数部分
- 请求部分由表单action决定.参数部分由表单中输入框决定
- 格式:
- 请求部分?输入框1名字=输入框1信息&输入框2名字=输入框2信息&.....
- -->
- <form action="/myweb/reg" method="get">
- <table border="1">
- <tr>
- <td>用户名</td>
- <td><input name="username" type="text"></td>
- </tr>
- <tr>
- <td>密码</td>
- <td><input name="password" type="password"></td>
- </tr>
- <tr>
- <td>昵称</td>
- <td><input name="nickname" type="text"></td>
- </tr>
- <tr>
- <td>年龄</td>
- <td><input name="age" type="text"></td>
- </tr>
- <tr>
- <td><img src="/myweb/random.jpg"></td>
- <td><input name="code" type="text"></td>
- </tr>
- <tr>
- <td colspan="2" align="center">
- <input type="submit" value="注册">
- </td>
- </tr>
- </table>
- </form>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>失败</title>
- </head>
- <body>
- <center>
- <h1>注册信息输入有误,请<a href="/myweb/reg.html">重新注册</a></h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>成功</title>
- </head>
- <body>
- <center>
- <h1>恭喜您,注册成功了!</h1>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>发表文章</title>
- </head>
- <body>
- <center>
- <h1>发表文章</h1>
- <form action="/myweb/writeArticle" method="get">
- <table border="1">
- <tr>
- <td>标题</td>
- <td><input name="title" type="text"></td>
- </tr>
- <tr>
- <td>作者</td>
- <td><input name="author" type="text"></td>
- </tr>
- <tr>
- <td>内容</td>
- <td><textarea cols="50" rows="10" name="content"></textarea></td>
- </tr>
- <tr>
- <td colspan="2" align="center"><input type="submit" value="发表"></td>
- </tr>
- </table>
- </form>
- </center>
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>404</title>
- </head>
- <body>
- <center>
- <img src="/root/404.png">
- <h1>404,资源不存在!</h1>
- </center>
- </body>
- </html>
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.example</groupId>
- <artifactId>WebServer</artifactId>
- <packaging>pom</packaging>
- <version>1.0-SNAPSHOT</version>
- <modules>
- <module>V1</module>
- <module>V2</module>
- <module>V3</module>
- <module>V4</module>
- <module>V5</module>
- <module>V6</module>
- <module>V7</module>
- <module>V8</module>
- <module>V9</module>
- <module>V10</module>
- <module>V11</module>
- <module>V12</module>
- <module>V13</module>
- <module>V14</module>
- <module>V15</module>
- <module>V16</module>
- <module>V17</module>
- <module>V18</module>
- <module>V19</module>
- <module>V20</module>
- <module>V21</module>
- <module>V22</module>
- <module>V23</module>
- <module>V24</module>
- <module>V25</module>
- <module>V26</module>
- <module>V27</module>
- </modules>
-
- <properties>
- <!-- 设置 JDK 版本为 1.8 -->
- <maven.compiler.target>1.8</maven.compiler.target>
- <maven.compiler.source>1.8</maven.compiler.source>
- <!-- 设置编码为 UTF-8 -->
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.25</version>
- </dependency>
-
- </dependencies>
-
- </project>
- log4j.rootLogger=INFO, console, file
- log4j.appender.console=org.apache.log4j.ConsoleAppender
- log4j.appender.console.layout=org.apache.log4j.PatternLayout
- log4j.appender.console.layout.ConversionPattern=%d %p [%c] - %m%n
- log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
- log4j.appender.file.File=logs/log.log
- log4j.appender.file.layout=org.apache.log4j.PatternLayout
- log4j.appender.A3.MaxFileSize=1024KB
- log4j.appender.A3.MaxBackupIndex=10
- log4j.appender.file.layout.ConversionPattern=%d %p [%c] - %m%n
- <project ><!-- 声明规范:pom版本标签(必须要的),表示是一个固定的版本,指定了当前pom的版本 -->
-
- <modelVersion><!--声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,
- 这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。 -->
-
- <groupId><!--项目的组织标识符,对应src/main目录下的Java的目录结构。
- 例如,本项目中的com.manage.maven既是项目组织标识符,也是src/main目录下的Java代码包。-->
-
- <artifactId><!--项目的标识符,可用来区分不同的项目。例如,本项目中是hello-maven也可以是test-maven。-->
-
- <packaging><!--项目产生的构件类型,例如jar、war、ear、pom。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型 -->
-
- <version><!--项目的版本号,项目打包后的JAR文件的版本号跟这里对应。-->
-
- <modules>模块根目录
- <module>V1</module>模块
-
-
- <properties><!--有时候对于同一个技术需要编写很多个依赖引入相关的jar包,每个依赖中都要填写版本号,倘若更换版本号的话,修改的地方比较多,
- 所以可以使用properties统一管理版本号,logback-version可以自由命名,通过${}方式进行引用:-->
- <!-- 设置 JDK 版本为 1.8 -->
- <maven.compiler.target>1.8</maven.compiler.target>
- <maven.compiler.source>1.8</maven.compiler.source>
- <!-- 设置编码为 UTF-8 -->
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
- </properties>
-
- <dependencies><!--一个完整的JAR依赖配置。-->
- <dependency><!--我们要使用的jar包的gav信息放到这里,这样maven就可以自动下载相关jar包了。-->
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.25</version>
- </dependency>
- </dependencies>
-
-
- </project>

上诉项目一共写了27个版本想看那个版本做了什么的去看JSD第二阶段