javaweb-Servlet的使用(乱码解决方式和验证码原理)

295 阅读11分钟

xml设置

创建项目需要勾选下图的创建xml(适用于3.0以下版本,3.0以上的可以跳过使用下面的注解开发) 在这里插入图片描述 创建完项目后,找到web---WEB-INF---web.xml配置以下代码

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
        <!--  配置servlet路径  -->
    <servlet>
        <!--    起名    -->
        <servlet-name>demo1</servlet-name>
        <!--   配置全类名(全限定名)     -->
        <servlet-class>com.jwz.web.servlet.ServletDemo01</servlet-class>
        <!--
        指定servlet的创建时机
        1.第一次被访问时创建
        <load-on-startup>的值为负数
        2.在服务器启动时创建
        <load-on-statup>的值为日或正整数
        启动服务器,注意初始化方法打印的值
        -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <!--  配置servlet的mapping -->
    <servlet-mapping>
        <!--下面名字和上面保持一致-->
        <servlet-name>demo1</servlet-name>
        <!--路径-->
        <url-pattern>/xiaoji</url-pattern>
    </servlet-mapping>
</web-app>

注解开发

@WebServlet() //配置访问的路径,无需配置xml,类似xml的url-pattern
@WebServlet(value = "/lujing")
@WebServlet(urlPatterns = "/lujing") //以上两种方式均可以配置访问路径
//一般默认使用value,因为value可以省略,所以可以简写下面形式
@WebServlet("/lujing")

servlet结构说明

@webservlet("/xiaoji")
public class ServletDemo01 implements Servlet {

    /**
     * 初始化方法
     * 在servlet被创建时,只会执行一次
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init.........");
    }

    /**
     * 获取ServletConfig对象
     * ServletConfig:servlet的配置对象
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     *提供服务方法
     * 每一次servlet被访问时执行,执行多次
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("hello servlet");
    }


    /**
     * 获取servlet的一些信息
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     *销毁方法
     *服务器正常关闭时执行,执行一次
     */
    @Override
    public void destroy() {
        System.out.println("destroy....");
    }
}

路径的设置

package com.jwz.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;

//定义多个路径,以下三个路径均可以访问

/**
 * 路径定义规则
 *  /XXX
 *  /XXX/XXX:多层路径,目录结构
 *  *.xiaoji
 */
//@WebServlet({"/d4","/dd4","/ddd4"})
//@WebServlet("/user/demo04")//多层路径
//@WebServlet("/user/*")//多层路径最后一层为任意匹配路径
//@WebServlet("/*")//为任意匹配路径
  @WebServlet("*.xiaoji")//任意字段.xiaoji才能访问
public class ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo04....");
    }
}

继承关系

直接继承GenericServlet,相当于只需要重写servlet的service方法

public class ServletDemo02 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("servlet02");
    }
}

继承HttpServlet有两个方法分别监听get和post的请求

@WebServlet("/xiaoji1")
public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doget");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("dopost");
    }
}

HttpServlet中的request方法

在继承的HttpServlet中的request获取请求行数据的方法
  1. 获取请求方式 :GET * String getMethod() 2. 获取虚拟目录:/day14 * String getContextPath() 3. 获取Servlet路径: /demo1 * String getServletPath() 4. 获取get方式请求参数:name=zhangsan * String getQueryString() 5. 获取请求URI:/day14/demo1 * String getRequestURI(): /day14/demo1 * StringBuffer getRequestURL() :http://localhost/day14/demo1 * URL:统一资源定位符 : http://localhost/day14/demo1 * URI:统一资源标识符 : /day14/demo1 6. 获取协议及版本:HTTP/1.1 * String getProtocol() 7. 获取客户机的IP地址: * String getRemoteAddr()
