Web基础


一、web相关概念回顾

1.1 软件架构

  1. C/S:客户端/服务器端
  2. B/S:浏览器/服务器端

1.2 资源分类

  1. 静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源。静态资源可以直接被浏览器解析。

    如:html,css,JavaScript

  2. 动态资源:每个用户访问相同资源后,得到的结果可能不一样,称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器

    如:servlet/jsp,php,asp….

1.3 网络通信三要素

  1. IP:电子设备(计算机)在网络中的唯一标识。
  2. 端口:应用程序在计算机中的唯一标识。 0~65536
  3. 传输协议:规定了数据传输的规则。有关的基础协议有:
  • tcp:安全协议,三次握手,速度稍慢
  • udp:不安全协议,速度快

1.4 web服务器软件

1.4.1 概念

  • 服务器:安装了服务器软件的计算机
  • 服务器软件:接收用户的请求,处理请求,做出响应
  • web服务器软件:接收用户的请求,处理请求,做出响应。
    • web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目
    • 也称web容器

1.4.2 常见的Java相关的web服务器软件

  • webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的;
  • webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的;
  • JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的;
  • Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。

[注]JavaEE

Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范。

二、Tomcat:web服务器软件

2.1 下载、安装与卸载

(1)下载:http://tomcat.apache.org/

(2)安装:解压压缩包即可。注意:安装目录建议不要有中文和空格

(3)卸载:删除目录就行了

2.2 启动与关闭

(1)启动:

  1. bin/startup.bat ,双击运行该文件即可
  2. 访问:浏览器输入:

(2)关闭:

  1. 正常关闭:
    • bin/shutdown.bat
    • ctrl+c
  2. 强制关闭:
  • 点击启动窗口的×

2.3 配置

(1)直接将项目放到webapps目录下即可。

  • /hello:项目的访问路径–>虚拟目录

简化部署:将项目打成一个war包,再将war包放置到webapps目录下,war包会自动解压缩。

image-20220808105253349

(2)配置conf/server.xml文件

<Host>标签体中配置<Context docBase="D:\hello" path="/hehe" />

  • docBase:项目存放的路径
  • path:虚拟目录

(3)在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写<Context docBase="D:\hello" />

  • 虚拟目录:xml文件的名称

2.4 静态项目和动态项目

Java动态项目的目录结构:

  • 项目的根目录
  • WEB-INF目录:
    • web.xmlweb项目的核心配置文件
    • classes目录:放置字节码文件的目录
    • lib目录:放置依赖的jar

2.5 JavaEE项目部署

Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。

image-20220808123459525

image-20220808123517632

【注】

  1. IDEA会为每一个tomcat部署的项目单独建立一份配置文件
  • 查看控制台的log:Using CATALINA_BASE: “C:\Users\fqy.IntelliJIdea2018.1\system\tomcat_itcast”
  1. 工作空间项目和tomcat部署的web项目:
  • tomcat真正访问的是“tomcat部署的web项目”,”tomcat部署的web项目”对应着”工作空间项目” 的web目录下的所有资源
  • WEB-INF目录下的资源不能被浏览器直接访问
  1. 断点调试:使用”小虫子”启动debug

参考文章

https://blog.csdn.net/weixin_44797182/article/details/124745744?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165993233916781818736284%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165993233916781818736284&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-124745744-null-null.142^v39^pc_rank_v37,185^v2^control&utm_term=idea%20%E6%96%B0%E5%BB%BAjavaweb&spm=1018.2226.3001.4187

三、Servlet

3.1 概念

server applet,运行在服务器端的小程序

Servlet就是一个接口定义了Java类被浏览器访问到(tomcat识别)的规则。将来我们自定义一个类,实现Servlet接口,复写方法。

image-20220808171520362

3.2 配置步骤

  1. 创建JavaEE项目
  2. 定义一个类,实现Servlet接口
  • public class ServletDemo1 implements Servlet
  1. 实现接口中的抽象方法
  2. 配置Servlet
<?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>ServletDemo1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
</web-app>

当访问http://localhost:8899/web/demo1时,执行:

image-20220808173023676

3.3 Servlet执行原理

  1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
  3. 如果有,则在找到对应的<servlet-class>全类名
  4. tomcat将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

image-20220808173239397

3.4 生命周期方法

