Servlet

目录

什么是Servlet

第一个Servlet程序

更方便的部署方式

安装Smart Tomcat插件

配置Smart Tomcat插件

访问出错怎么办

Servlet运行原理

ServletAPI详解

HttpServlet

HttpServletRequest

HttpServletResponse

Cookie和Session

上传文件


前置知识

👉HTTP协议👈

👉Tomcat👈

什么是Servlet

Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序员的 API, 帮助程序员简单高效的开发一个 web app

基于Servlet开发

1.初始化,允许程序员注册一个类到Tomcat中,让这个类和HTTP中的一个特定的请求相关联

2.进入循环,循环的处理很多请求

(1)读取HTTP请求,Servlet解析这个请求字符串,生成一个HttpServletRequest对象

(2)根据请求对象生成一个HttpServletResponse对象,根据请求生成响应,这个过程,就是初始化阶段注册的类里面的代码完成的

(3)把HttpServletResponse对象转换成HTTP响应,返回给浏览器

我们程序员,只需要关注2-(2)这个环节(业务逻辑),其他环节Tomcat/Servlet已经帮助我们实现好了

第一个Servlet程序

1.创建项目

创建一个maven项目

2.引入依赖

在pom.xml里引入ServletAPI的依赖包,然后刷新maven

3.创建目录

依次创建这三个目录

并且在web.xml里写一些代码

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>

4.编写代码

先写一个简单的helloworld

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也是Servlet里的注解,功能是把类和HTTP特定的请求进行关联,是根据URL请求的路径来关联的
//如果Tomcat收到了一个路径为/hello的请求,就会调用到HelloServlet的代码
//如果这个请求是GET请求,就会调用到HelloServlet的doGet的方法
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //这个调用父类代码的操作,是直接构造了一个错误的响应(状态码为405的响应)
        //super.doGet(req, resp);
        //这个操作,就是往HTTP响应的body中,写了一个helloworld的字符串
        resp.getWriter().write("helloworld");
    }
}

5.打包程序

之后会出现

而Tomcat里面需要的是.war的文件,所以我们需要先配置pom.xml

然后再刷新maven,再双击package

我们看到这个名字是artfact id + version,名字很复杂,我们需要换一个名字

先删除原文件,再配置pom.xml

刷新maven,双击package

6.部署程序

把war包拷贝到Tomcat中webapps目录就行

在WEB-INF目录中,就包含了web.xml以及编译后生成的.class文件

META-INF目录中,包含了当前webapp中以来的一些第三方jar包

7.验证程序

更方便的部署方式

安装Smart Tomcat插件

配置Smart Tomcat插件

1.开始配置

2.更改名字并且设置路径(记住这里的ContextPath为/servlet)

3.此时IDEA上面会出现Tomcat的图标

4.可以看到运行成功了

5.测试一下

访问出错怎么办

出现404

1.URL写错了

2.web.xml写错了

在IDEA终端可以查看到

出现405

请求的方法和代码中重写的方法对不上号

因为我这里改成了doPost方法

出现500

1.代码抛异常了

因为我这里写了一个空指针异常

2.没注释掉super

我这里super调用了父类代码

出现空白页面

1.代码里没往body里写东西

出现无法访问此网站

2.Tomcat启动失败了或者没启动

Servlet运行原理

Tomcat拿到HTTP请求后,就会对请求进行解析,生成一个HttpServletRequest对象,调用Servlet类,执行程序员写好的代码

Servlet在初始化的时候,会先调用一次init方法,可以自己重写init,就可以在初始化阶段做一些事情了

Servlet在销毁之前,会调用一次destroy方法,也可以重写destroy方法,做一些善后工作(但实际上,不一定能真的执行到destroy方法,如果直接kill到tomcat进程,此时destroy都来不及执行)

ServletAPI详解

我们写 Servlet 代码的时候,首先第一步就是先创建类,继承自 HttpServlet,并重写其中的某些方法,重写的目的是为了能够把程序员定义的逻辑插入到Tomcat这个框架中,好让Tomcat可以调用

HttpServlet

核心方法

说一下servlet的生命周期

1.Servlet在实例化之后调用一次init

2.Servlet每次收到请求,调用一次service

3.Servlet在销毁之前,调用一次destroy

处理GET/POST请求

我们需要写两个文件

首先重写doGet和doPost方法

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 MethodServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("test/html;charset=utf-8");
        resp.getWriter().write("GET 响应");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("test/html;charset=utf-8");
        resp.getWriter().write("POST 响应");
    }
}

在一个html文件中,新增一个按钮,和对应的点击事件处理函数

<!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>Document</title>
</head>

<body>
    <button onclick="sendGet()">发送GET请求</button>
    <button onclick="sendPost()">发送POST请求</button>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script>
        function sendGet() {
            $.ajax({
                type: "get",
                url: "method",
                success: function (data, status) {
                    console.log(data);
                }
            })
        }
        function sendPost() {
            $.ajax({
                type: "post",
                url: "method",
                data: "request body",
                success: function (data, status) {
                    console.log(data);
                }
            })
        }
    </script>
