【Java EE】-Servlet(二) Servlet API 详解

作者:学Java的冬瓜
博客主页:☀冬瓜的主页
专栏:【JavaEE】
分享:寂寞会发慌,孤独是饱满的。——史铁生《命若琴弦》

主要内容:HttpServlet的方法,init,service,destroy,doGet/doPost/doPut/doDelete…,servlet的生命周期、1>使用地址栏输入URL发送get请求,2>使用postman发送http请求,3>使用ajax发送http请求,4>使用JavaScript发送body为json格式的post请求。HttpServletRequest的相关方法的使用,前后端交互的方式,1>get请求,query string 传参,2> post请求,使用form表单传参,3> post请求,body使用json格式传参,req.getParameter(String name),req.getInputStream()。

【Java EE】-Servlet(二) Servlet API 详解_第1张图片

文章目录

  • 一、HttpServlet
    • 1、HtteServlet方法
    • 2、servlet生命周期
    • 3、服务器接收请求三种方式
      • 3.1、地址栏输入URL发送get请求
      • 3.2、通过postman发送http请求
      • 3.3、使用ajax发送http请求
      • 3.4、使用JavaScript构造body为json格式的post请求
  • 二、HttpServletRequest
    • 1、HttpServletRequest方法
    • 2、方法的使用
    • 3、前后端交互方式(重点)
      • 3.1、GET请求,query string传参
      • 3.2、POST请求,使用form表单
      • 3.3、POST请求,body使用json格式(重点)
      • 3.4、小结:前后端数据交互
  • 三、HttpServletResponse

一、HttpServlet

1、HtteServlet方法

HttpServlet方法 调用时机
init 在HttpServlet被实例化之后被调用一次
destroy 在HttpServlet不再使用的时候调用一次,具体的不使用时机下面会讨论
service 收到Http请求的时候调用
doGet 收到 Get请求的时候,由service方法调用
doPost 收到Post请求的时候,由service调用
doPut / doDelete / doOptions… 收到对应方法的时候,由service方法对应调用
  • init方法使用类似 饿汉模式的方法,是在首次请求时才执行,而不是服务器启动时执行,且只执行这一次。
  • destroy方法执行时机:是在servlet生命周期结束时调用 destroy方法。有的版本直接关闭服务器是来不及执行 destroy方法的,是直接杀进程,不能执行到destroy方法;而有些版本则是使用管理端口8005再做一些事情才关闭服务器,这种就可以执行到destroy方法。由于destroy执行的时机不好把握,所以不推荐使用destroy方法。
  • service,每次请求都会调用service方法,再通过service方法判断是 get/post/…方法,然后调用请求对应的方法。

2、servlet生命周期

1> 首次请求时,创建servlet,执行init方法。在第一次访问时,servlet容器中会创建对应的servlet类的实例,每个servlet类只有一个实例,但一个servlet容器可以包含多个servlet实例对应多个servlet类。
2> 每次收到请求,都会执行 service方法,通过service方法去调用请求对应的方法。
3> 在servlet销毁前,调用destroy方法。

注意:一个servlet程序可以包含多个servlet,单个的servlet的生死并不影响整个servlet程序。

3、服务器接收请求三种方式

以下三种方式复用这同一份服务器端的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;

@WebServlet("/method")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Get");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Post");
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Delete");
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("Put");
    }

}

3.1、地址栏输入URL发送get请求

  • 注意:地址栏输入URL,按回车,只能发送get请求。
    【Java EE】-Servlet(二) Servlet API 详解_第2张图片

3.2、通过postman发送http请求

  • 注意:使用postman发送请求,可以发送get、post、put、delete等各种http请求。
    【Java EE】-Servlet(二) Servlet API 详解_第3张图片
    【Java EE】-Servlet(二) Servlet API 详解_第4张图片

3.3、使用ajax发送http请求

前端不传数据给后端:此时Content-Length=0,Content-Type未指定

  • 注意:使用ajax发送请求,可以发送get、post、put、delete等各种http请求。

