一、Servlet

概念 、步骤 、执行原理 、生命周期、 Servlet3.0 注解配置这些内容在上一篇文章Servlet入门介绍过了
下面继续介绍Servlet

1.1 、Servlet的体系结构

Servlet 接口
GenericServlet 抽象类 实现了Servlet接口
HttpServlet 抽象类 继承了GenericServlet

GenericServlet:

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

package cn.kinggm520.web;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;


@WebServlet("/demo2")
public class Demo_2 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("继承GenericServlet");
    }
}

1.2、HttpServlet:对http协议的一种封装,简化操作

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

例:

package cn.kinggm520.web;

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 Demo_3_HttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("do get");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("do post");
    }


}

在浏览器输入localhost:8080/demo3
控制台会打印 do get
注意:浏览器直接访问为get方式
可以使用表单的post 方式验证doPost方法

<form action="/demo3" method="post">
   <input type="text" name="uerName" >
     <input type="submit" value="提交" >

点击提交按钮 则控制台会打印 do post

1.3、Servlet相关配置

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

package cn.kinggm520.web;

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({"/d4","/dd4","/ddd4"})
public class Demo_4_HttpServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo4...");
    }
}

路径定义规则
*是通配符
/xxx:路径匹配
/xxx/xxx:多层路径,目录结构
*.do:扩展名匹配

二、HTTP

2.1、HTTP简介

概念:Hyper Text Transfer Protocol 超文本传输协议

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

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

历史版本:
1.0:每一次请求响应都会建立新的连接
1.1:复用连接

请求消息数据格式
请求行
格式: 请求方式 请求url 请求协议/版本
GET /index.html HTTP/1.1
请求头:
格式: key:value
请求体:
只有表单POST方式提交才有请求体,封装了表单提交的数据.

常见的请求头:
User-Agent: 浏览器内核信息. 后期做文件下载案例需要
Referer: 浏览器通知服务器当前请求来自哪
作用: 防盗链/访问统计.
注意:
浏览器地址栏直接去访问,Referer没有值 为 null
从其他页面过来的请求,才有Referer值

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

GET
请求参数在请求行中,在url后。
请求的url长度有限制
不安全

POST
请求参数在请求体中
请求的url长度没有限制
相对安全

请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值
常见的请求头:
User-Agent:浏览器告诉服务器,当前使用的浏览器版本信息
可以在服务器端获取该头的信息,解决浏览器的兼容性问题

Referer:http://localhost/login.html
告诉服务器,当前请求从哪里来?
作用:
防止别人盗取链接
做统计工作

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

请求体(正文)
封装POST请求消息的请求参数

字符串格式
请求行
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:

3.1、 request对象和response对象的原理

request和response对象是由服务器创建的。我们来使用它们
request对象是来获取请求消息,response对象是来设置响应消息
原理图:
九、Servlet&HTTP&Request_数据
request对象继承体系结构
ServletRequest 接口

HttpServletRequest 接口 继承 ServletRequest

org.apache.catalina.connector.RequestFacade 类 实现HttpServletRequest (tomcat服务器写的)

3.2、 request功能

3.2.1、获取请求消息数据

1.获取请求行数据
GET /Demo/demo1?name=zhangsan HTTP/1.1
方法
获取请求方式 :GET
String getMethod()

获取虚拟目录:/Demo (重要方法)
String getContextPath()

获取Servlet路径: /demo1
String getServletPath()

获取get方式请求参数:name=zhangsan
String getQueryString()

获取请求URI:/Demo/demo1 (重要方法)
String getRequestURI(): 返回 /Demo/requestDemo1
StringBuffer getRequestURL() : 返回 http://localhost/Demo/requestDemo1

URL:统一资源定位符: (范围更精确) http://localhost/Demo/demo1 中华田园犬
URI:统一资源标识符:(范围更大) /Demo/demo1 狗
关于二者具体的区别可以 看这篇文章

获取协议及版本:HTTP/1.1
String getProtocol()

获取客户机的IP地址:
String getRemoteAddr()
代码示例