(1)被创建:执行init方法,只执行一次。Servlet被创建的时机是:

  • 默认情况下,第一次被访问时,Servlet被创建
  • 可以配置执行Servlet的创建时机。在<servlet>标签下配置:
    1. 第一次被访问时,创建,<load-on-startup>的值为负数
    2. 在服务器启动时,创建,<load-on-startup>的值为0或正整数
  • Servletinit方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的

    • 多个用户同时访问时,可能存在线程安全问题;

    • 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值。

(2)提供服务:执行service方法,执行多次。每次访问Servlet时,Service方法都会被调用一次。

(3)被销毁:执行destroy方法,只执行一次

  • Servlet被销毁时执行。服务器关闭时,Servlet被销毁
  • 只有服务器正常关闭时,才会执行destroy方法。
  • destroy方法在Servlet被销毁之前执行,一般用于释放资源

3.5 Servlet 3.0

Servlet 3.0支持注解配置,可以不需要web.xml

步骤:

  1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
  2. 定义一个类,实现Servlet接口
  3. 复写方法
  4. 在类上使用@WebServlet注解,进行配置
    • @WebServlet("资源路径")[访问资源路径urlPatterns]

3.6 Servlet体系结构

Servlet – 接口

GenericServlet – 抽象类

  • Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
  • 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可

HttpServlet – 抽象类,对http协议的一种封装,简化操作【经常使用】

  • 定义类继承HttpServlet
  • 复写doGet/doPost方法

image-20220808214832351

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("/demo3")
public class ServletDemo3 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...");
    }
}

3.7 Servlet相关配置

urlparttenServlet访问路径

【注】

  • 一个Servlet可以定义多个访问路径@WebServlet({"/d4","/dd4","/ddd4"})

  • 路径定义规则

    • /xxx:路径匹配

    • /xxx/xxx多层路径,目录结构

    • *.do扩展名匹配—>浏览器输入localhost/demo4.do,可以访问到(前面不能输入/

四、HTTP请求消息

4.1 概念及特点

Hyper Text Transfer Protocol 超文本传输协议

传输协议:定义了,客户端和服务器端通信时,发送数据的格式

特点:

  1. 基于TCP/IP的高级协议
  2. 默认端口号:80
  3. 基于请求/响应模型的:一次请求对应一次响应
  4. 无状态的:每次请求之间相互独立,不能交互数据

历史版本:

  • 1.0:每一次请求响应都会建立新的连接

  • 1.1:复用连接

image-20220808221413750

4.2 请求消息数据格式

4.2.1 请求行

格式:请求方式 请求url 请求协议/版本

GET /login.html HTTP/1.1

HTTP协议有7种请求方式,常用的有2种:

[1]**GET**:

  1. 请求参数在请求行中,在url
  2. 请求的url长度有限制的
  3. 不太安全

[2]**POST**:

  1. 请求参数在请求体中【参数位置与GET不一样】
  2. 请求的url长度没有限制的
  3. 相对安全

4.2.2 请求头

请求头用于浏览器告诉服务器一些信息

格式:请求头名称: 请求头值

常见的请求头:

  1. **User-Agent**:浏览器告诉服务器,我访问你使用的浏览器版本信息

​ 可以在服务器端获取该头的信息,解决浏览器的兼容性问题

  1. **Referer**:http://localhost/login.html
  • 告诉服务器,我(当前请求)从哪里来?
  • 作用:防盗链;统计工作

image-20220808224519479

4.2.3 请求空行

空行,用于分割POST请求的请求头和请求体。

4.2.4 请求体(正文)

请求体封装POST请求消息的请求参数。【GET无】

4.3 字符串格式

POST /login.html	HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive	[复用]
Upgrade-Insecure-Requests: 1

username=zhangsan	[请求体]

五、Request

5.1 request对象和response对象的原理

image-20220808230901807

【注】

  1. requestresponse对象是由服务器创建的。我们来使用它们
  2. request对象是来获取请求消息response对象是来设置响应消息

5.2 request对象继承体系结构

ServletRequest – 接口
| 继承
HttpServletRequest – 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)

5.3 request获取请求消息数据功能

5.3.1 获取请求行数据

GET /day14/demo1?name=zhangsan HTTP/1.1