另外使用一个html文件来写ajax,从而构造各种请求,发送给后端。

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>构造ajax请求访问服务器title>
head>
<body>
    
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js">script>
    <script>
        $.ajax({
            type: 'post',
            url: 'method', //使用相对路径
            success: function(body){
                console.log(body) //此处的body即是HTTP响应报文里的body
            }
        })
    script>
body>
html>
  • 交互过程如下图所示:通过地址栏输入,然后服务器返回304,浏览器就访问本地的 test.html页面,在这个html中,type表示请求的方法,url则要访问的是服务器端资源的地址。服务器这里,在Smart Tomcat这里可以看Context Path;服务器端使用了注解表示servlet path。

【Java EE】-Servlet(二) Servlet API 详解_第5张图片

  • 从下图(使用fiddler抓包)可以看出,启动服务器后,在地址栏输入创建的 test.html的路径,浏览器发送一个get请求,服务器返回一个304提示客户端可以直接使用客户端本地缓存的资源,就是test.html,所以浏览器就执行 test.html。然后使用ajax构造请求,发送给服务器,服务器执行 HelloServlet的相应方法,服务器返回200表示访问成功。
  • 客户端第二次发送请求,即ajax构造完请求发送http给服务器时,浏览器的地址栏上的地址就变为了:127.0.0.1:8080/servletHello2/method
    【Java EE】-Servlet(二) Servlet API 详解_第6张图片
    上述过程简化后如下图:
    【Java EE】-Servlet(二) Servlet API 详解_第7张图片

3.4、使用JavaScript构造body为json格式的post请求

进阶:前端需要使用json格式传数据给后端,后端对请求进行处理:此时Content-Length != 0,Content-Type指定为 application/json 格式

客户端中,使用 testJS.html来构造body为json格式的post请求,在JavaScript中使用XMLHttpRequest对象来发送http请求。(使用ajax构造body为json格式的post请求后面再讲)

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript构造body为json格式的post请求title>
head>
<body>
    <script>
        // 1.构造body对象
        let body = {
            username: "zhangsan",
            password: "123"
        }
        
        // 2.将body对象转化为json字符串
        let jsonBody = JSON.stringify(body);

        // 3.获取XMLHttpRequest对象,在ajax中使用XMLHttpRequest对象来发送http请求
        // 然后设置请求头的方式,设置请求的body为 json格式。
        let xhr = new XMLHttpRequest();
        xhr.open('post', 'getParameter2', true); //设置请求方法和请求地址
        xhr.setRequestHeader('Content-type','application/json; charset=utf-8'); //设置数据格式

        // 4.设置回调函数
        xhr.onreadystatechange = function(){
            if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200){
                // 请求成功,设置响应的打印到浏览器的数据
                let response = JSON.parse(xhr.responseText);
                console.log(response);
            }
        };

        // 5.发送请求
        let date = xhr.send(jsonBody);

    script>
body>
html>

在后端服务器中,使用GetParameterServlet2.java来处理前端使用JavaScript构造body为json格式的post请求。
注意:由于 JavaScript构造的body为json格式的post请求可能格式上与后端的jackson不匹配,所以可能会出问题。我这里就直接使用InputStream把post的整个body取出来,然后把他写回浏览器中。

import com.fasterxml.jackson.databind.ObjectMapper;

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.InputStream;

@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
    //获取post请求,且body的类型为json的数据
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.获取postman构造的post请求报文的header中描述 body长度的字段Content-Length,根据字段创建缓冲数组
        int length = req.getContentLength();
        byte[] buffer = new byte[length];

        // 2.获取InputStream对象,并且将body的内容读入buffer中。
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);

        // 3.把字符数组转换成字符串,在服务器控制台打印字符串且也在浏览器上(postman)打印
        String body = new String(buffer,0,length,"utf-8");
        System.out.println("body = " + body);
        resp.getWriter().write(body);
    }
}

post请求抓包结果如下:
【Java EE】-Servlet(二) Servlet API 详解_第8张图片

