9-Servlet API

本文详细介绍了Servlet的生命周期、核心方法,包括如何处理GET和POST请求,以及解决中文乱码问题。讨论了HttpServletRequest和HttpServletResponse的核心方法,如获取JSON格式数据,并使用Jackson库进行JSON解析。此外,还提到了Ajax提交数据的方式,以及使用Postman进行模拟请求的方法。
摘要由CSDN通过智能技术生成

目录

1.HttpServlet

1.1.核心方法

PS:所有方法

1.2.处理 GET 请求

PS:关于中文乱码问题

1.3.处理POST请求

PS:数据提交的两种方式——form表单提交&ajax提交

2.HttpServletRequest

2.1.核心方法

2.2.核心方法示例

PS:Postman

2.3.获取JSON格式数据

2.3.1.使用 InputStream 获取 JSON 参数

2.3.2.使用 Jackon 库进行 JSON 解析

--->PS:Java中JSON对象操作

--->PS:若觉得定义Student类太麻烦,还有更简单的写法——可以转换成HashMap(不推荐)。

3.HttpServletResponse

3.1.核心方法

3.2.代码示例:设置状态码

3.3.代码示例:自动刷新

PS:配置Servlet的JDK版本

3.4.代码示例:重定向


1.HttpServlet

我们写 Servlet 代码的时候,⾸先第⼀步就是先创建类,继承⾃ HttpServlet,并重写其中的某些⽅法。

1.1.核心方法

实际开发的时候主要重写 doXXX ⽅法,很少会重写 init / destory / service。

这些⽅法的调⽤时机,就称为 "Servlet ⽣命周期"。(也就是描述了⼀个 Servlet 实例从⽣到死的过程)

注意:HttpServlet 的实例只是在程序启动时创建⼀次,⽽不是每次收到 HTTP 请求都重新创建实例。

PS:所有方法

1.2.处理 GET 请求

@WebServlet("/hello") // http://ip:port/hello -> url映射到当前类
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //返回的类型和编码格式
        resp.setContentType("text/html; charset=utf-8"); //返回的不是html静态页面,返回的是html里的内容
        //返回的数据
        resp.getWriter().println("<h1>你好,Servlet.</h1>"); //打印器:打印数据给前端,此处println也可使用write来代替
    }
}

PS:关于中文乱码问题

  • 如果在响应代码中写⼊中⽂,此时在浏览器访问的时候,会看到 "乱码" 的情况。因为如果自己不设置编码格式,会默认使用Content-Type:text/html; charset=ISO-8859-1;
  • 关于 "乱码":中⽂的编码⽅式有很多种,其中最常⻅的就是 utf-8。如果没有显式的指定编码⽅式,则浏览器不能正确识别编码,就会出现乱码的情况。
  • 可以在代码中,通过 resp.setContentType("text/html; charset=utf-8"); 显式的指定编码⽅式。

1.3.处理POST请求

PS:数据提交的两种方式——form表单提交&ajax提交

(GET形式通过浏览器访问URL传参提交。)

通过前端页面提交:

  1. form 表单提交
  2. ajax 提交

⼀个 Servlet 程序中可以同时部署静态⽂件,静态⽂件就放到 webapp ⽬录中即可。

①form表单提交

定义交互规范:

Request:

  • URL地址:xxx/login
  • Parmams请求参数:username=xxx&password=yyy

Response:

  • 结果信息:如果前端输入的用户名和密码都等于admin,就认为登录成功,否则登录失败。

在webapp目录下创建前端登录页面login.html:

(在Idea中修改HTML代码不需要重启Tomcat,直接刷新浏览器页面即可;而修改Servlet代码需要重启Tomcat。)

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录系统</title>
</head>
<body>
    <form action="login" method="post">
        <div style="margin-top:50px;margin-left:40%;">
            <h1 style="padding-left:50px;">用户登录</h1>
            姓名:<input type="text" name="username">
            <p>
            密码:<input type="password" name="password">
            <p>
            <div style="padding-left:50px;">
                 <input type="submit" value=" 提 交 "> &nbsp;&nbsp;&nbsp;
                 <input type="reset" value=" 重 置 ">
            </div>
        </div>
    </form>
