Servlet,session(Tomcat提供的API接口)

servlet概论

servlet是tomcat给我们提供的一组操控HTTP协议的API。
Servlet 就是 Tomcat 这个 HTTP 服务器提供给 Java 的一组 API, 来完成构建动态页面这个任务。

Servlet 主要做的工作:

  • 允许注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类中的一些代码.。
  • 解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象。
  • 构造 HTTP 响应.,只要给指定的 HttpResponse 对象填写一些属性字段, Servlet 就会自动的安装 HTTP
    协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端.。

创建servlet

创建一个项目Maven

maven是一个IDEA的构建工具,可以帮助我们去构建测试,打包一个项目。
在这里插入图片描述
我这里是2022版本,或许和各位的界面不同,但是大同小异。

一个maven项目会有一个pom.xml的配置文件。
在这里插入图片描述
不同的maven有不同的坐标,一般不发布到maven的中央仓库其实不需关注。
在这里插入图片描述
目录结构
在这里插入图片描述

引入依赖

从maven中央仓库中下载安装。
中央仓库链接
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
复制完后放入pom.xml中就好了
不过要注意的是在pom.xml中增加一个dependencies的标签,然后将内容放在这个标签下就好。还有一点要注意,这个标签可以放入n个像servlet这样的依赖。

在这里插入图片描述
如果第一次使用maven,会显示红色。但是等依赖下载完毕就好了。
而下好的依赖的在一个影藏文件中,在电脑中的自己的用户目录里的.m文件中

在这里插入图片描述
下载好后就不需要自己
在这里插入图片描述

创建目录

在这里插入图片描述
这web.xml是给Tomcat的看的。相当于这个目录是给前端看的。

而里面的内容是固定的,粘贴我发的就好。

<!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>

复制的时候记住选择纯文本粘贴,不然小心粘贴一些特殊字符。

实现代码

public class hollewold extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
}

doget:要完成的工作,就是根据请求,计算生成响应。
在这里插入图片描述

  • http请求:tomcat收到请求,就会把这个请求按照http协议生成一个对象,而这个对象的的各个属性就是http的各个信息。
    http响应:此处的响应对象是一个空的响应对象,需要我们根据业务逻辑设置响应信息,在doget中。比如body和header。只要把属性设置到这个resp对象中,tomcat就会自动响应对象,然后构造一个http响应字符串通过socket返回客户端。
@WebServlet("/hello")
public class hollewold extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //服务端在控制台中显示
        System.out.println("hello world");
        //在客户端中显示
        resp.getWriter().write("hello world");//字符流
    }
}

resp.getWriter().write(“hello world”);这句就会在html中的body中显示

  1. 创建一个类 HelloServlet , 继承自 HttpServlet
  2. 在这个类上方加上@WebServlet(“/hello”) 注解, 表示 Tomcat 收到的请求中, 路径为 /hello
    的请求才会调用 HelloServlet 这个类的代码.
  3. 重写 doGet 方法. doGet 的参数有两个,分别表示收到的 HTTP 请求 和要构造的 HTTP 响应. 这个方法会在 Tomcat 收到 GET 请求时触发
  4. HttpServletRequest 表示 HTTP 请求. Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成了一个HttpServletRequest 对象. 后续想获取请求中的信息(方法, url, header, body 等)
  5. 都是通过这个对象来获取. HttpServletResponse 表示 HTTP 响应. 代码中把响应对象构造好(构造响应的状态码, header, body 等)
  6. resp.getWriter() 会获取到一个流对象, 通过这个流对象就可以写入一些数据,写入的数据会被构造成一个 HTTP 响应的 body 部分, Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器。

规则:

  1. 创建的类需要继承自 HttpServlet
  2. 这个类需要使用 @WebServlet 注解关联上一个 HTTP 的路径
  3. 这个类需要实现doXXX 方法

打包

部署代码到tomcat,但是先需要打包。
在java中通常打包为jar,war。
而这里打包成war部署给tomcat的程序。war是tomcat的专属格式。