响应发回testJS.html回调函数这里时,在客户端的控制台打印body。因为地址栏上的地址没有变成 127.0.0.1:8080/servlet/getParameter2,所以使用XMLHttpRequest对象send发送请求,是请求转发。
【Java EE】-Servlet(二) Servlet API 详解_第9张图片

二、HttpServletRequest

1、HttpServletRequest方法

HttpServletRequest方法 描述
String getProtocol() 返回协议的名称和版本
String getMethod() 返回 http方法 请求的名称
String getRequestURI() 返回URL的一部分,即从从URL开头到查询字符串之前的部分
String getContextPath() 返回URL的一部分,即从开头到第二级路径servlet Path之前的部分,也就是URL从开头到项目根目录的部分
String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串
Enumeration getParameterNames() 获取queryString里(get请求地址栏上)的键,返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。
String getParameter(String name) queryString(get请求地址栏上)根据键获取值,以字符串形式返回请求参数的值,或者如果参数不存在则返回null。
String[] getParameterValues(String name) 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。
Enumeration getHeaderNames() 返回一个枚举,包含在该请求中包含的所有的头名。
String getHeader(String name) 以字符串形式返回指定的请求头的值。
String getCharacterEncoding() 返回请求主体中使用的字符编码的名称。
String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null。
int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。
InputStream getInputStream() 用于读取请求的 body 内容. 返回一个 InputStream 对象。
  • Enumeration getParameterNames()方法可以获取地址栏输入地址时,get请求的 queryString的键值对中的 键,以枚举的方式返回。
    String getParameter(String name)方法可以获取地址栏输入地址时, get请求的 query String的键值对中的 值,以字符串的方式返回。
    Enumeration getHeaderNames() 方法和 String getHeader(String name) 方法与上述两个方法相似。
  • Header里包含 字符编码,请求正文的类型,请求正文的长度,这里用单独的三个方法封装这些属性,从而不需要先获取header,再去获取这些属性。
  • InputStream getInputStream(),获取输入流对象,通过这个输入流对象,就可以把body的内容读出来。

2、方法的使用

1> 列表中前五个方法的使用:

@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 需要设置ContextType为text/html,下面的代码才有效
        resp.setContentType("text/html; charset=utf-8");
        // 使用StringBuilder,把内容拼接在一起,然后写回resp的body中
        StringBuilder stringBuilder = new StringBuilder();
        
        stringBuilder.append(req.getProtocol());
        stringBuilder.append("
"
); stringBuilder.append(req.getMethod()); stringBuilder.append("
"
); stringBuilder.append(req.getRequestURI()); stringBuilder.append("
"
); stringBuilder.append(req.getContextPath()); stringBuilder.append("
"
); stringBuilder.append(req.getQueryString()); // 把内容写入resp resp.getWriter().write(stringBuilder.toString()); } }

【Java EE】-Servlet(二) Servlet API 详解_第10张图片
2> 获取header中的键值对的键和值

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("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 需要设置ContextType为text/html,下面的html代码才有效
        resp.setContentType("text/html; charset=utf-8");
        // 使用StringBuilder,把内容拼接在一起,然后写回resp的body中
        StringBuilder stringBuilder = new StringBuilder();

        // 获取header里面的键headerName(使用getHeaderNames())和值headerValue(使用getHeader(键))
        Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()){
            // 获取枚举中的header每个键值对的的键
            String headerName = headerNames.nextElement();
            // 获取枚举中的header每个键值对的的值
            String headerValue = req.getHeader(headerName);
            stringBuilder.append(headerName+":"+headerValue);
            stringBuilder.append("
"
); } resp.getWriter().write(stringBuilder.toString()); } }

【Java EE】-Servlet(二) Servlet API 详解_第11张图片

  • 使用HttpServletRequest类提供的方法写出来的结果和 fiddler抓包的结果是一样的。我们在服务器端设置了响应报文类型为:text/html;charset=utf-8,可以在fiddler的响应报文中看到:
    【Java EE】-Servlet(二) Servlet API 详解_第12张图片

3、前后端交互方式(重点)