</body>
</html>

在java源文件目录下创建后端类LoginServlet:

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("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String result = "未知错误";
        //1.得到参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        //2.业务处理
        //非空效验
        if(null != username && null != password && !username.equals("") && !password.equals("")) {
            if(username.equals("admin") && password.equals("admin")) {
                result = "恭喜:登录成功!";
            } else {
                result = "登录失败,用户名或密码输入错误,请检查!";
            }
        }else {
            result = "非法参数!";
        }

        //3.将结果返回给前端
        //为防止中文乱码,需要设置
        resp.setContentType("text/html; charset=utf-8");
        resp.getWriter().write(result);
    }
}

POST方式提交存在的问题:

  • 不支持浏览器访问。
  • 且每一次提交都会重新发送请求。

form表单提交存在的问题:

  • 提交数据后,整个页面都会提交,整个页面没了:由login.html->login。

②ajax提交

Ajax 即 Asynchronous Javascript And XML(异步JavaScript和XML),使⽤ Ajax 不需要刷新整个⻚⾯,也叫局部提交,在当前页面得到响应结果,这使得程序能够更快地回应⽤户的操作。

ajax使用方式有2种:

  1. 原生的ajax方式。
  2. jQuery方式。

不使用原生的写法,原因:

  • 太麻烦。
  • 在不同浏览器的写法不同。

那么->使用jQuery方式。

在webapp目录下创建前端登录页面login-ajax.html:(在VSCode中打开写代码,支持更好~)

在webapp目录下创建js目录,将jquery复制进来。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录系统</title>
    <script src="js/jquery-1.9.1.min.js"></script>
</head>
<body>
    <div style="margin-top:50px;margin-left:40%;">
         <h1 style="padding-left:50px;">用户登录</h1>
         姓名:<input id="username" type="text"><p>
         密码:<input id="password" type="password"><p>
         <div style="padding-left:50px;">
             <input type="button" value=" 提 交 " onclick="mySubmit()"> &nbsp;&nbsp;&nbsp;
             <input type="reset" value=" 重 置 ">
         </div>
    </div>
    <script>
        //ajax请求登录
        function mySubmit() {
            //非空效验,在前端就屏蔽掉,消耗浏览器(客户端)的资源,不能浪费宝贵的服务器端资源
            var username = jQuery("#username");
            var password = jQuery("#password");
            if("" == jQuery.trim(username.val())) { //jQuery.trim()是去空格
                alert("请先输入用户名!");
                username.focus(); //将光标回置到username上
                return; //js的方法没有返回的类型,此处可以返回值,也可不返回值。
            }
            if("" == jQuery.trim(password.val())) {
                alert("请先输入密码!");
                password.focus();
                return; 
            }
            jQuery.ajax({
                url:"login", //设置请求地址,"login"为⼀个相对路径, 最终真实发送的请求的 URL 路径为"servlet-demo/login"
                type:"POST", //设置请求方法类型,要大写
                contentType:"application/x-www-form-urlencoded; charset=utf-8", //设置请求类型,防止中文乱码
                dataType:"", //设置响应类型及其编码格式,通常情况下不需要在前端设置,在后端会设置
                data:{"username":username.val(),"password":password.val()}, //设置请求参数,一定要和后端的key值保持一致,否则会出现“非法参数”的错误
                success:function(data) { //回调函数
                    alert(data);
                }
            });
        }
    </script>
</body>
</html>

不管前端框架怎么变,后端代码不用变。

当没有设置Content-Type时,默认是表单类型:

Content-Type: application/x-www-form-urlencoded; charset=UTF-8";

通过类似的⽅式还可以验证 doPut、doDelete 等⽅法,此处不再⼀⼀演示。

2.HttpServletRequest

2.1.核心方法