方法:

  1. 获取请求方式 :GET
  • String getMethod()
  1. **(*)**获取虚拟目录:/day14
  • String getContextPath()
  1. 获取Servlet路径: /demo1
  • String getServletPath()
  1. 获取get方式请求参数:name=zhangsan
  • String getQueryString()
  1. **(*)**获取请求URI/day14/demo1
  1. 获取协议及版本:HTTP/1.1
  • String getProtocol()
  1. 获取客户机的IP地址:
  • String getRemoteAddr()
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/RequestDemo")
public class RequestDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取请求方式: GET
        String method = request.getMethod();
        System.out.println(method);
        //2.(*)获取虚拟目录: /day14
        String contextPath = request.getContextPath();
        System.out.println(contextPath);
        //3. 获取Servlet路径: /demo1
        String servletPath = request.getServletPath();
        System.out.println(servletPath);
        //4. 获取get方式请求参数:
        String queryString = request.getQueryString();
        System.out.println(queryString);
        //5.(*)获取请求URI: /day14/demo1
        String requestURI = request.getRequestURI();
        StringBuffer requestURL = request.getRequestURL();
        System.out.println(requestURI);
        System.out.println(requestURL);
        //6. 获取协议及版本: HTTP/1.1
        String protocol = request.getProtocol();
        System.out.println(protocol);
        //7. 获取客户机的IP地址:
        String remoteAddr = request.getRemoteAddr();
        System.out.println(remoteAddr);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

image-20220808233526032

5.3.2 获取请求头数据

方法:

  • (*)String getHeader(String name):通过请求头的名称获取请求头的值
  • Enumeration<String> getHeaderNames():获取所有的请求头名称【类似迭代器】

[例1]

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;

@WebServlet("/requestDemo2")
public class RequestDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //演示获取请求头数据  
        //1.获取所有请求头名称
        Enumeration<String> headerNames = request.getHeaderNames();
        //2.遍历
        while(headerNames.hasMoreElements()){
            String name = headerNames.nextElement();
            //根据名称获取请求头的值
            String value = request.getHeader(name);
            System.out.println(name+"---"+value);
        }
    }
}

[例2]

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;

@WebServlet("/requestDemo3")
public class RequestDemo3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //演示获取请求头数据:user-agent
        String agent = request.getHeader("user-agent");
        //判断agent的浏览器版本
        if(agent.contains("Chrome")){
            //谷歌
            System.out.println("谷歌来了...");
        }else if(agent.contains("Firefox")){
            //火狐
            System.out.println("火狐来了...");
        }
    }
}

[例3]

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.io.IOException;

@WebServlet("/requestDemo4")
public class RequestDemo4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //演示获取请求头数据:referer

        String referer = request.getHeader("referer");
        System.out.println(referer);//http://localhost/day14/login.html

        //防盗链
        if(referer != null ){
            if(referer.contains("/day14")){
                //正常访问
               // System.out.println("播放电影....");
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("播放电影....");
            }else{
                //盗链
                //System.out.println("想看电影吗?来优酷吧...");
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("想看电影吗?来优酷吧...");
            }
        }
    }
}

5.3.3 获取请求体数据

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数

步骤:

  1. 获取流对象
  • BufferedReader getReader():获取字符输入流,只能操作字符数据
  • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据【在文件上传知识点后讲解】
  1. 再从流对象中拿数据
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("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求消息体--请求参数

        //1.获取字符流
        BufferedReader br = request.getReader();
        //2.读取数据
        String line = null;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
}

5.4 request其他功能

5.4.1 获取请求参数

获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数

  • String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
  • String[] getParameterValues(String name):根据参数名称获取参数值的数组【多用于复选框】 hobby=xx&hobby=game
  • Enumeration<String> getParameterNames():获取所有请求的参数名称
  • Map<String,String[]> getParameterMap():获取所有参数的map集合
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.util.Enumeration;
import java.util.Map;
import java.util.Set;