</body>

</html>

然后我们就可以测试一下了

PUT请求也是同理,需要注意的是

HttpServletRequest

核心方法

一些方法的解释

URL和URI的区别

L location 资源的位置

I Id 资源的标识符

含义类似,只要能唯一标识资源的就是URI,在URI的基础上给出其资源的访问方式就是URL

打印请求信息

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 ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");

        //把生成的响应body放到respBody中
        StringBuilder respBody = new StringBuilder();
        respBody.append(req.getProtocol());
        respBody.append("<br>");
        respBody.append(req.getMethod());
        respBody.append("<br>");
        respBody.append(req.getRequestURI());
        respBody.append("<br>");
        respBody.append(req.getContextPath());
        respBody.append("<br>");
        respBody.append(req.getQueryString());
        respBody.append("<br>");

        respBody.append("<h3>headers:</h3>");
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String headerName = headerNames.nextElement();
            respBody.append(headerName+": ");
            respBody.append(req.getHeader(headerName));
            respBody.append("<br>");
        }

        resp.getWriter().write(respBody.toString());
    }
}

效果

获取GET请求中的参数

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 {
        resp.setContentType("text/html; charset=utf-8");

        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        /*
        if(userId==null||userId.equals("")){
            //参数不存在
            //处理参数不存在的情况
        }
        */
        resp.getWriter().write(String.format("userId: %s; classId: %s <br>",userId,classId));
    }
}

效果

获取POST请求中的参数

1.application/x-www-form-urlencoded

这种格式类似于query string格式,所以还是用原来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("/postParameter")
public class PostParameterServlet extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");

        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write(String.format("userId: %s; classId: %s <br>",userId,classId));
    }
}

testPost.html

<!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>testPost</title>
</head>
<body>
    <!--使用ajax/form表单构造POST请求-->
    <form action="postParameter" method="POST">
        <input type="text" name="userId">
        <input type="text" name="classId">
        <input type="submit" name="提交">
    </form>
</body>
</html>

效果

2.mutlipart/form-data

这种格式比较复杂,主要是用来提交文件的

3.application/json

先把整个body读取出来,再使用json库解析

 

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;

//通过这个类来表示解析后的结果
class JsonData{
    public int userId;
    public int classId;
}

@WebServlet("/postParameterJson")
public class PostParameterJson extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先把整个body读出来
        String body = readBody(req);
        //使用jackson来解析
        //先创建一个jackson的核心对象ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        JsonData jsonData = objectMapper.readValue(body,JsonData.class);
        //以上就完成了Json格式的字符串到Java对象的解析过程
        resp.getWriter().write(String.format("userId: %s; classId: %s <br>",jsonData.userId,jsonData.classId));
    }
    private String readBody(HttpServletRequest req) throws IOException{
        //读取body需要根据req.getInputStream得到一个流对象,从这个流对象中读取
        InputStream inputStream = req.getInputStream();
        //通过contentLength拿到请求中的body字节数
        int contentLength = req.getContentLength();
        byte[] buffer = new byte[contentLength];
        inputStream.read(buffer);
        return new String(buffer,"utf-8");
    }
}

如果body的值是{"userId":100,"classId":1}

1.先把JSON格式的字符串转换成类似于HashMap的键值对结构

userId:100 classId:1

2.根据类对象,获得到要转换结果的类,都有哪些属性,获得到属性的名字

此处是通过JsonData获取到里面的属性,有两个userId和classId(通过反射机制)

3.拿这JsonData这里的每个属性的名字,在上面的第一步构造出的哈希表里去查,如果查到了,就把查询的值赋值到JsonData对应的属性里面

testPost2.html

 

<!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>testPost2</title>
</head>
<body>
    <!--构造body为json格式的数据就只能使用ajax的方式来实现-->
    <button onclick="sendJson()">发送请求</button>
    <script src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
    <script>
        function sendJson(){
            let body = {
                userId : 100,
                classId : 1
            };
            $.ajax({
                type: "post",
                url: "postParameter",
                contentType : "application/json;charset=utf-8",
                data: JSON.stringify(body),
                success:function(data,status){
                    console.log(body);
                }
            })
        }
    </script>
</body>
</html>

效果

一般用form或者ajax来构造请求,这样很麻烦,所以我们可以用第三方工具postman来构造请求

HttpServletResponse

核心方法

HttpServletRequest里面的内容,是客户端构造的,服务器需要做的就是获取到这里的内容,尤其是程序员自己定义的数据

HttpServletResponse里面的内容,是服务器构造的,要返回给客户端的

注意: 对于状态码/响应头的设置要放到 getWriter / getOutputStream 之前. 否则可能设置失效.