目前对于Servlet的请求,最常见的ContentType有3种:

  • ①默认的表单提交方式(最常用):原生的form表单提交、ajax提交(没有设置ContentType)。
  • ②json数据传输方式:可以跨业务和语言的数据格式。
  • ③multipart/form-data文件提交方式
  • 用哪个要看这个接口是自己去用还是公共的。

通过这些⽅法可以获取到⼀个请求中的各个⽅⾯的信息。

注意:请求对象是服务器收到的内容,不应该修改。因此上⾯的⽅法也都只是 "读" ⽅法,⽽不是 "写" ⽅法。

2.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("/myreq")
public class MyReqServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使得当前的类既支持GET方式,又支持POST方式
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        StringBuilder builder = new StringBuilder();
        //得到请求的协议和版本号
        builder.append("协议:" + req.getProtocol() + "<br>");
        //得到请求的类型
        builder.append("Method:" + req.getMethod() + "<br>");
        //得到URI-是URL除去IP+端口号,直接是资源地址
        builder.append("URI:" + req.getRequestURI() + "<br>");
        //得到URL-统一资源定位符,是全的地址
        builder.append("URL:" + req.getRequestURL() + "<br>");
        //得到访问URL中当前网站的地址,URI的前半部分
        builder.append("ContextPath:" + req.getContextPath() + "<br>");
        //得到所有的参数,不是body里面的参数,是URL后面跟的参数
        builder.append("QueryString:" + req.getQueryString() + "<br>");

        builder.append("<hr><h2>请求头</h2>");
        //得到所有的请求类型(headerNames)和请求值
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) { //判断还有没有head name,判断之后光标不会往下移动
            String headName = headerNames.nextElement(); //光标下置
            //得到head value
            String headValue = req.getHeader(headName);
            builder.append(headName + ": " + headValue + "<br>");
        }
        builder.append("<hr>");

        builder.append("参数:" + req.getParameter("name") + "<br>");
        resp.getWriter().write(builder.toString());
    }
}

PS:Postman

  • 安装时直接点下一步。
  • Postman是做模拟请求的。前后端配合时,主动权在后端,模拟前端进行数据的提交,后端就不需要等前端的页面了,只需要规定好key值即可。
  • 测接口时也会用。有时页面是有限制的,比如输入了js,不让用户名为空,此时就可以通过其他手段,模拟请求,绕过js,不输入用户名,就能把请求发给后端。

在Header里设置请求头:

2.3.获取JSON格式数据

使⽤之前 getParameter 的⽅式是获取不了 JSON 格式的数据的。会得到null。

2.3.1.使用 InputStream 获取 JSON 参数

 

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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("/postjson")
public class JsonPostServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp); //有GET请求会直接转发到POST上
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置返回类型和编码
        resp.setContentType("text/html; charset=utf-8");

        //1.得到数据流
        ServletInputStream inputStream = req.getInputStream();

        //2.使用数组(容器)接收流信息
        byte[] bytes = new byte[req.getContentLength()];
        inputStream.read(bytes);

        //3.将数组转换成字符串(或对象)
        String result = new String(bytes, "utf-8");
        System.out.println(result);

        resp.getWriter().println(result);
    }
}

注意:

到⽬前为⽌,服务器拿到的 JSON 数据仍然是⼀个整体的 String 类型,如果要想获取到 id 和 name 的具体值,还需要搭配 JSON 库进⼀步解析。

2.3.2.使用 Jackon 库进行 JSON 解析

--->前置知识:

--->PS:Java中JSON对象操作

  • 阿里巴巴(国内):fast json(号称最快,但漏洞多)/ fast json2(性能是fast json的1.65倍)
  • 阿帕奇(Java 领域最大的国际社区):jackson(用的最多的 json 转换工具,被 Spring 内置,问题少,性能适中)
  1. 将对象转换成 json 字符串(字符串可以保存在磁盘上)(这个过程也叫序列化)。
  2. 将 json 字符串转换成对象(这个过程也叫反序列化)。

在中央仓库中搜索 Jackson,选择 JackSon Databind,将 Jackon 依赖添加到项目的 pom.xml 文件中,点击 reload 按钮。

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.2.1</version>
</dependency>