@WebServlet("/requestDemo6")
public class RequestDemo6 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post 获取请求参数
        //根据参数名称获取参数值
        String username = request.getParameter("username");
      
        //根据参数名称获取参数值的数组
        String[] hobbies = request.getParameterValues("hobby");
        
        //获取所有请求的参数名称
        Enumeration<String> parameterNames = request.getParameterNames();

        // 获取所有参数的map集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        
        //遍历
        Set<String> keyset = parameterMap.keySet();
        for (String name : keyset) {            
            //获取键获取值
            String[] values = parameterMap.get(name);
            System.out.println(name);
            for (String value : values) {
                System.out.println(value);
            }
            System.out.println("-----------------");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get 获取请求参数
        this.doPost(request,response);
    }
}

[注]中文乱码问题

  • get方式:tomcat 8 已经将get方式乱码问题解决了
  • post方式:会乱码
    • 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");

5.4.2 请求转发

一种在服务器内部的资源跳转方式。

image-20220809121511246

步骤:

  • 通过request对象获取请求转发器对象RequestDispatcher getRequestDispatcher(String path)

  • 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)

特点:

  • 浏览器地址栏路径不发生变化

  • 只能转发到当前服务器内部资源中

  • 转发是一次请求

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/RequestDemo")
public class RequestDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo8888被访问了。。。");
        //转发到demo1资源
        request.getRequestDispatcher("/RequestDemo1").forward(request,response);
    }

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

image-20220809122505641

5.4.3 共享数据

  • 域对象:一个有作用范围的对象,可以在范围内共享数据
  • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
  • 方法:request.xx
  1. void setAttribute(String name,Object obj):存储数据
  2. Object getAttitude(String name):通过键获取值
  3. void removeAttribute(String name):通过键移除键值对

5.4.4 获取ServletContext

  • ServletContext getServletContext()

ServletContext对象的功能将在下节讲解。

六、案例:用户登录

6.1 用户登录案例需求

1.编写login.html登录页面 username & password 两个输入框

2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表

3.使用JdbcTemplate技术封装JDBC

4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您

5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

image-20220809165727080

6.2 开发步骤

6.2.1 创建项目和数据库环境

  1. 创建项目,导入html页面,配置文件,jar

    注意:

    • 需使用maven框架创建项目!
    • 项目结构如下,需注意druid.properties配置文件需放在resources文件夹下。

    image-20220810004930906

  2. 创建数据库环境

image-20220809172709396

6.2.2 创建类User

创建类User,并提供gettersetter方法

/**
 * 用户实体类
 */
public class User {
    private int id;
    private String username;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

6.2.3 编写JDBC工具类

使用Durid连接池。

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 * JDBC工具类,使用Druid连接池
 */
public class JDBCUtils {
    private static DataSource ds;

    static{
        try {
            //1.加载配置文件
            Properties pro = new Properties();
            //使用ClassLoader加载配置文件,获取字节输入流
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            pro.load(is);
            //2.初始化连接池对象
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //获取连接池对象
    public static DataSource getDataSource(){
        return ds;
    }

    //获取连接Connection对象
    public static Connection getConnection() throws Exception{
        return ds.getConnection();
    }
}

6.2.4 创建UserLogin类,提供login方法

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserLogin {
    //声明JDBCTemplate对象共用
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    /**
     * 登录方法
     */
    public User login(User loginUser){
        try {
            //1.编写sql
            String sql = "select * from user where username = ? and password = ?";
            //2.调用query方法
            User user = template.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),
                    loginUser.getUsername(),loginUser.getPassword());
            return user;
        } catch (DataAccessException e) {
            e.printStackTrace();    //记录日志
            return null;
        }
    }
}

6.2.5 编写LoginServlet类

import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/loginServlet")
public class Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        //2.获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //3.封装user对象
        User loginUser = new User();
        loginUser.setUsername(username);
        loginUser.setPassword(password);
        //4.调用UserLogin的login方法
        UserLogin userLogin = new UserLogin();
        User user = userLogin.login(loginUser);     //查询到的user
        //5.判断user
        if(user == null)
        {
            //登录失败
            request.getRequestDispatcher("/failServlet").forward(request,response);
        }else
        {
            //登录成功
            request.setAttribute("user",user);     //存储数据,键为"user",值为user对象
            request.getRequestDispatcher("/successServlet").forward(request,response);
        }
    }

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