在这里插入图片描述
打包操作:

  1. 检查代码中是否存在一些依赖依赖,是否下载好
  2. 把代码进行编译,生成一堆.class文件。
  3. 这些点class文件以及web.xml,按照一定的格式进行大包。

在这里插入图片描述
打包成功

在这里插入图片描述
可以看见我们这里已经成功打包,但是要注意我们打包是.jar,不是.war,所以我们要对他命名。pom.xml中

在这里插入图片描述

  • packaging字段表示打包的格式
  • 下面的则是对打包进行命名
  • 然后重新打包。
    在这里插入图片描述

在这里插入图片描述

这里就成功了。打好的war包就是一个普通的压缩包,也可以用压缩软件查看,但是我们这里就将包给tomcat的webapps,然后他会在运行的时候自动解压缩。

验证程序

在这里插入图片描述
doGet表示必须要收到get请求,才能执行。
在这里插入图片描述
URL中hello_world是文件名,而hello则是@WebServlet(“/hello”)的hello。

URL:127.0.0.1:8080/文件名/@WebServlet(中的内容)

简化打包操作:下载smart tomcat的插件

在这里插入图片描述
在这里插入图片描述

使用Smart Tomcat插件:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这个呢和我们进行手动打包不一样,smart tomcat是会以项目名为url第一级目录,而手动以自己命名为url第一级目录。

Servlet的常见问题

404:表示你浏览器访问的资源,在服务器不存在。
1.你请求的路径写错了
2.路径写对了,但是war包没有被正确加载

405
1.发的请求的方法与代码不匹配
比如代码写的是doPost,你发的请求是个Get请求
2.虽然方法和代码匹配,但是忘了去掉super.doXXX.

500
意味着这你的服务器代码抛出了异常。

空白页面
意味着服务器没有返回数据。

Servlet API

1.HttpServlet

我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法.

方法名称调用时机
init在 HttpServlet 实例化之后被调用一次
destory在 HttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求的时候调用
doGet收到 GET 请求的时候调用(由 service 方法调用)
doPost收到 POST 请求的时候调用(由 service 方法调用)
doPut/doDelete/doOptions/…收到其他请求的时候调用(由 service 方法调用)

init方法:HTTPServlet被实例化(首次收到匹配的请求时,会调用到)会调用一次,这个方法用来做些实例化相关的工作。

destory方法:这个方法是webapp被卸载前执行一次用来做一些收尾工作。(不靠谱,不建议,使用8005管理端口,来停止服务器,此时destroy)(8005端口配置一些业务外的配置)

service方法:每次收到路径匹配的请求,都会执行。(doGet/doPost其实是在service中被调用的,一般不会重写service,只是重写doXXX)

Servlet的生命周期:
初始:init
中年使用:service
结束:destory

网页出现乱码问题:

  1. 数据返回的时候,自身是一种编码方式(IDEA默认UTF-8)
  2. 浏览器在显示的时候,根据系统默认编码走(Windows 10 gbk)
  3. 编码对不上就会乱码。

java的char类型的是Unicode编码类型,而String是utf-8,而字符转为字符串就会自动转码。

resp.setContentType(“text/html,charse=utf-8”);

使用这个,就可将告诉浏览器,返回的数据是utf8格式

HttpServletRequest

一个HTTP请求,Tomcat收到请求后,就会解析生成HttpServletRequest的对象。而这个对象就会生成http报文里的参数。

方法描述
String getProtocol()返回请求协议的名称和版本。
String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。
String getContextPath()返回指示请求上下文的请求 URI 部分。
String getQueryString()返回包含在路径后的请求 URL 中的查询字符串。
Enumeration getParameterNames()返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。
String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回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 对象.

URI:唯一资源标识符
URL:唯一资源定位符

其实使用差别不大,URI可以当成URL使用。

getParameter

最常用的API之一
前端给后端传递数据,是非常常见的需求。

  1. 通过 query string 传递
    query string 中的键值对,都是我们自己定义的;
    比如我们定义了username,password;
    在这里插入图片描述