在java源文件夹下创建普通类App:

Lombok可在Maven中央仓库搜索依赖,添加到项目的pom.xml中。

也可以网页版安装:

Lombok网页安装https://plugins.jetbrains.com/plugin/6317-lombok

也可以直接通过Idea添加Lombok依赖:

Lombok中的@Data注解就会自动给每个属性添加get方法和set方法(@Getter注解和@Setter注解)以及其他的一些方法(如toString方法)。

这只是给项目添加了Lombok,要想给Idea添加Lombok,使得打点有对应的提示,需要添加Lombok插件:

添加toString方法(打印对象的):

或者直接使用Lombok中的@ToString注解(被@Data注解包含着)

语义更明确:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

public class App {
    public static void main(String[] args) throws JsonProcessingException {
        //1.创建一个json操作对象
        ObjectMapper objectMapper = new ObjectMapper();

        //2.1.将对象转换成json字符串/序列化
        Student student = new Student();
        student.setId(1);
        student.setName("Java");
        student.setPassword("123");
        String result = objectMapper.writeValueAsString(student);
        System.out.println(result);

        //2.2.将json字符串转换成对象
        String jsonStr = "{\"id\":2,\"name\":\"lisi\",\"password\":\"456\"}"; //直接复制上面在控制台打印的内容,修改即可
        Student lisi = objectMapper.readValue(jsonStr, Student.class); //参数:(字符串,要转换的目标类型)
        System.out.println(lisi);
    }
}

@Setter
@Getter
@ToString
class Student {
    private int id;
    private String name;
    private String password;
    //...
}

--->PS:若觉得定义Student类太麻烦,还有更简单的写法——可以转换成HashMap(不推荐)。

  • 因为json都是key-value形式,HashMap也是key-value形式。
  • 但HashMap是语义不明确的,企业中大部分团队不允许使用。
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.HashMap;
import java.util.Map;

public class App {
    public static void main(String[] args) throws JsonProcessingException {
        //1.创建一个json操作对象
        ObjectMapper objectMapper = new ObjectMapper();

        //2.1.将对象转换成json字符串/序列化
        Student student = new Student();
        student.setId(1);
        student.setName("Java");
        student.setPassword("123");
        String result = objectMapper.writeValueAsString(student);
        System.out.println(result);

        //2.2.将json字符串转换成对象
        String jsonStr = "{\"id\":2,\"name\":\"lisi\",\"password\":\"456\"}"; //直接复制上面在控制台打印的内容,修改即可
        HashMap<String, Object> map = objectMapper.readValue(jsonStr, HashMap.class);
        for(Map.Entry<String, Object> item : map.entrySet()) {
            System.out.println(item.getKey() + ":" + item.getValue());
        }
    }
}

@Setter
@Getter
@ToString
class Student {
    private int id;
    private String name;
    private String password;
    //...
}

--->回到项目:

JSON后端读取——创建类JsonPostServlet:

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.HashMap;

@WebServlet("/postjson")
public class JsonPostServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp); //有GET请求会直接转发到POST上
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置返回类型和编码
        resp.setContentType("text/html; charset=utf-8");

        //1.得到数据流
        ServletInputStream inputStream = req.getInputStream();

        //2.使用数组(容器)接收流信息,在Java中接收流只能用bytes,不能用String,这是API的设计
        byte[] bytes = new byte[req.getContentLength()];
        inputStream.read(bytes);

        //3.将数组转换成字符串(或对象)
        String result = new String(bytes, "utf-8");
        System.out.println(result);

        //4.将字符串转换成对象(或字典)
        ObjectMapper objectMapper = new ObjectMapper();
        HashMap<String, String> map = objectMapper.readValue(result, HashMap.class);

        System.out.println("用户名:" + map.get("username"));
        System.out.println("密码:" + map.get("password"));

        resp.getWriter().println("用户名:" + map.get("username") + "| 密码:" + map.get("password"));
    }
}