@WebServlet("/demo01")
public class demo01 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //发送请求为Get,这里使用doget
        //获取请求方式:GET
        System.out.println(request.getMethod());
        //获取虚拟目录:/servlet_war_exploded
        System.out.println(request.getContextPath());
        //获取servlet路径:/demo01
        System.out.println(request.getServletPath());
        //获取get请求参数:name=zhangsan&age=28
        System.out.println(request.getQueryString());
        //获取请求的URI:/servlet_war_exploded/demo01
        System.out.println(request.getRequestURI());
        //获取请求的URL:http://localhost:8080/servlet_war_exploded/demo01
        System.out.println(request.getRequestURL());
        //获取协议及版本:HTTP/1.1
        System.out.println(request.getProtocol());
        //获取客户机的ip地址:0:0:0:0:0:0:0:1
        System.out.println(request.getRemoteAddr());
    }
}
在继承的HttpServlet中的request获取请求头数据的方法
  • String getHeader(String name):通过请求头的名称获取请求头的值 * Enumeration getHeaderNames():获取所有的请求头名称
package com.jwz.servlet.request方法演示;
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;

/**
 * 演示request获取请求行数据(具体方法看txt文本)
 */
@WebServlet("/demo02")
public class demo02获取请求头 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //演示获取请求头数据
        //获取所有请求头名称
        Enumeration<String> e = request.getHeaderNames();
        //遍历
        while(e.hasMoreElements()){
            String name = e.nextElement();
            //根据名称获取请求头的值
            String value = request.getHeader(name);
            System.out.println(name+"-----"+value);
        }
        System.out.println("=====================");
        //获取指定请求头(user-agent)数据
        String agent = request.getHeader("user-agent");
        //判断浏览器的版本
        if(agent.contains("Chrome")){
            System.out.println("获取到谷歌浏览器");
        }else if(agent.contains("Firefox")){
            System.out.println("获取到火狐浏览器");
        }
        System.out.println("=====================");
        //获取指定请求头(referer)数据
        //直接用浏览器访问打印为null
        //在web文件夹创建htmla标签为<a href="/虚拟目录/访问路径">referer演示</a>
        //然后访问html文件:http://localhost:8080/虚拟目录/referer.html,通过超链接点击访问地址即可
        //最后打印出:http://localhost:8080/servlet_war_exploded/referer.html
        //也就是html文件访问路径
        String referer = request.getHeader("referer");
        System.out.println(referer);
    }
}
referer的html文件内容
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
</head>
<body>
<a href="/servlet_war_exploded/demo02">referer演示</a>
</body>
</html>
上面所有代码统一打印出来的结果

在这里插入图片描述

在继承的HttpServlet中的request获取请求体数据的方法

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数 * 步骤: 1. 获取流对象 * BufferedReader getReader():获取字符输入流,只能操作字符数据 * ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据 * 在文件上传知识点后讲解 2. 再从流对象中拿数据

示例java代码(post请求)
@WebServlet("/demo03")
public class demo03获取请求体 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求消息体
        //1.获取字符流
        BufferedReader br = req.getReader();
        //2.读取数据
        String line=null;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        //最后开启服务器,访问body.html,点提交后看控制台
    }
}

示例上面代码里的body.html代码(post请求)
<body>
<--action里面为虚拟目录加访问映射名-->
<form action="/xiaoji/demo03" method="post">
	用户名:<input type="text" name="username"><br>
	密码:<input type="text" name="password"><br>
	<input type="submit" value="提交"><br>
</form>>
</body>
访问body.html以后,表单填写账号密码,点击提交以后的效果图

在这里插入图片描述

其他功能:
1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
			1. String getParameter(String name):根据参数名称获取参数值    username=zs&password=123
			2. String[] getParameterValues(String name):根据参数名称获取参数值的数组  hobby=xx&hobby=game
			3. Enumeration<String> getParameterNames():获取所有请求的参数名称
			4. Map<String,String[]> getParameterMap():获取所有参数的map集合
			当两个请求业务逻辑相同时,可以使用this.doPost(request,response)
