servlet 深入学习

1、请求对象与响应对象介绍

  • 用户在客户端输入网址(虚拟路径)时,开始发送一个HTTP请求(请求行、请求头、请求体)至服务器。服务器内的Tomcat引擎会解析请求的地址,去找XML文件,然后根据虚拟路径找Servlet的真实路径,真实的Servlet会将请求的信息封装成request(请求)对象,然后再创建一个response(响应)对象,(此时的response内是空的)同时创建servlet对象,并调用service方法(或doGet和doPost方法)。这样就是把两个对象传给了服务器内的某个servlet的service方法,通过这个方法,我们可以获得request的所有的信息,并且向response内设置信息。response.getwriter().write()将内容写到response的缓冲区,这样service方法结束了,方法返回后,tomcat引擎会将从该response缓冲区中获取的设置信息封装成一个HTTP响应(响应行、响应头、响应体),发送给客户端,客户端解析响应回来的东西继而进行显示。
  • 我们可以通过设置修改响应的信息进行相应的重定向(用户访问的网页不存在并跳转到其他网页上)、修改响应文本(需要修改浏览器和服务器两边的编码,并且还得处理兼容问题)。
  • 需要强调的是:请求对象和响应对象都是由服务器创建,我们只是拿来用;

在这里插入图片描述

2、响应对象详解

  • HttpServletResponse接口继承自ServletResponse接口,由于HTTP响应消息分为状态行、响应消息头、消息体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应消息头、响应消息体的方法。虽然HttpServletResponse接口中的方法较多,但是我们常用的也就是那么几个,如果用到其他的方法了可以阅读响应的源码或者相关资料就行了;

发送状态码相关函数

方法说明
public void setStatus(int sc)设置响应消息状态码,Web服务器默认产生一个状态码为200的状态行
public void sendError(int sc)发送表示错误信息的状态码,第二个方法还增加了一个用于提示说明的文本信息
public void sendError(int sc, String msg)发送响应消息头相关函数

发送响应消息头相关函数

方法说明
public void addHeader(String name, String value)
public void setHeader(String name, String value)
设置HTTP响应头字段,name指定字段名称,value指定字段值。addHeader可以增加同名的响应头字段,setHeader则会覆盖同名的头字段
public void setContentLength(int len)设置响应消息的实体内容的大小,单位为字节,即设置Content-Length字段的值
public void setContentType(String type)设置Servlet输出内容的MIME类型,即设置Content-Type字段的值
public void setCharacterEncoding(String charset)设置输出内容字符编码,即设置Content-Type字段的值,注意,该方法优先级比setContentType的高
public void sendRedirect(String location)Servlet请求重定向

发送响应消息体相关函数

方法说明
public ServletOutputStream getOutputStream()获取HttpServletResponse的字节输出流ServletOutputStram类型
public PrintWriter getWriter()获取HttpServletResponse的字符输出流ServletWriter类型
  • 首先明确,后台给前台响应的数据分为:普通文本、HTML、JSON;

给前台相应普通文本:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(value = "/demo1")
public class MyServlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取打印输出流
        PrintWriter writer = response.getWriter();
        //响应一段文本数据
        writer.write("Hello Browser!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

在这里插入图片描述

服务器开启之后,通过你在浏览器键入的URL,服务器默认创建HTTP GET请求,并通过配置文件中的类的全限定名称找到这个后台的Servlet,带回了后台相应给前台的一段文本数据,将他展示在了浏览器的页面上;

本质上,一个继承了HttpServlet类的servlet中有两个核心的方法,doGet()和doPost(),由于get请求和post请求有区别:get请求的参数参数在url里面,不安全,post请求的参数放在请求体中,安全;他们两种请求的逻辑应该有所差别,但HttpServlet底层已经做过了处理,因此这里代码简写;【后面介绍】

给前台相应HTML文本:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "MyServlet2", value = "/demo2")
public class MyServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取打印输出流
        PrintWriter writer = response.getWriter();
        //响应一段HTML数据
        writer.write("<h1 style='color:red;'>Hello Browser!</font>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

在这里插入图片描述
给前台相应JSON数据:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "MyServlet3", value = "/demo3")
public class MyServlet3 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	request.setCharacterEncoding("utf-8");
    	response.setContentType("application/json;charset=utf-8");
        String jsonStr = "{\"username\":\"zhangsan\",\"password\":\"123456\"}";
        response.getWriter().write(jsonStr);
    }
}

在这里插入图片描述

一般异步请求的时候响应JSON数据,响应JSON数据的时候,后台设置MIME文件类型为:response.setContentType("application/json;charset=utf-8");或者response.setContentType("text/json;charset=utf-8");