到了JavaEE只需要加一个注解即可简单解决!

JSON前端请求——创建页面login-ajax-json.html:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录系统</title>
    <script src="js/jquery-1.9.1.min.js"></script>
</head>
<body>
<div style="margin-top:50px;margin-left:40%;">
    <h1 style="padding-left:50px;">用户登录</h1>
    姓名:<input id="username" type="text"><p>
    密码:<input id="password" type="password"><p>
    <div style="padding-left:50px;">
        <input type="button" value=" 提 交 " onclick="mySubmit()"> &nbsp;&nbsp;&nbsp;
        <input type="reset" value=" 重 置 ">
    </div>
</div>
<script>
        //ajax请求登录
        function mySubmit() {
            //非空效验,在前端就屏蔽掉,消耗浏览器(客户端)的资源,不能浪费宝贵的服务器端资源
            var username = jQuery("#username");
            var password = jQuery("#password");
            if("" == jQuery.trim(username.val())) { //jQuery.trim()是去空格
                alert("请先输入用户名!");
                username.focus(); //将光标回置到username上
                return; //js的方法没有返回的类型,此处可以返回值,也可不返回值。
            }
            if("" == jQuery.trim(password.val())) {
                alert("请先输入密码!");
                password.focus();
                return;
            }
            jQuery.ajax({
                url:"postjson", //设置请求地址
                type:"POST", //设置请求方法类型,要大写
                contentType:"application/json; charset=utf-8", //设置请求类型,防止中文乱码
                dataType:"", //设置响应类型及其编码格式,通常情况下不需要设置
                //JSON.stringify() -> 将对象转换成JSON字符串
                //注意:⼀定要把 data 中的 json 对象转换成 json 字符串。
                data:JSON.stringify({"username":username.val(),"password":password.val()}), //设置请求参数,一定要和后端的key值保持一致,否则会出现“非法参数”的错误
                success:function(data) { //回调函数
                    alert(data);
                }
            });
        }
    </script>
</body>
</html>

3.HttpServletResponse

3.1.核心方法

注意:

  • 响应对象是服务器要返回给浏览器的内容,这⾥的重要信息都是程序猿设置的。因此上⾯的⽅法都 是 "写" ⽅法。
  • 对于状态码/响应头的设置要放到 getWriter / getOutputStream 之前,否则可能设置失效。

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

@WebServlet("/state")
public class StateServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //GET是不能发JSON的,因为JSON的数据是在body里的
         int state = Integer.valueOf(req.getParameter("state"));
         if(state > 0) {
             resp.setStatus(state);
         } else {
             resp.setContentType("text/html; charset=utf-8");
             resp.getWriter().println("<h2>无效参数</h2>");
         }
    }
}

变换不同的 status 的值,就可以看到不同的响应结果。

3.3.代码示例:自动刷新

实现⼀个程序,让浏览器每秒钟⾃动刷新⼀次(HTML页面本身自己就能刷新)。并显示当前的时间戳。

PS:配置Servlet的JDK版本

 

 

 

 

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.time.LocalDateTime;

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh", "1");
        resp.getWriter().println("" + LocalDateTime.now());
    }
}

3.4.代码示例:重定向

实现⼀个程序,返回⼀个重定向 HTTP 响应,⾃动跳转到另外⼀个⻚⾯。

创建工具类——用来判null和判空:

public class StringUtils {
    /**
     * 用来判null和判空的,如果为null或空,返回true
     * @param str
     * @return
     */
    public static boolean hasLength(String str) {
        return str == null || str.length() == 0;
    }
}
import utils.StringUtils;
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("/main")
public class MainServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String token = req.getParameter("token");
        if(!StringUtils.hasLength(token) && "admin".equals(token)) {
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8"); //可以分开写,也可以写在一起,都可以
            resp.getWriter().println("<h1>欢迎您:系统管理员!</h1>");
        } else {
            //未授权,跳转到登录页面
            resp.sendRedirect("http://localhost:8080/servlet-demo/login.html");
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值