3.1、GET请求,query string传参

  • 直接在地址栏输入URL + query string(协议版本号浏览器会自动为我们加上),那么使用req.getParameter(String name) 就可以获取到query string中 键name对应的值。
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("/getParameter")
public class GetParameterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 地址栏路径:/getParameter?user=lisi&age=20
        String user = req.getParameter("user");
        String age = req.getParameter("age");

        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write("user:"+user+" age:"+age);
    }
}

【Java EE】-Servlet(二) Servlet API 详解_第13张图片

3.2、POST请求,使用form表单

  • 使用test.html中的form表单,构造post请求发送给服务器并定位到getParameter注解,服务器收到请求后调用注解getParameter对应的这个servlet的post方法。
  • 注意:form表单中的方法为get时,在input标签输入的内容会自动出现在地址栏上作为query string;方法为post时,在input标签输入的内容会填充到http请求报文的body中。这两种情况,服务器都可以使用req.getParameter(String name) 获取到键对应的值。

test.html中使用form表单构造http请求,get/post/put…

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>前端给后端传参方式title>
head>
<body>
	
    <form action="getParameter" method="get">
        
        <span>username:span><input type="text" name="username">
        <br>
        <span>password:span><input type="password" name="password">
        <br>
        <input type="submit" value="submit">
    form>
body>
html>

后端服务器收到form表单的请求后,根据action属性的url定位到GetParameterServlet.java,然后执行对应的get/post/put…方法,然后使用req.getParameter获取到前端发送过来的数据。

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("/getParameter")
public class GetParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 地址栏路径:/getParameter
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write("username:"+username+" password:"+password);
    }
}

【Java EE】-Servlet(二) Servlet API 详解_第14张图片

3.3、POST请求,body使用json格式(重点)

第一种方式:直接获取到body:

  • 可以使用postman来构造post请求的body为 json格式,也可以使用ajax构造post请求的body为 json格式。
  • 注意:客户端发送json格式的数据,服务端不能再使用req.getParameter(String name)取其中的键对应的值;而是使用req.getInputStream(),读取长度取决于请求报文中header里的 Content-Length多大。

postman作为浏览器发送post请求,经过服务器处理,在服务器控制台打印这个post请求的body,且原样返回了post请求中body的json格式的数据,打印到postman(浏览器)上。如下图:

【Java EE】-Servlet(二) Servlet API 详解_第15张图片

【Java EE】-Servlet(二) Servlet API 详解_第16张图片

代码如下:

客户端利用postman发送body为json格式的post请求数据,服务器端处理post请求的代码 :GetParameterServlet2.java

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.InputStream;

@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
    //获取post请求,且body的类型为json的数据
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.获取postman构造的post请求报文的header中描述 body长度的字段Content-Length,根据长度字段创建缓冲数组
        int length = req.getContentLength();
        byte[] buffer = new byte[length];

        // 2.获取InputStream对象,并且将body的内容读入buffer中。
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);

        // 3.把字符数组转换成字符串,在服务器控制台打印字符串且也在浏览器上(postman)打印
        String body = new String(buffer,0,length,"utf-8");
        System.out.println("body = " + body);
        resp.getWriter().write(body);
    }
}

第二种方式:使用jackson将body键值对封装成java对象

  • 另外,发送post请求的body使用json格式传输数据,这种方法是最新流行的前后端交互的 方式,越新的项目可能使用 json格式的方式就更多。
  • post请求的body为json格式这种方式,不能和form表单一样,直接使用getParameter(String name) 使用key获取value。需要引入第三方库——可以使用jackson,引入第三方库后,服务器就可以解析json格式的数据,把body键值对分到Java对象属性中 => 引入方法:直接通过maven来引入第三方库,找到文件向pox.xml中引入即可,如果报红,就刷新一下maven。
    【点击进入Maven相关文件下载地址】

使用postman构造http的post请求,然后这个post请求的body使用json格式,服务器需要对json格式的 post请求的body进行处理。

【Java EE】-Servlet(二) Servlet API 详解_第17张图片
代码如下:

使用jackson把json格式的键值对封装成Java对象,从而对Java对象的属性可以做访问操作。

注意:

  • 应该将创建的User类的成员变量进行封装,提供get和set方法,这里为了方便没有封装,而是直接将成员变量名设置为public。
  • body中的键值对个数和Java对象成员变量不匹配的影响:
    1> 如果post请求中的body的键值对个数 小于等于 服务器中的Java对象成员变量个数,但是内容是匹配的(键和成员变量名),那么post中未传键值对对应 的Java对象的成员变量值为null;
    2> 如果post请求键值对个数等于 服务器中Java对象成员变量的个数,但是post请求的body中键名和 后端中的java对象的成员变量名不同,那就是500错误,或者post请求的键值对个数 大于 服务器端的Java对象的成员变量个数,也是500错误。
import com.fasterxml.jackson.databind.ObjectMapper;

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.InputStream;

// 使用jackson时,封装一个类,用于对应post请求的body的 json类型的键值对数据,即使用jackson把json格式的键值对封装成Java对象。
class User {
    public String username;
    public String password;
}

@WebServlet("/getParameter2")
public class GetParameterServlet2 extends HttpServlet {
    //获取post请求,且body的类型为json的数据
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 使用jackson涉及到的核心对象:ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        // 把传入的数据 body的json类型键值对数据封装成一个类,.readValue()方法就是读取post请求中的数据
        // 然后根据第二个参数的属性依次填入对象,得到一个Java对象。得到Java对象后,属性值就随便取了。
        User user = (User) objectMapper.readValue(req.getInputStream(), User.class);//需要强制类型转换一下,因为返回的是一个object对象。

        // 把获取到的内容改变格式 到浏览器输出
        resp.getWriter().write("username=" + user.username + " password=" + user.password);
    }
}

3.4、小结:前后端数据交互

  • req.getParameter(String name) 可以获取 非form表单和form表单 get请求的 query string;也可以获取 form表单中post请求 的body中的键值对根据键获得值。
    req.getInputStream() 可以读取body中的内容,但是只能完整读取整个body,想要获取json格式的请求的body的键值对,可以引入第三方库,可以使用jackson
  • 对jackson的readValue方法的理解:
    【Java EE】-Servlet(二) Servlet API 详解_第18张图片
    【Java EE】-Servlet(二) Servlet API 详解_第19张图片

三、HttpServletResponse

  • HttpServletResponse的方法相对来说,比较简单,这里直接列出了方法和描述,不再过多赘述,只做简单介绍。
  • sendRedirect(String location) 方法是重定向,第一次请求时,服务器会给浏览器返回一个临时重定向状态码302,然后浏览器重新发送请求 访问 location。
    如果不用sendRedirect(String location)方法,也可以使用setStatus(int sc)方法设置响应的 header的 status为302,setHeader(String name,String value)方法 设置location为目标访问地址。
  • 关于setStatus(int sc)方法,使用setStatus(404),则浏览器显示一个默认的404页面;加上resp.getWriter().write("

    404

    "),则显示我们后端写的这个错误页面;使用resp.setError(404),则显示通常访问是404的页面,这个页面和浏览器有关。
    【Java EE】-Servlet(二) Servlet API 详解_第20张图片

HttpServletResponse方法 描述
void setStatus(int sc) 为该响应设置状态码。
void setHeader(String name,String value) 在响应的header中添加键值对,如果 name已经存在,则覆盖旧的值
void addHeader(Stringname, String value) 在响应的header末尾添加键值对,添加一个带有给定的名称和值的 header. 如果 name已经存在,不覆盖旧的值, 并列添加新的键值对
void setContentType(String type) 设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME 字符集)例如,UTF-8。
void sendRedirect(String location) 使用指定的重定向位置 URL 发送临时重定向响应到客户端。
PrintWriter getWriter() 用于往 body 中写入文本格式数据
OutputStream getOutputStream() 用于往 body 中写入二进制格式数据.

你可能感兴趣的:(【JavaEE】,servlet,java-ee,java)