6.2.6 编写FailServlet和SuccessServlet类

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("/successServlet")
public class SuccessServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取request域中共享的user对象
        User user = (User)request.getAttribute("user");
        //给页面写一句话
        if(user != null)    //防止空指针异常
        {
            //设置编码
            response.setContentType("text/html;charset=utf-8");
            //输出
            response.getWriter().write("登录成功!"+user.getUsername()+"欢迎您");
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request,response);
    }
}
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //给页面写一句话
        //设置编码
        response.setContentType("text/html;charset=utf-8");
        //输出
        response.getWriter().write("登录失败!用户名或密码错误");
    }

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

【备注】login.html中form表单的action路径的写法:虚拟目录+Servlet的资源路径

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/loginServlet" method="post">
        用户名:<input type="text" name="username"> <br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

6.2.7 演示

输入正确:

image-20220810005553417

输入错误:

image-20220810005533641

6.2.8 改进:BeanUtils

BeanUtils工具类,简化数据封装,用于封装JavaBean的。

[1]JavaBean是标准的Java类,可以用于封装数据。其需要满足:

  • 类必须被public修饰
  • 必须提供空参的构造器
  • 成员变量必须使用private修饰
  • 提供公共的settergetter方法

[2]区分:

  • 成员变量

  • 属性:settergetter方法截取后的产物

    • 例如:getUsername() –> Username –> username

[3]方法:

  • setProperty()

    image-20220810115832045

    操作的是属性

  • getProperty()

  • populate(Object obj , Map map):将map集合的键值对信息,封装到对应的JavaBean对象中

    • 把键作为属性的名称,把值作为JavaBean对应属性的值
//2.获取请求参数,map右侧是字符串类型的数组,以解决表单中有多个name值一样的项
Map<String,String[]> map = request.getParameterMap();
//3.创建User对象
User loginUser = new User();
try {
    BeanUtils.populate(loginUser,map);      //应选择apache的
} catch (IllegalAccessException e) {
    throw new RuntimeException(e);
} catch (InvocationTargetException e) {
    throw new RuntimeException(e);
}

七、HTTP响应消息

7.1 概述

[1]请求消息:客户端发送给服务器端的数据

数据格式:

  1. 请求行
  2. 请求头
  3. 请求空行
  4. 请求体

[2]响应消息:服务器端发送给客户端的数据

7.2 响应消息数据格式

7.2.1 响应行

格式:协议/版本 响应状态码 状态码描述

响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。

下面介绍响应状态码。

状态码都是3位数字,其分类有:

  • 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码

  • 2xx:成功。代表:200

  • 3xx重定向。代表:302(重定向),304(访问缓存)

  • 4xx:客户端错误。代表有:

    • 404:请求路径没有对应的资源
    • 405:请求方式没有对应的doXXX方法
  • 5xx:服务器端错误。代表:500(服务器内部出现异常)

重定向示意:

重定向

7.2.2 响应头

格式:头名称 : 值

常见的响应头:

  1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
    • Content-Type: text/html;charset=UTF-8
  2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据。值:
    • in-line:默认值,在当前页面内打开
    • attachment;filename=xxx:以附件形式打开响应体。文件下载

7.2.3 响应空行和响应体

  1. 响应空行
  2. 响应体:传输的数据

响应字符串格式

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT

<html>
	<head>
		<title>$Title$</title>
	</head>
	<body>
		hello , response
	</body>
</html>

7.3 Response对象

功能:设置响应消息

​ 1. 设置响应行

  • 格式:HTTP/1.1 200 ok
  • 设置状态码:setStatus(int sc)
  1. 设置响应头:setHeader(String name, String value)

  2. 设置响应体:使用步骤是:

    1. 获取输出流

      • 字符输出流PrintWriter getWriter()

      • 字节输出流ServletOutputStream getOutputStream()

    2. 使用输出流,将数据输出到客户端浏览器

7.4 路径

路径分类:

  1. 相对路径:通过相对路径不可以确定唯一资源

    • 如:./index.html

    • 不以/开头,以.开头路径

    • 规则:找到当前资源和目标资源之间的相对位置关系

      • ./:当前目录
      • ../:后退一级目录
  2. 绝对路径:通过绝对路径可以确定唯一资源

  • 如:http://localhost/day15/responseDemo2 /day15/responseDemo2

  • /开头的路径

  • 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出

    • 客户端浏览器使用:需要加虚拟目录(项目的访问路径)
      • 建议虚拟目录动态获取:request.getContextPath(),然后进行字符串拼接
      • <a><form>,重定向…
    • 服务器使用:不需要加虚拟目录
      • 转发路径