关于中文乱码问题

在这里插入图片描述

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(value = "/demo1")
public class MyServlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取打印输出流
        PrintWriter writer = response.getWriter();
        //响应一段文本数据
        writer.write("你好,世界!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

为什么会乱码?
计算机中的数据都是以二进制形式存储的,因此,传输文本时,就会发生字符的字节之间的转换。字符与字节之间的转换时通过查码表完成的,字符转换为字节的过程称为编码,字节转换为字符的过程称为解码,如果编码和解码使用的码表不一样,则会出现乱码问题。
注意:HttpServletResponse对象的字符输出流在编码时,默认采用的是ISO 8859-1编码,该编码方式不兼容中文,比如会将"中国"编码为"63 63"(在ISO 8959-1的码表中查不到的字符会显示63)。当浏览器对接收到的数据进行解码时,会默认采用Unicode编码,将"63"解码为"?",浏览器就将"中国"两个字符解码为"??"。在这里插入图片描述

  • 怎么处理,我们给请求对象和响应对象设置编码方式:
request.setCharacterEncoding("utf-8");             
response.setContentType("text/html;charset=utf-8");

在这里插入图片描述

给前台响应一张图片

如果我们把一张图片资源放在web下,只要服务器开启,谁都可以访问,这样没有任何逻辑;我们可以将图片资源放在WEB-INF下,这样前台不能通过键入URL访问,后台通过响应对象以流的方式响应给前台,有了针对性;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet(name = "MyServlet4", value = "/demo4")
public class MyServlet4 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String localName = request.getLocalName();
        System.out.println(localName);
        if(localName.equals("192.168,18.123")){
            //做出逻辑判断,如果是该主机要求访问后台资源,结束方法
            return;
        }

        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("WEB-INF/google.png");

        //图片资源使用字节流写出去
        ServletOutputStream outputStream = response.getOutputStream();
        FileInputStream fis = new FileInputStream(realPath);
        int len = 0;
        byte[] bytes = new byte[1024 * 8];
        while ((len = fis.read(bytes)) != 0) {
            outputStream.write(bytes, 0, len);
            outputStream.flush();
        }
        fis.close();
        outputStream.close();
    }
}

绘制验证码响应给前台

我们在做登录与注册的时候,通常出现的验证码都是后台绘制好响应给前台;

import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

@WebServlet(name = "DrawCode", value = "/code")
public class DrawCode extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 200;
        int height = 70;
        //获取一个图片对象
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //获取一只画笔
        Graphics graphics = image.getGraphics();
        //首先画一个矩形
        graphics.drawRect(0, 0, width, height);
        //填充背景色
        graphics.setColor(getRandomColor());
        graphics.fillRect(0, 0, width, height);

        //画随机字符
        graphics.setFont(new Font("微软雅黑", Font.BOLD, 40));
        String str = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz";
        String code = "";
        Random random = new Random();
        for (int i = 1; i <= 4; i++) {
            int index = random.nextInt(str.length());
            graphics.setColor(getRandomColor());
            String s = String.valueOf(str.charAt(index));
            code += s;
            graphics.drawString(s, width / 5 * i, height / 5 * 3);
        }

        //画干扰线
        for (int i = 0; i < 20; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            graphics.setColor(getRandomColor());
            graphics.drawLine(x1, y1, x2, y2);
        }

        //画干扰点
        for (int i = 0; i < 10; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            graphics.setColor(getRandomColor());
            graphics.drawString(".", x, y);
        }

        //将随机生成的验证码存放在全局域对象中
        ServletContext context = this.getServletContext();
        context.setAttribute("code", code);

        //必须设置响应内容类型为图片,否则前台不识别
        response.setContentType("image/png");
        //获取文件输出流
        OutputStream os = response.getOutputStream();
        //输出图片流
        ImageIO.write(image, "png", os);
        os.flush();
        os.close();//关闭流
    }

    /***
     * 获取随机颜色
     * @return
     */
    private static Color getRandomColor() {
        Random ran = new Random();
        Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
        return color;
    }
}

在这里插入图片描述

3、请求对象详解

  • HttpServletRequest接口继承ServletRequest接口,专门用于封装HTTP请求消息。由于HTTP请求信息包括请求行、请求头和请求体三部分, 所以HttpServletRequest接口定义了获取请求行、请求头和请求体的相关方法。

获取请求行的相关方法

方法说明
public String getMethod()获取HTTP请求方式,POST、GET等
public String getRequestURI()获取请求行中资源名称部分
public String getQueryString()获取请求行中的参数部分
public String getProtocol()获取请求行中协议名称和版本,如HTTP 1.1
public String getContextPath()获取请求URL中属于Web应用程序的路径