直接调用另一个请求的方法。
			5.中文乱码问题:
				* get方式:tomcat 8 已经将get方式乱码问题解决了
				* post方式:会乱码
					* 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");
		""
		请求转发:一种在服务器内部的资源跳转方式
			1. 步骤:
				1-1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path),
				1-2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response) 
					如:
							另一个资源的访问路径为xiaoji
							RequestDispatcher xiaoji=request.getRequestDispatcher("/xiaoji")
							xiaoji.forward(request,response)
							通过以上两行代码就能完成转发操作(可以用链式编程,比较建议)
			2. 特点:
				2-1. 浏览器地址栏路径不发生变化
				2-2. 只能转发到当前服务器内部资源中。
				2-3. 转发是一次请求
				3. 共享数据:
			3-1. 域对象:一个有作用范围的对象,可以在范围内共享数据
			3-2. request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
			3-3. 方法:
				3-3-1. void setAttribute(String name,Object obj):存储数据
				3-3-2. Object getAttitude(String name):通过键获取值
				3-3-3. void removeAttribute(String name):通过键移除键值对
		4. 获取ServletContext:
			4-1. ServletContext getServletContext()

response

  • 功能:设置响应消息 1. 设置响应行 1. 格式:HTTP/1.1 200 ok 2. 设置状态码:setStatus(int sc) 2. 设置响应头:setHeader(String name, String value) 3. 设置响应体: * 使用步骤: 1. 获取输出流 * 字符输出流:PrintWriter getWriter() * 字节输出流:ServletOutputStream getOutputStream() 2. 使用输出流,将数据输出到客户端浏览器
字符输出流的使用
@WebServlet("/res03")
public class demo03 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	//乱码问题解决:
        //第一种写法(有不足,只能跟着浏览器编码设置,并不能让浏览器跟代码的编码走)
        //获取流对象之前,设置流的默认编码:IS0-8859-1设置为:GBK
        //response.setCharacterEncoding("utf-8");

        //第二种写法(建议)
        //告诉浏览器,服务器发送的消息.体数据的编码。建议浏览器使用该编码解码
        //response.setHeader("content-type","text/html;charset=utf-8");
        
        //第三种写法(非常建议!!!),最简单又实用
        response.setContentType("text/html;charset=utf-8");
        //1.获取字符输出流
        PrintWriter pw = response.getWriter();
        //2.输出数据
        pw.write("<h1>哈喽,response</h1>");
    }
}
字节输出流使用
@WebServlet("/res04")
public class demo04获取字节输出流 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	//防止乱码
    	response.setContentType("text/html;charset=utf-8");
        //1.获取字节输出流
        ServletOutputStream sos = response.getOutputStream();
        //2.输出数据,并将数据转为字节类型
        sos.write("你好:hello".getBytes());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request,response);
    }
}
重定向代码点击参考本人这篇博客代码即可
* 重定向的特点:redirect
		1. 地址栏发生变化
		2. 重定向可以访问其他站点(服务器)的资源
		3. 重定向是两次请求。不能使用request对象来共享数据
	* 转发的特点:forward
		1. 转发地址栏路径不变
		2. 转发只能访问当前服务器下的资源
		3. 转发是一次请求,可以使用request对象来共享数据
	
	* forward 和  redirect 区别
	* 路径写法:
		1. 路径分类
			1. 相对路径:通过相对路径不可以确定唯一资源
				* 如:./index.html
				* 不以/开头,以.开头路径

				* 规则:找到当前资源和目标资源之间的相对位置关系
					* ./:当前目录
					* ../:后退一级目录
			2. 绝对路径:通过绝对路径可以确定唯一资源
				* 如:http://localhost/day15/responseDemo2		/day15/responseDemo2
				* 以/开头的路径

				* 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
					* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
						* 建议虚拟目录动态获取:request.getContextPath()
						* <a> , <form> 重定向...
					* 给服务器使用:不需要加虚拟目录
						* 转发路径

简单验证码实现(用线条充当干扰)

成品图

在这里插入图片描述

刷新出现随机数和随机线条

在这里插入图片描述