7.5 案例解析

7.5.1 完成重定向

重定向是资源跳转的方式

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo1......");
        //访问/responseDemo1,会自动跳转/responseDemo2资源
        //1. 访问状态码为302
        //response.setStatus(302);
        //2. 设置响应头location
        //response.setHeader("location","/responseDemo2");
        //简单的重定向方法
        response.sendRedirect("/responseDemo2");
    }

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

redirectforward 的区别:

  • 重定向的特点:redirect

    1. 地址栏发生变化
    2. 重定向可以访问其他站点(服务器)的资源
    3. 重定向是两次请求。不能使用request对象来共享数据
  • 转发的特点:forward

    1. 转发地址栏路径不变
    2. 转发只能访问当前服务器下的资源
    3. 转发是一次请求,可以使用request对象来共享数据

7.5.2 输出字符数据

服务器输出字符数据到浏览器的步骤:

  1. 获取字符输出流
  2. 输出数据

注:乱码问题:

  1. PrintWriter pw = response.getWriter(); 获取的流的默认编码是ISO-8859-1
  2. 设置该流的默认编码
  3. 告诉浏览器响应体使用的编码
//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取字符输出流
        PrintWriter pw = response.getWriter();
        //2. 输出数据
        pw.write("<h1>hello response</h1>");
    }

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

image-20220811213819185

7.5.3 输出字节数据

服务器输出字节数据到浏览器

步骤:

  1. 获取字节输出流
  2. 输出数据
response.setContentType("text/html;charset=utf-8");
//1.获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//2.输出数据
sos.write("你好".getBytes("utf-8"));

7.5.4 验证码

  1. 本质:图片
  2. 目的:防止恶意表单注册

验证码书写

import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


        int width = 100;
        int height = 50;

        //1.创建一对象,在内存中图片(验证码图片对象)
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


        //2.美化图片
        //2.1 填充背景色
        Graphics g = image.getGraphics();//画笔对象
        g.setColor(Color.PINK);//设置画笔颜色
        g.fillRect(0,0,width,height);

        //2.2画边框
        g.setColor(Color.BLUE);
        g.drawRect(0,0,width - 1,height - 1);

        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
        //生成随机角标
        Random ran = new Random();

        for (int i = 1; i <= 4; i++) {
            int index = ran.nextInt(str.length());
            //获取字符
            char ch = str.charAt(index);//随机字符
            //2.3写验证码
            g.drawString(ch+"",width/5*i,height/2);
        }


        //2.4画干扰线
        g.setColor(Color.GREEN);

        //随机生成坐标点

        for (int i = 0; i < 10; i++) {
            int x1 = ran.nextInt(width);
            int x2 = ran.nextInt(width);

            int y1 = ran.nextInt(height);
            int y2 = ran.nextInt(height);
            g.drawLine(x1,y1,x2,y2);
        }


        //3.将图片输出到页面展示
        ImageIO.write(image,"jpg",response.getOutputStream());


    }

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

验证码切换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        //给超链接和图片绑定单击事件,并重新设置图片的src属性值
        window.onload = function (){
            //获取图片对象
            var img = document.getElementById("checkCode");
            //绑定单击事件
            img.onclick = function (){
                //加时间戳
                var date = new Date().getTime();
                img.src = "/checkCodeServlet?" + date;
            }
            var img2 = document.getElementById("change");
            //绑定单击事件
            img2.onclick = function (){
                //加时间戳
                var date = new Date().getTime();
                img2.src = "/checkCodeServlet?" + date;
            }
        }
    </script>
</head>
<body>
    <img id="checkCode" src="/checkCodeServlet" />
    <a id="change" href="">看不清?换一张</a>
</form>
</body>
</html>

八、ServletContext对象

8.1 概念

代表整个web应用,可以和**程序的容器(服务器)**来通信

8.2 获取

  1. 通过request对象获取 request.getServletContext();

  2. 通过HttpServlet获取 this.getServletContext();

8.3 功能

[1] 获取MIME类型

  • MIME类型:在互联网通信过程中定义的一种文件数据类型
    • 格式: 大类型/小类型 text/html,image/jpeg
    • 获取:String getMimeType(String file)