@WebServlet("/getParameter")
public class getParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username=req.getParameter("username");
        String password=req.getParameter("password");
        if (username==null){
            System.out.println("没有username");
        }
        if (password==null){
            System.out.println("没有password");
        }
        System.out.println(username+" "+password);
        resp.getWriter().write("ok");
    }
}
  1. 通过body传递(form)
    相当于body里存的数据的格式就和query string一样,但是content-type是application/x-www-form-urlencoded,此时也是通过getParameter获取键值对。
 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //给请求设置
        req.setCharacterEncoding("utf8");
        String username=req.getParameter("username");
        String password=req.getParameter("password");
        if (username==null){
            System.out.println("没有username");
        }
        if (password==null){
            System.out.println("没有password");
        }
        System.out.println(username+" "+password);
        resp.getWriter().write("ok");
    }

前端:

<form action="postParameter" method="POST">
    <input type="text" name="username">
    <input type="text" name="password">
    <input type="submit" value="提交">
</form>

乱码了就用:
req.setCharacterEncoding(“utf8”);
将前端传来的数据进行编码转换。

  1. 通过body(json)
    json格式也是键值对格式的数据,但是Servlet没有内置,我们还是要去中央库下载。

在这里插入图片描述
在这里插入图片描述

class User{
    public String username;
    public String password;
}

public class json extends HttpServlet {
    private ObjectMapper objectMapper=new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        User user =objectMapper.readValue(req.getInputStream(),User.class);

        System.out.println("username="+user.username+",password"+user.password);
        resp.getWriter().write("ok");
    }
}

readValue做的事
1.解析json字符串,转换成若干个键值对。
2.根据第2个参数user.class。反射的时候信息从点plus文件中获取。
3.遍历属性,根据属性的名字(通过反射),去上述准备好的键值对里查询。看看这个属性名字是否存在对应的value,如果存在,就把value赋一直到该属性中。

HttpServletResponse

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

用header实现自动刷新
给HTTP响应 中,设置Refresh时间

@WebServlet("/refresh")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     resp.setHeader("Refresh","1");
      resp.getWriter().write("time"+System.currentTimeMillis());

    }
}

通过这个代码我们就可以每隔一秒就刷新一下数据

实现重定向

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(302);
        resp.setHeader("Location","https:www.baidu.com");
        //同等
        resp.sendRedirect("https:www.baidu.com");
    }
}

实现一个表白墙(服务端)(你们可以不用看,这个代码主要是我复习用的)

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.cj.jdbc.MysqlDataSource;

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.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

class Message{
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper=new ObjectMapper();
    private List<Message> messageList=new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过这个方法来处理,获取所有留言信息
        //需要返回一个json字符串数组,jackson 直接帮我们处理好了格式
            List<Message> messageList=load();
          String respString= objectMapper.writeValueAsString(messageList);
          resp.setContentType("application/json;charset=utf8");
          resp.getWriter().write(respString);
    }



    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //处理提交的新信息
        Message message= objectMapper.readValue(req.getInputStream(),Message.class);
        save(message);
        System.out.println("消息提交成功!massage"+message);
        //响应只是返回200报文,body为空,此时不需要额外处理,默认返回是200

    }