getQueryString()的使用:

由于该方法是返回包含在请求 URL 中路径后面的查询字符串。如果 URL 没有查询字符串,则此方法返回 null;因为对于POST请求来说,请求参数虽然也可以拼接在URL后面,但是我们一般不这样做,所以POST请求的参数一般放在请求体里面,也就是说使用POST请求URL后面是没有参数的,因此该方法的返回值一定是null,也即该方法只对get请求有效

get请求与post请求详解

【注意】:在tomcat8.0以上的版本,GET请求,请求参数是中文不乱码;而对于POST请求,请求参数是中文还是会乱码;

代码示例:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String queryString = request.getQueryString();
    System.out.println("请求参数为:" + queryString);
    //请求参数为:username=zhangsan&pwd=123456
    //请求参数为:username=%E5%BC%A0%E4%B8%89&pwd=123456
}

在这里插入图片描述

这里可以看到一个现象:如果是英文的用户名和密码,后台获得的是本来的字符串,但是如果采用中文的用户名和密码,后台得到的是编码过后的字符串,为什么?

浏览器中的URL为什么对中文进行编码详解

因为网络标准对URL的编码做了硬性的规定,只能包含英文字母、特殊字符等存在,这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致"URL编码"成为了一个混乱的领域;
后台如果想得到用户输入的字符串,可以进行编解码;

解决方法:

Java中有编解码的对应类与方法:
public class URLEncoderextends Object——编码
public static String encode(String s, String enc):将字符串s转为按照enc编码格式编码;
public class URLDecoderextends Object——解码
public static String decode(String s, String enc):将字符串s按照enc编码格式解码;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String queryString = request.getQueryString();
    String decode = URLDecoder.decode(queryString, "utf-8");
    System.out.println("后台解码过后的请求参数为:" + decode);

    String gbk = URLEncoder.encode(decode, "gbk");
    System.out.println("编码过后的文字为:" + gbk);

    /*后台解码过后的请求参数为:username=张三&pwd=123456
    编码过后的文字为:username%3D%D5%C5%C8%FD%26pwd%3D123456*/
}

获取客户端信息以及上下文路径的方法

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "MyServlet8", value = "/demo8")
public class MyServlet8 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取上下文路径
        String contextPath = request.getContextPath();
        System.out.println(contextPath);
        //获取客户端IP
        String addr = request.getRemoteAddr();
        System.out.println(addr);
        //获取主机名
        String host = request.getRemoteHost();
        System.out.println(host);
        //获取端口
        int port = request.getRemotePort();
        System.out.println(port);
        
        /*/servlet20200819_war_exploded
        0:0:0:0:0:0:0:1
        0:0:0:0:0:0:0:1
        58312*/
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

获取请求消息头的相关方法

方法说明
public String getHeader(String name)获取指定字段的值,如果没有返回null,如果有多个返回第一个值
public Enumeration getHeaders(String name)返回一个指定字段的Enumeration集合对象
public Enumeration getHeaderNames()返回一个包含所有字段的Enumeration集合对象
public String getContentType()获取Content-Type字段的值

代码示例:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URLDecoder;

@WebServlet(name = "MyServlet7", value = "/demo7")
public class MyServlet7 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取部分请求头
        String referer = request.getHeader("Referer");
        String header = request.getHeader("User-Agent");
        System.out.println(referer);
        System.out.println(header);
        /*http://localhost:8080/servlet20200819_war_exploded/
        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36*/
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

获取请求体的相关方法

方法说明
public ServletInputStream getInputStream()获取请求的ServletInputStream对象,如果实体内容为非文本,只能通过getInputStream方法获取请求体消息体
public BufferedReader getReader()获取请求的BufferedReader对象,该对象会将实体内容字节数据转换为指定字符集编码的文本字符串
public String getParameter(String name)获取指定的参数值,没有该参数返回null
public Enumeration getParameterNames()返回一个包含所有参数名的Enumeration对象
public String[] getParameterValues(String name)HTTP请求中可能有多个相同的参数,获取同一个参数名对应的所有参数值

代码示例:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