代码实现
@WebServlet("/yzm")
public class 验证码实现 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //创建一张图片,在内存中存图片(验证码图片对象)
        int width=100;
        int height=50;
        //宽高和图片类型,使用BufferedImage里面的rgb也就是表示三原色红绿蓝格式的图片(也可以选其他的)
        BufferedImage img=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        //美化图片
        //1.填充背景色
        Graphics g = img.getGraphics();//画笔对象
        g.setColor(Color.pink);//设置画笔颜色
        g.fillRect(0,0,width,height);
        //2.画边框
        g.setColor(Color.blue);
        g.drawRect(0,0,width-1,height-1);
        //3.写验证码
        String str="ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        Random r=new Random();

        for (int i = 1; i <= 4; i++) {
            int index = r.nextInt(str.length());//随机索引
            char ch = str.charAt(index);//随机字符
            g.drawString(ch+"",width/5*i,height/2);
        }

        //4.画干扰线
        g.setColor(Color.GREEN);//颜色
        //g.drawLine(1,1,30,30);//起末点坐标点
        //十条线循环十次
        for (int i = 0; i < 10; i++) {
            //随机生成坐标点
            int x1 = r.nextInt(width);
            int x2 = r.nextInt(width);

            int y1 = r.nextInt(height);
            int y2 = r.nextInt(height);
            g.drawLine(x1,y1,x2,y2);//起末点坐标点
        }

        //将图片输出到页面展示
        //ImageIO.write可以将内存中的图片写入到页面上
        ImageIO.write(img,"jpg",resp.getOutputStream());
    }
}

ServletContext对象:

	1. 概念:代表整个web应用,可以和程序的容器(服务器)来通信
	2. 获取:
		1. 通过request对象获取
			request.getServletContext();
		2. 通过HttpServlet获取
			this.getServletContext();

以下为代码实现:
	@WebServlet("/con01")
public class demo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //ServletContext对象获取
        //1. 通过request对象获取
        //			request.getServletContext();
        ServletContext co1 = req.getServletContext();
        //2. 通过HttpServlet获取
        //			this.getServletContext();
        ServletContext co2 = this.getServletContext();
        System.out.println(co1);
        System.out.println(co2);
        System.out.println(co1==co2);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}
	3. 功能:
		1. 获取MIME类型:
			* MIME类型:在互联网通信过程中定义的一种文件数据类型
				* 格式: 大类型/小类型   text/html		image/jpeg
	
			* 获取:String getMimeType(String file)  
			
以下是代码实现:
	@WebServlet("/con02")
public class demo02获取MIME类型 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.通过httpServlet获取对象
        ServletContext servletContext = this.getServletContext();
        //2.定义文件名称'
        String filename="a.jpg";
        //获取MIME类型
        String mimeType = servletContext.getMimeType(filename);
        System.out.println(mimeType);//image/jpeg
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}
		2. 域对象:共享数据
			1. setAttribute(String name,Object value)
			2. getAttribute(String name)
			3. removeAttribute(String name)

以下是代码实现:
		页面一代码:
				@WebServlet("/con03")
public class demo03获取域对象一 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.通过httpServlet获取对象
        ServletContext servletContext = this.getServletContext();
        //2.设置数据
        servletContext.setAttribute("msg","haha");
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}
------------------------------------------------------------------------------
		页面二代码:
			@WebServlet("/con04")
public class demo03获取域对象二 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.通过httpServlet获取对象
        ServletContext servletContext = this.getServletContext();
        //2.获取数据
        Object msg = servletContext.getAttribute("msg");
        System.out.println(msg);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
}

通过访问页面二可以共享页面一存储的数据(同样所有用户都可以访问这个数据)
			* ServletContext对象范围:所有用户所有请求的数据
		3. 获取文件的真实(服务器)路径
			1. 方法:String getRealPath(String path)  
				 //1.通过httpServlet获取对象
      		     ServletContext servletContext = this.getServletContext();
				 String b = servletContext .getRealPath("/b.txt");//web目录下资源访问
		         System.out.println(b);
		
		
		        String c = servletContext .getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
		        System.out.println(c);
		
		
		        String a = servletContext .getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
		        System.out.println(a);