package cn.kinggm520.web;

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("/requestDemo1")
public class RequestDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//      1、获取请求方式GET
        String method = request.getMethod();
        System.out.println("1、"+method);

//        2、获取虚拟目录  重要
        String contextPath = request.getContextPath();
        System.out.println("2、"+contextPath);

//        3、获取Servlet路径
        String servletPath = request.getServletPath();
        System.out.println("3、"+servletPath);
//        4、获取请求参数
        String queryString=request.getQueryString();
        System.out.println("4、"+queryString);

//        5、获取请求的URI  重要
        String requestURI =request.getRequestURI();
        StringBuffer requestURL =request.getRequestURL();
        System.out.println("5.1、"+requestURI);
        System.out.println("5.2、"+requestURL);


//        6、获取协议及版本 HTTP/1.1
        String protocol = request.getProtocol();
        System.out.println("6、"+protocol);

//        7、获取客户机的IP地址
        String remoteAddr = request.getRemoteAddr();
        System.out.println("7、"+remoteAddr);

    }
}

先配置IDEA tomcat服务器的deployment 将虚拟目录设置为/Demo
九、Servlet&HTTP&Request_java_02

打开服务器 在浏览器地址栏输入 http://localhost:8080/Demo/requestDemo1?name=hh

控制台输出结果:
1、GET
2、/Demo
3、/requestDemo1
4、name=hh
5.1、/Demo/requestDemo1
5.2、http://localhost:8080/Demo/requestDemo1
6、HTTP/1.1
7、0:0:0:0:0:0:0:1

2.获取请求头数据
方法:
String getHeader(String name): 通过请求头的名称获取请求头的值
Enumeration getHeaderNames(): 获取所有的请求头名称
实例代码:

package cn.kinggm520.web;

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();
//        3、根据名称获取请求头的值
            String value = request.getHeader(name);
            System.out.println(name+"---"+value);
        }


    }
}

控制台打印结果:
host—localhost:8080
connection—keep-alive
upgrade-insecure-requests—1
user-agent—Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
accept—text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3
sec-fetch-site—none
sec-fetch-mode—navigate
accept-encoding—gzip, deflate, br
accept-language—zh-CN,zh;q=0.9
cookie—JSESSIONID=AA40DCB39EF4959C953725F2177C955E; Idea-fda92745=313c0429-dcc1-43d4-b1b9-a39cdbe3bd4b; user=kinggm-123456

常用的方法是这个 String getHeader(String name): 通过请求头的名称获取请求头的值
代码示例:(获取版本信息 用于以后做浏览器兼容)

package cn.kinggm520.web;

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 {

        String agent = request.getHeader("user-agent"); //获取版本信息 用于以后做浏览器兼容
        if(agent.contains("Chrome")){
            System.out.println("谷歌浏览器");
        }else if(agent.contains("Firefox")){
            System.out.println("火狐浏览器");
        }
    }
}


IDEA打开Tomcat服务器
控制台打印结果:
使用谷歌浏览器 访问localhost:8080/Demo/requestDemo3
打印 谷歌浏览器
使用火狐浏览器 访问localhost:8080/Demo/requestDemo3
打印 火狐浏览器

代码示例:(获取referer 用于防盗链)

package cn.kinggm520.web;

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 {
        String referer= request.getHeader("referer");
        System.out.println(referer);

    }
}

在index.jsp页面写个< a>标签
< a href="/Demo/requestDemo4">Demo4</ a>
如果直接使用浏览器访问 localhost:8080/Demo/requestDemo4
控制台打印为 null

只有从别的页面间接访问才能够获取referer
当使用超链接访问时 控制台打印 http://localhost:8080/Demo/index.jsp

通过判断referer的内容是否包含特定内容 可以做到防盗链

//        防盗链
        if(referer.contains("/Demo")){
            System.out.println("直接访问 正常");
        }else {
            System.out.println("间接访问 盗链");
        }