@WebServlet(name = "MyServlet7", value = "/demo7")
public class MyServlet7 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求参数的两种方式
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //返回枚举类型
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String key = names.nextElement();
            String value = request.getParameter(key);
            System.out.println(key + "--------------" + value);
        }
        System.out.println("------------------------------------------");

        Map<String, String[]> map = request.getParameterMap();
        Set<Map.Entry<String, String[]>> entries = map.entrySet();
        for (Map.Entry<String, String[]> entry : entries) {
            //默认值的返回值是一个字符串数组,因为表单当中会有多选
            System.out.println(entry.getKey() + "--------" + entry.getValue()[0]);
        }

        /*username--------------张三
        pwd--------------123456
        ------------------------------------------
        username--------张三
        pwd--------123456*/
    }
}

1、我们在做前后台交互的时候,前台文本框中的name属性最终被作为键提交给后台,后台就是通过这个键找到它对应的值;表单中表单项填写的太多了肯定不能通过getParameter()这个方法一个一个找,因此以上两种方法非常适合;
2、并且注意:getParameterMap()中获取到的键都是数组,如果不是多选,只需要取出第一个元素,就是我们要获取的表单的值;

post请求获得请求参数的方式:getReader()

post请求的请求参数是放在请求体里面的,因此只能通过该方法读取,并且post请求的提交的数据大小没有限制,而get请求是有限制的;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URLDecoder;

@WebServlet(name = "MyServlet7", value = "/demo7")
public class MyServlet7 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BufferedReader reader = request.getReader();
        String s = reader.readLine();
        String decode = URLDecoder.decode(s, "utf-8");
        System.out.println("后台解码过后的请求参数为:" + decode);
        //后台解码过后的请求参数为:username=张三&pwd=123456
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

URI与URL的区别

URL与URI区别详解原文链接

url统一资源定位符,uri统一资源标识符,他们之间有什么区别呢?看下代码打印的结果:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "MyServlet5", value = "/demo5")
public class MyServlet5 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = request.getMethod();
        System.out.println("这一次的请求方式是:" + method);

        //获取
        // url 统一资源定位符
        // uri 统一资源标识符
        String requestURI = request.getRequestURI();
        System.out.println("统一资源标识符:" + requestURI);
        StringBuffer requestURL = request.getRequestURL();
        System.out.println("统一资源定位符:" + requestURL);

        /*这一次的请求方式是:GET
        统一资源标识符:/servlet20200819_war_exploded/demo5
        统一资源定位符:http://localhost:8080/servlet20200819_war_exploded/demo5

        这一次的请求方式是:POST
        统一资源标识符:/servlet20200819_war_exploded/demo5
        统一资源定位符:http://localhost:8080/servlet20200819_war_exploded/demo5*/
    }
}

可以发现统一资源标识符没有给出协议、主机名、端口号,只给出了资源标识符;看过别人的文档之后,自己的总结如下:
1、有一家公司的总经理给了你一张名片,上面写了他的头衔:北京XXX公司总经理张三,还有他的办公室地址,北京市海淀区长安街35号北京XXX公司总经理办公室
2、那么这个头衔就和张三对应起来了,只要一说到这个头衔,大家都知道说的是张三,反应到网络世界,这个头衔就叫做URI,只要你给我一个URI,我就知道它代表了什么,比如,http://www.sina.com.cn代表了新浪网,admin@qq.com代表了某一个人的qq邮箱,你的qq号也是一个URI(腾讯服务器内可以识别就是你的QQ账户),URI就是网络资源的头衔,通过URI标记可以把网络世界里面的每一个事物都加以标记并区分开来。
3、好的,现在出现了一个问题,你现在知道北京XXX公司总经理是张三,但是我们找不到他,因为不知道他的地址,虽然有他的URI头衔,但是除此以外,你对他具体的情况一无所知;于是你还得到他的办公室地址,通过北京市海淀区长安街35号北京XXX公司总经理办公室这个地址,你就找到了张三。反应到网络世界,网络世界里面的每一个资源不光有自己的头衔,还要能够被人访问。所以,网络地址是必须的,否则,这个网络资源的存在没有任何意义,这个地址就叫做URL
4、URI强调的是给资源标记命名URL强调的是给资源定位,由于URL显然比URI包含信息更多,所以URL也充当了WWW万维网里面URI的角色,但是他比URI多了一层意义,我不光知道你叫什么,我还知道你在哪里;
5、URL 是 URI 的子集,所有的URL都是URI,但不是每个URI都是URL,还有可能是URN;

4、this.doGet(req,res)的推导

  • 上面所有的servlet代码都是下面这样的模板:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