//    读取数据
    private List<Message> load() {
        List<Message> messageList=new ArrayList<>();
        DataSource dataSource=new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/BiaoBai?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("xietian1314");

        try {
            Connection connection=dataSource.getConnection();
            String sql="select * from messge";
            PreparedStatement statement=connection.prepareStatement(sql);
            ResultSet resultSet=statement.executeQuery();

            while (resultSet.next()){
                Message message=new Message();
                message.from=resultSet.getString("from");
               message.to= resultSet.getString("to");
               message.message= resultSet.getString("messge");
               messageList.add(message);
            }
            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return messageList;
    }
    //往数据库写数据
    private void save(Message message) {
        DataSource dataSource=new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/BiaoBai?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("xietian1314");

        try {
            Connection connection=dataSource.getConnection();
            String sql="insert into message valuses(?,?,?)";
            PreparedStatement statement=connection.prepareStatement(sql);
            statement.setString(1,message.from);
            statement.setString(2,message.to);
            statement.setString(3,message.message);
            statement.executeUpdate();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

链接了数据库。
小结:
很多网站,都是一套逻辑。

  1. 约定前后点的交互接口
  2. 实现服务器代码(通常可以用文件或者数据库存储数据)
  3. 实现客户端代码(通常会用ajax 构造请求,并使用一些js的webAPI操作页面内容)

session回话

Cookie保存用户身份标识,此时身份标识如何分配,以及省份信息具体如何存储(Session)会话,都是需要服务器的支持。

session:会给用户分配一个sessionID(令牌)同时记录下当前的一些身份信息(可以自定义),然后这个sessionID(令牌)就会被返回到浏览器的cookie中,然后浏览器访问服务器都会带着这个SessionID(令牌),然后服务器就可以识别出当前的用户身份。

在这里插入图片描述

方法描述
HttpSession getSession()在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null
Cookie[] getCookies()返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对.

(实现登录功能很重要)

getSession有一个参数boolean类型。

  • 如果参数为false,getSession的行为是:
  1. 读取请求中cookie里的session ID。
  2. 在服务器这边根据session ID来查询对应的session对象。
  3. 如果查到了就会返回这个session对象,如果没有查到就返回null。

(实现登录功能很重要)

如果参数为true。getSession的行为是:

  1. 读取请求中cookie的sessionID。
  2. 在服务器这边根据session ID来查询对应的session对象。
  3. 如果查到了,就会返回这个session对象。
  4. 如果没有查到,就会创建一个session对象,同时生成一个session ID,把session ID为key,session 对象为value。把键值对存储到服务器里的一个哈希表中同时把session ID,同时set-cookie的方式返回给浏览器。

实现一个登录功能:
页面(前端,web):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" value="username">
        <input type="password" value="password">
        <input type="submit" value="登录">
    </form>
</body>
</html>

服务器(登录页)

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.先从请求中拿到用户名密码
        String username=req.getParameter("username");
        String password=req.getParameter("password");
        req.setCharacterEncoding("utf8");
        //验证密码
        if (username==null|| password==null ||username.equals("")||password.equals("")) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前输入的用户名或者密码不为空");
            return;
        }

        if (!username.equals("zhangsan")&&!username.equals("lisi")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名或密码有误");
            return;
        }

        if (!password.equals("123456")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名或密码有误");
            return;
        }
        //验证密码和用户名,创建一个Session会话。
        //当前用户处于未登录的状态,此时请求的cookie中没有SessionID
        HttpSession session= req.getSession(true);
        //存储键值对,在之后服务器进行验证
        session.setAttribute("username",username);
        //登录成功后自动跳转
        resp.sendRedirect("index");

    }
}

服务器(响应页)

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //此处静止创建会话,如果没有找到,认为用户是未登录的状态
        //如果找到了才会认为是登录状态
        HttpSession session =req.getSession(false);
        if(session==null){
            //未登录的状态
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        String username=(String) session.getAttribute("username");
        if (username==null) {
            //虽然会话对象,但是里面没有必要的属性,也认为是登录状态异常。
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        //上述都没有问题,接下来就直接生成一个动态页面
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("欢迎你"+username);
    }
}

(部署到tomcat) Session ID也不会一直存在下去。比如服务器重启原来的session表中的内容就没有了,再次访问的时候就可能出现session ID无法查询到,于是就被识别成未登录状态了。
在IDEA中,为了方便调试程序,smart Tomcat,会在停止服务器的时候将会话持久保存,并且在下次启动的时候。自动会把会话恢复到内存中,此时绘画不会丢失。 Session ID也能查到。

Servlet上传文件

HttpServletRequest 类方法

方法描述
Part getPart(String name)获取请求中给定 name 的文件
Collection getParts()获取所有的文件

Part 类方法

方法描述
String getSubmittedFileName()获取提交的文件名
String getContentType()获取提交的文件类型
long getSize()获取文件的大小
void write(String path)把提交的文件数据写入磁盘文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值