设置状态码

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("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");

        //让用户传入一个请求
        //请求在query string中带一个参数,就表示响应的状态码
        //根据用户的输入,返回不同的状态码的响应
        String statusString = req.getParameter("status");
        if(statusString==null||statusString.equals("")){
            resp.getWriter().write("当前请求的参数status缺失");
            return;
        }
        resp.setStatus(Integer.parseInt(statusString));
        resp.getWriter().write("status: "+statusString);
    }
}

效果

Fiddler抓包

自动刷新

 HTTP响应中可以设置一个header,Refresh的值就是刷新的间隔时间(s)

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("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        //设置一个毫秒级时间戳
        resp.setHeader("Refresh","1");
        long timeStamp = System.currentTimeMillis();
        resp.getWriter().write("time: " + timeStamp);
    }
}

效果

 

重定向

重定向就是呼叫转移

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("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("https://www.baidu.com/");
    }
}

效果

Cookie和Session

Cookie是服务器通过HTTP响应的Set-Cookie字段来设置的,返回给浏览器的。

Cookie是浏览器通过域名/地址存储的,在下次请求中,会自动被添加到请求里,发送给服务器。

Cookie是键值对存储的,这个键值对由程序员自己定义

Session是服务器存储的,像一个简单的hash表,key就是sessionId,value就是程序员自己定义的存储用户身份信息的数据

核心方法

HttpServletRequest类

HttpSession getSession()功能:

尝试根据当前请求中的sessionId获取到当前的session

1.如果存在就返回;

2.如果不存在就创建:

创建一个HttpSession对象,作为value

创建一个sessionId作为key

把key和value插入到hash表中

同时把这个sessionId通过Set-Cookie字段返回给浏览器

HttpServletResponse类

HttpSession类中的相关方法

一个HttpSession对象里面包含多个键值对

Cookie类中的相关方法

每个Cookie对象就是一个键值对

实现用户登录

IndexServlet.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 javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //根据用户请求中的sessionId,获取到用户信息并显示在页面上
        resp.setContentType("text/html;charset=utf-8");
        //1.判断当前用户是否已经登录
        HttpSession httpSession = req.getSession(false);
        if(httpSession==null){
            //如果当前会话不存在,则说明用户尚未登录,重定向到login.html
            resp.sendRedirect("login.html");
            return;
        }
        //2.如果用户已经登录,就可以从HttpSession中拿到用户信息了
        String username = (String) httpSession.getAttribute("username");
        Integer loginCount = (Integer) httpSession.getAttribute("loginCount");
        loginCount=loginCount+1;
        httpSession.setAttribute("loginCount",loginCount);
        //3.返回一个html页面
        StringBuilder html = new StringBuilder();
        html.append("<div>用户:"+username+"</div>");
        html.append("<div>访问次数"+loginCount+"</div>");
        resp.getWriter().write(html.toString());
    }
}

LoginServlet.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 javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        //1.先从请求的body中读取用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //2.判定用户名和密码是否正确
        if(!"root".equals(username)||!"123456".equals(password)){
            //登录失败
            resp.getWriter().write("登陆失败");
            return;
        }
        System.out.println("登录成功");
        //3.登录成功则创建会话
        //会话是根据请求中的sessionId查的,sessionId是在cookie中的
        //因为此处是首次登录,请求中还没有cookie,所以就会走”新建会话“这样的流程
        //同时进行操作,创建出一个HttpSession对象作为value,创建出一个sessionId作为key
        //把key和value存入到哈希表中
        //同时把生成的sessionId作为set-cookie字段返回给浏览器
        HttpSession httpSession = req.getSession(true);
        //此时存入程序员自定义的数据
        httpSession.setAttribute("username","root");
        httpSession.setAttribute("loginCount",0);
        //4.让页面跳转至主页,用重定向的方式实现
        resp.sendRedirect("index");
    }
}

login.html

<!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>登录页面</title>
</head>
<body>
    <form action="login" method="post">
        用户名:<input type="text" name="username">
        <br>
        密码:<input type="password" name="password">
        <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

抓包查看

login请求

login响应

index请求

index响应

上传文件

核心方法

HttpServletRequest类

Part类

实现提交图片

UploadServlet

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        //1.从req对象中读取Part对象
        Part part = req.getPart("MyImage");
        //2.读取Part对象中的一些参数
        System.out.println(part.getSubmittedFileName());//上传文件的真实文件名
        System.out.println(part.getContentType());//文件类型
        System.out.println(part.getSize());//文件大小
        //3.把文件列入到指定目录中
        part.write("C:/files/MyImage.jpg");
        //4.返回一个响应
        resp.getWriter().write("upload ok");
    }
}

upload.html

<!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>上传图片</title>
</head>
<body>
    <form action="upload" method="POST" enctype="multipart/form-data">
        <input type="file" name="MyImage">
        <input type="submit" value="提交图片">
    </form>
</body>
</html>

响应

IDEA控制台

请求

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值