@WebServlet(name = "MyServlet7", value = "/demo7")
public class MyServlet7 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     	…………
    }
}
  • 因为get请求和post请求的传递数据的方式是不一样的,因此数据取出来的方式也不一样,但是为什么可以写成上面的模式,这样怎么取出数据?
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet(name = "MyServlet8", value = "/demo8")
public class MyServlet8 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String string = request.getQueryString();
        System.out.println("这是get请求读取到的数据:" + string);
        //解析数据,展示数据
        String[] split = string.split("&");
        String[] one = split[0].split("=");
        String[] two = split[1].split("=");
        System.out.println(one[0] + ":" + one[1] + "," + two[0] + ":" + "," + two[1]);
        /*这是post请求读取到的数据:username=zhangsan&pwd=123456
        username:zhangsan,pwd:,123456*/
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        BufferedReader reader = request.getReader();
        String line = reader.readLine();
        System.out.println("这是post请求读取到的数据:" + line);
        //解析数据,展示数据
        String[] split = line.split("&");
        String[] one = split[0].split("=");
        String[] two = split[1].split("=");
        System.out.println(one[0] + ":" + one[1] + "," + two[0] + ":" + "," + two[1]);
        /*这是get请求读取到的数据:username=zhangsan&pwd=123456
        username:zhangsan,pwd:,123456*/
    }
}

上面的代码只有最开始读取字符串那行代码不同,其他地方都一样,可以将所有代码抽取在doGet()或者doPost()中,获取当前请求的方式,进行判断,然后根据不同方式取出数据;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet(name = "MyServlet8", value = "/demo8")
public class MyServlet8 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = request.getMethod();
        String line = "";
        if (method.equalsIgnoreCase("get")) {
            line = request.getQueryString();
        } else if (method.equalsIgnoreCase("post")) {
            BufferedReader reader = request.getReader();
            line = reader.readLine();
        }
        //解析数据,展示数据
        String[] split = line.split("&");
        String[] one = split[0].split("=");
        String[] two = split[1].split("=");
        System.out.println(one[0] + ":" + one[1] + "," + two[0] + ":" + "," + two[1]);
        /*username:zhangsan,pwd:,123456*/
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

其实,HttpServlet 里面已经做了处理,无论是get请求还是post请求,都可以直接使用请求对象HttpServletRequest对象获取前台数据;
在这里插入图片描述

5、请求转发与重定向

请求转发(内部转发)

  • Forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应;也就是请求与响应都被分为了多份,交由几个servlet执行;
    在这里插入图片描述
  • 请求转发的特点:

1、一次请求,一次响应;
2、地址栏不发生变化;
3、请求转发只能访问内部的资源,不能访问该web工程以外的资源;
4、可以访问到WEB-INF下的资源;

  • 请求转发使用到的方法:
request.getRequestDispatcher("/要内部转发的路径").forward(request, response);

代码示例:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("a 收到请求了");
        System.out.println("处理请求");
        System.out.println("转发到b");
        //内部转发
        request.getRequestDispatcher("/b").forward(request, response);
        System.out.println("回到a");
    }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("b 收到请求了");
        System.out.println("处理请求");
        System.out.println("转发到c");
        //内部转发
        request.getRequestDispatcher("/c").forward(request, response);
        System.out.println("回到b");
    }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("c 收到请求了");
        System.out.println("处理请求");
        //内部转发
        request.getRequestDispatcher("/home.jsp").forward(request, response);
    }

在这里插入图片描述

a接收到请求之后,自己处理了一部分请求,但是把剩余的部分交给了b处理,b处理了部分请求,又把剩余的请求交给了c处理,最终页面跳转到欢迎页面;在回去的时候,响应还是从a中返回,相当于绕了一圈还是回到了a,也因此说,内部转发是一次请求,一次响应;

重定向

  • Redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个新的请求。
    在这里插入图片描述
  • 重定向的特点:

1、多次请求,多次响应;
2、地址栏发生变化;
3、可以访问内部资源,也可以访问外部资源;
4、不能访问WEB-INF下的资源;

  • 重定向用到的方法:
//设置状态码302重定向
response.setStatus(302);
//设置响应头location
response.setHeader("location","http://www.baidu.com");

//以上两步可以合成一个简单的方法
response.sendRedirect("http://www.baidu.com");

代码示例:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置状态码302重定向
        response.setStatus(302);
        //设置响应头location
        response.setHeader("location","http://www.baidu.com");

        //以上两步可以合成一个简单的方法
        response.sendRedirect("http://www.baidu.com");
    }

重定向的过程类似于这样的:当用户在地址栏键入URL,通过虚拟路径找到这个servlet,但是这个servlet内部是这样做的:首先设置状态码302,意在告诉浏览器我这里不能对你的响应作出处理,你可以去访问百度,并把这个百度的地址告诉了它;于是浏览器重新向百度发出请求,最终百度做出了回应;因此重定向是多次请求,多次响应的;并且由于再次访问的时候还是属于浏览器访问,因此不能访问到WEB-INF下的资源;