[2]域对象:共享数据【非必要不用】

  • ServletContext对象范围:所有用户所有请求的数据

    1. setAttribute(String name,Object value)
    2. getAttribute(String name)
    3. removeAttribute(String name)

[3]获取文件的真实(服务器)路径

方法:String getRealPath(String path)

String b = context.getRealPath("/b.txt");					//web目录下资源访问
System.out.println(b);

String c = context.getRealPath("/WEB-INF/c.txt");			//WEB-INF目录下的资源访问
System.out.println(c);

String a = context.getRealPath("/WEB-INF/classes/a.txt");	//src目录下的资源访问
System.out.println(a);

8.4 案例:文件下载

文件下载需求:

  1. 页面显示超链接
  2. 点击超链接后弹出下载提示框
  3. 完成图片文件下载

分析:

1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
1. 任何资源都必须弹出下载提示框
  1. 使用响应头设置资源的打开方式:content-disposition:attachment;filename=xxx

步骤:

  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename

  2. 定义Servlet

  • 获取文件名称
  • 使用字节输入流加载文件进内存
  • 指定response的响应头: content-disposition:attachment;filename=xxx
  1. 将数据写出到response输出流

download.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>download</title>
</head>
<body>
    <a href="/img/2.jpg">图片1</a>
    <a href="img/1.avi">视频</a>
    <hr>
    <a href="/downloadServlet?filename=2.jpg">图片1</a>
    <a href="/downloadServlet?filename=1.avi">视频</a>
</body>
</html>

DownloadServlet.java

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取请求参数,文件名称
        String filename = request.getParameter("filename");
        //2. 使用字节输入流加载文件进内存
        //2.1 找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/"+filename);
        //2.2 用字节流关联
        FileInputStream fis = new FileInputStream(realPath);
        //3. 设置response的响应头
        //3.1 设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename);     //获取文件的mime类型
        response.setHeader("content-type",mimeType);
        //3.2 设置响应头打开方式:content-disposition
        response.setHeader("content-disposition","attachment;filename="+filename);
        //4. 将输入流的数据写出到输出流中
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024*8];
        int len = 0;
        while((len = fis.read(buff)) != -1)
        {
            sos.write(buff,0,len);
        }
        fis.close();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

问题:中文文件问题的解决思路:

  1. 获取客户端使用的浏览器版本信息

  2. 根据不同的版本信息,设置filename的编码方式不同(URL,BASE64等)

//1.获取user-agent请求头(获取浏览器版本信息)
String agent = request.getHeader("user-agent");
//2.使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);

九、会话技术概述

  1. 会话:一次会话中包含多次请求和响应
    • 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
  2. 功能:在一次会话的范围内的多次请求间,共享数据
  3. 方式:
    • 客户端会话技术:Cookie
    • 服务器端会话技术:Session

十、Cookie

10.1 Cookie定义

客户端会话技术,将数据保存到客户端

10.2 Cookie使用步骤

  1. 创建Cookie对象,绑定数据
  • new Cookie(String name, String value)
  1. 发送Cookie对象
  • response.addCookie(Cookie cookie)
  1. 获取Cookie,拿到数据
  • Cookie[] request.getCookies()
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/cookieDemo1")
public class CookieDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 创建Cookie对象
        Cookie c = new Cookie("msg","hello");
        //2. 发送Cookie
        response.addCookie(c);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/cookieDemo2")
public class CookieDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //3. 获取Cookie
        Cookie[] cs = request.getCookies();
        //获取数据,遍历Cookies
        if(cs != null)
        {
            for(Cookie c : cs)
            {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name+":"+value);
            }
        }
    }

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

10.3 实现原理

基于响应头set-cookie请求头cookie实现。

image-20220812223918896

10.4 Cookie使用细节

10.4.1 发送多个Cookie

可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。

10.4.2 Cookie存活时间

  1. 默认情况下,当浏览器关闭后,Cookie数据被销毁
  2. 持久化存储setMaxAge(int seconds)
  • 正数:将Cookie数据写到硬盘的文件中。持久化存储,并指定cookie存活时间;时间到后,cookie文件自动失效

  • 负数:默认值

  • 零:删除cookie信息


文章作者: ShiQuLiZhi
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ShiQuLiZhi !
评论
  目录