3.获取请求体数据
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
步骤:
获取流对象
BufferedReader getReader():获取字符输入流,只能操作字符数据
例:(获取用户名和密码)
regist.html页面代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<form action="/Demo/requestDemo5" method="post">
    用户名:<input name="userName" type="text">
    <br>
    密码:<input type="text" name="password">

    <br>
    <input type="submit" value="提交">
</form>
</body>
</html>

RequestDemo5.java 代码

package cn.kinggm520.web;

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 {
//    获取请求消息体
        BufferedReader reader = request.getReader();
//        读取数据
        String line =null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

运行效果:
九、Servlet&HTTP&Request_数据_03

ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
在后面文章的文件上传知识点后讲解

其他功能(常用 重点):
获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
String getParameter(String name):根据参数名称获取参数值

String[] getParameterValues(String name):根据参数名称获取参数值的数组 (多用于复选框)

Enumeration getParameterNames():获取所有请求的参数名称

Map<String,String[]> getParameterMap():获取所有参数的map集合
代码示例:
第一个方法 String getParameter(String name):根据参数名称获取参数值 (常用)
regist2.html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<form action="/Demo/requestDemo6" method="get">
    用户名:<input name="userName" type="text">
    <br>
    密码:<input type="text" name="password">

    <br>
    <input type="submit" value="提交">
</form>
</body>
</html>

RequestDemo6.java

package cn.kinggm520.web;

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("/requestDemo6")
public class RequestDemo6 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//根据name属性值获取输入值
        String username = request.getParameter("userName");
        System.out.println("post:"+username);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//根据name属性值获取输入值
        String username = request.getParameter("userName");
        System.out.println("get:"+username);
    }
}

运行效果:
九、Servlet&HTTP&Request_请求头_04
String getParameter(String name) 这个方法的好处就是 无论是get还是post 获取输入数据的方式统一了
此时只要在 doGet方法内加入 doPost(request,response); 就能简化代码的编写 无论是get还是post方式都能执行一样的代码了

下面演示第二个方法
String[] getParameterValues(String name):根据参数名称获取参数值的数组 (多用于复选框)
前端代码

  <input type="checkbox" name="hobby" value="guns">枪械
    <input type="checkbox" name="hobby" value="skate">滑板

后台代码

//        根据参数获取值的数组
        String hobbies[] =request.getParameterValues("hobby");
        for (String hobby : hobbies) {
            System.out.println(hobby);
        }

效果:
九、Servlet&HTTP&Request_请求头_05
第三个方法
Enumeration getParameterNames():获取所有请求的参数名称

//        获取所有请求的参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()){
            String name = parameterNames.nextElement();
            String value = request.getParameter(name); //只能获取一个
            System.out.println(name+":"+value);

        }

效果:
九、Servlet&HTTP&Request_java_06
第四个方法
Map<String,String[]> getParameterMap():获取所有参数的map集合 (常用)

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

效果:
九、Servlet&HTTP&Request_服务器_07

中文乱码问题
get方式:tomcat 8 已经将get方式乱码问题解决
post方式:输入中文会乱码
解决:在获取参数前,设置request的编码request.setCharacterEncoding(“utf-8”);
以后在开发中 无论doGet 还是doPost 首先在方法体内第一行加下面这句代码 就能解决乱码问题
前提你的前端页面的字符集也是UTF-8格式

//设置流的编码字符集
        request.setCharacterEncoding("utf-8");

请求转发:一种在服务器内部的资源跳转方式
步骤:
通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)

特点
浏览器地址栏路径不发生变化
只能转发到当前服务器内部资源中
转发是一次请求

代码示例:

 RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestDemo8");
        requestDispatcher.forward(request,response);
 System.out.println("Demo8被访问了");

效果:
九、Servlet&HTTP&Request_数据_08

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

例:
Demo7

 //        存储数据到request域中
        request.setAttribute("msg","hello");

Demo8

//        获取数据
        Object msg = request.getAttribute("msg");   //注意返回值是一个对象
        System.out.println(msg);

执行结果:
九、Servlet&HTTP&Request_服务器_09

获取ServletContext(下篇文章再细讲)
ServletContext getServletContext()