请求域

请求转发的出现引出了一个域对象的存在:请求域request对象,之前认识了一个全局域对象:ServletContext,它的作用域是整个web工程;而请求域的范围是:请求转发中多个资源共享的数据,也就是仅限于本次请求与响应

  • 用到的方法:
void setAttribute(String name,Object obj):存储数据
Object getAttitude(String name):通过键获取值
void removeAttribute(String name):通过键移除键值对

6、web项目中路径书写的问题

学习web工程,会发现一个问题,关于资源路径的书写,有的时候需要加上项目的上下文路径,有的时候则不需要,怎么区分?

  • 内部转发/请求转发不需要写项目的上下文路径,这属于服务器内部资源的跳转;
  • HTML、JSP页面等前端页面中的路径都需要写项目的上下文路径;
  • 重定向也需要写项目的上下文路径,因为新的请求是从浏览器发出;
  • 访问别人的服务器,路径必须全部写完整,也就是协议、主机名、端口都要有;如果自己的电脑访问自己的服务器,写全路径肯定没有问题,但是注意此时的主机名不能写成IP,而应该写成localhost,这涉及到了跨域请求的问题;

跨域请求详解

7、前后台交互的登陆注册案例

画验证码的servlet:

import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

@WebServlet(name = "DrawCode", value = "/code")
public class DrawCode extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 200;
        int height = 70;
        //获取一个图片对象
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //获取一只画笔
        Graphics graphics = image.getGraphics();
        //首先画一个矩形
        graphics.drawRect(0, 0, width, height);
        //填充背景色
        graphics.setColor(getRandomColor());
        graphics.fillRect(0, 0, width, height);

        //画随机字符
        graphics.setFont(new Font("微软雅黑", Font.BOLD, 40));
        String str = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz";
        String code = "";
        Random random = new Random();
        for (int i = 1; i <= 4; i++) {
            int index = random.nextInt(str.length());
            graphics.setColor(getRandomColor());
            String s = String.valueOf(str.charAt(index));
            code += s;
            graphics.drawString(s, width / 5 * i, height / 5 * 3);
        }

        //画干扰线
        for (int i = 0; i < 20; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            graphics.setColor(getRandomColor());
            graphics.drawLine(x1, y1, x2, y2);
        }

        //画干扰点
        for (int i = 0; i < 10; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            graphics.setColor(getRandomColor());
            graphics.drawString(".", x, y);
        }

        //将随机生成的验证码存放在全局域对象中
        ServletContext context = this.getServletContext();
        context.setAttribute("code", code);

        //必须设置响应内容类型为图片,否则前台不识别
        response.setContentType("image/png");
        //获取文件输出流
        OutputStream os = response.getOutputStream();
        //输出图片流
        ImageIO.write(image, "png", os);
        os.flush();
        os.close();//关闭流
    }

    /***
     * 获取随机颜色
     * @return
     */
    private static Color getRandomColor() {
        Random ran = new Random();
        Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
        return color;
    }
}

登陆servlet:

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

@WebServlet(name = "LoginServlet", value = "/login")
public class LoginServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码格式
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");

        //获取前台提交的数据
        String username = request.getParameter("username").trim();
        String password = request.getParameter("pwd").trim();
        //做出非空校验
        if (username != null && password != null) {
            try {
                //查询数据库
                Properties properties = new Properties();
                //注意文件加载路径的写法
                properties.load(this.getClass().getClassLoader().getResourceAsStream("druid.properties"));
                //获取德鲁伊连接池对象
                DataSource ds = DruidDataSourceFactory.createDataSource(properties);
                //获取dbutils工具类的操作对象
                QueryRunner qr = new QueryRunner(ds);
                //进行查询
                String sql = "select username,password from user where username=? and password=?";
                //获取查询出来的用户对象
                user u = qr.query(sql, new BeanHandler<user>(user.class), username, password);
                if (u != null) {
                    //如果用户存在,让页面内部转发到新的页面,然后把用户名放在请求域对象里面,前台使用EL表达式取出来
                    request.setAttribute("username", u.getUsername());
                    //内部转发
                    request.getRequestDispatcher("index.jsp").forward(request, response);
                } else {
                    //将错误信息放在请求域对象当中
                    request.setAttribute("msg", "用户名或密码错误");
                    //页面内部转发到当前页面
                    request.getRequestDispatcher("login.jsp").forward(request, response);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

注册servlet:

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

@WebServlet(name = "RegisterServlet", value = "/register")
public class RegisterServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            //设置编码格式
            request.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=utf-8");
            //获取前台提交的数据
            Map<String, String[]> map = request.getParameterMap();
            //数据的校验

            //把检验用户名、密码不为空,两次密码输入一致放在前台校验,你不要拿到后台
            String[] username = map.get("username");
            String[] pwd = map.get("pwd");
            String[] repwd = map.get("repwd");
            String[] tel = map.get("tel");
            String[] email = map.get("email");
            String[] sex = map.get("sex");
            String[] birthday = map.get("birthday");
            String[] hobby = map.get("hobby");
            StringBuilder myHobby = new StringBuilder("==");
            if (hobby.length != 0) {
                for (int i = 0; i < hobby.length; i++) {
                    myHobby.append(hobby[i]).append("==");
                }
            }

            //查询数据库
            Properties properties = new Properties();
            //注意文件加载路径的写法
            properties.load(this.getClass().getClassLoader().getResourceAsStream("druid.properties"));
            //获取德鲁伊连接池对象
            DataSource ds = DruidDataSourceFactory.createDataSource(properties);
            //获取dbutils工具类的操作对象
            QueryRunner qr = new QueryRunner(ds);
            //进行查询
            String sql = "select username from user where username=?";
            //获取查询出来的用户对象
            user u = qr.query(sql, new BeanHandler<user>(user.class), username[0]);
            if (u != null) {
                //如果用户存在,告诉用户重新注册
                request.setAttribute("hasUser", "yes");
                //内部转发到提示页面
                request.getRequestDispatcher("empty.jsp").forward(request, response);
            } else {
                //如果用户不存在
                request.setAttribute("hasUser", "no");

                //开始判断验证码是否正确
                String code = (String) this.getServletContext().getAttribute("code");
                String[] codes = map.get("code");
                if (code.equalsIgnoreCase(codes[0])) {
                    //如果验证码输入正确
                    //将数据插入到数据库

                    String sql1 = "insert into user values(?,?,?,?,?,?,?,?)";
                    int update = qr.update(sql1, null, username[0], pwd[0], email[0], sex[0], birthday[0], tel[0], myHobby.toString());

                    if (update > 0) {
                        // 告知用户,你注册成功了!
                        request.setAttribute("username", username[0]);
                        //内部转发到登陆页面
                        request.getRequestDispatcher("login.jsp").forward(request, response);
                    }
                } else {
                    request.setAttribute("codeIsRight", "no");
                    //页面内部转发到提示页面
                    request.getRequestDispatcher("empty.jsp").forward(request, response);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实体类user

import java.util.Date;

public class user {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String sex;
    private Date birthday;
    private String phone;
    private String hobby;

    public user() {
    }
    …………get和set方法
}

提示页面

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/19
  Time: 9:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>提示页面</title>
</head>
<script>
    function index(){
        alert("这个是提示页面,你来没!");
        var codeIsRight = '<%=request.getAttribute("codeIsRight")%>';
        var hasUser = '<%=request.getAttribute("hasUser")%>';

        if (codeIsRight == "no") {
            alert("验证码输入错误!");
            window.location.href = 'register.jsp';
        }

        if (hasUser == "yes") {
            alert("该用户已经存在,请重新注册!");
            window.location.href = 'register.jsp';
        }
    }

    index();
</script>
<body>
</body>
</html>

欢迎页面

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/18
  Time: 19:48
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
   <center>
       <h1>
           <%=request.getAttribute("username") == null ? "" : request.getAttribute("username")%>欢迎你的访问!
       </h1>
   </center>
</body>
</html>

登陆页面:

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/18
  Time: 21:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>用户登陆</title>
    <style type="text/css">
        input[type="submit"] {
            background-color: #0d65d0;
            color: white;
            border: 0px;
            width: 100px;
            height: 40px;
            font-size: 18px;
        }

        input {
            height: 30px;
        }

        td {
            font-family: Microsoft YaHei;
        }
    </style>
</head>
<body>
<center>
    <h1>登陆页面</h1>
    <form action="/LoginAndRegister_war_exploded/login" method="post">
        <table border="0" cellspacing="0" cellpadding="0" width="400px" height="200px">
            <tr>
                <td style="text-align: right;">用户名:</td>
                <td><input type="text" name="username" placeholder="请输入用户名" style="width: 90%;"/></td>
            </tr>
            <tr>
                <td style="text-align: right;">密码:&nbsp;&nbsp;&nbsp;</td>
                <td><input type="password" name="pwd" placeholder="请输入密码" style="width: 90%;"/></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="登陆" style="margin-left: 40%"/>
                    <a href="/LoginAndRegister_war_exploded/register.jsp" style="font-size: 13px;margin-left: 10px;">没有账号,请先注册</a>
                </td>
                <td></td>
            </tr>
            <tr>
                <td colspan="2">
                    <div style="color: red;margin-left: 40%;">
                        <%=request.getAttribute("msg") == null ? "" : request.getAttribute("msg")%>
                    </div>
                </td>
                <td></td>
            </tr>
        </table>
    </form>
</center>
</body>
</html>

注册页面:

<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2020/8/18
  Time: 21:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>用户注册</title>
    <style type="text/css">
        input[type="submit"] {
            background-color: #0d65d0;
            color: white;
            border: 0px;
            width: 100px;
            height: 40px;
            font-size: 18px;
        }

        input {
            height: 30px;
        }

        td {
            font-family: Microsoft YaHei;
        }
    </style>
</head>
<body>
<center>
    <h1>注册页面</h1>
    <form action="/LoginAndRegister_war_exploded/register" method="post" onsubmit="return check()">
        <table border="0" cellspacing="0" cellpadding="0" width="500px" height="400px">
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username" placeholder="请输入用户名" style="width: 90%;" id="username"/></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="pwd" placeholder="请输入密码" style="width: 90%;" id="pwd"/></td>
            </tr>
            <tr>
                <td>确认密码:</td>
                <td><input type="password" name="repwd" placeholder="请重复输入密码" style="width: 90%;" id="repwd"/></td>
            </tr>
            <tr>
                <td>电话:</td>
                <td><input type="tel" name="tel" value="" placeholder="请输入电话号码" style="width: 90%;"/></td>
            </tr>
            <tr>
                <td>邮箱:</td>
                <td><input type="email" name="email" placeholder="请输入邮箱" style="width: 90%;"/></td>
            </tr>

            <tr>
                <td>性别:</td>
                <td>
                    <input type="radio" name="sex" id="0" value="男" style="vertical-align: middle;"
                           checked="checked"/><label
                        for="0"></label> &nbsp;

                    <input type="radio" name="sex" id="1" value="女" style="vertical-align: middle;"/><label
                        for="1"></label>
                </td>
            </tr>
            <tr>
                <td>出生日期:</td>
                <td><input type="date" name="birthday" value="" style="width: 90%;"/></td>
            </tr>
            <tr>
                <td>爱好:</td>
                <td><input type="checkbox" name="hobby" id="song" value="唱歌" style="vertical-align: middle;"
                           checked="checked"/><label
                        for="song">唱歌</label>
                    <input type="checkbox" name="hobby" id="sport" value="运动" style="vertical-align: middle;"/><label
                            for="sport">运动</label>
                    <input type="checkbox" name="hobby" id="read" value="阅读" style="vertical-align: middle;"/><label
                            for="read">阅读</label>
                </td>
            </tr>
            <tr>
                <td>验证码:</td>
                <td colspan="3">
                    <input type="text" name="code" placeholder="请输入验证码" id="code"/>
                    <img src="/LoginAndRegister_war_exploded/code"
                         style="width: 100px;height: 30px;border: 1px black solid;margin-bottom: -10px;" id="img"
                         onclick="switchCode()">
                    <a href="#" style="font-size: 15px;font-family: Microsoft YaHei;margin-left: 10px;"
                       onclick="switchCode()">看不清?换一张</a></td>
            </tr>
            <tr>
                <td colspan="2">
                    <center><input type="submit" value="注册"/></center>
                </td>
                <td></td>
            </tr>
        </table>
    </form>
</center>
</body>
<script>
    function switchCode() {
        var img = document.getElementById("img");
        //欺骗服务器
        img.src = "/LoginAndRegister_war_exploded/code?" + new Date().getTime();
    }

    function check() {
        alert("你进来了吗!");
        var flag = true;
        var name = document.getElementById("username");
        var pwd = document.getElementById("pwd");
        var repwd = document.getElementById("repwd");
        var code = document.getElementById("code");
        if (name.value.trim().length == 0) {
            alert("用户名不能为空!");
            name.focus();
            return false;
        }
        if (pwd.value.trim().length == 0) {
            alert("密码不能为空!");
            pwd.focus();
            return false;
        }

        if (repwd.value.trim().length == 0 || repwd.value != pwd.value) {
            alert("两次输入的密码不一致!");
            repwd.focus();
            return false;
        }
        if (code.value.trim().length == 0) {
            alert("验证码不能为空!");
            code.focus();
            return false;
        }

        return flag;
    }
</script>
</html>

效果演示:
在这里插入图片描述

8、普通项目怎么转为web工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值