Java Servlet(三)--- 写一个简单的网站,表白墙程序,登录功能的实现

编写代码

写一个MessageWall(表白墙)的网站

提交消息

  1. (1) 先写前端代码,发送请求
    (2) 再写后端代码,解析请求,构造响应
    (3) 最后写前端代码,解析响应

把刚才的网页放到 webapp 目录中
tomcat 这样的一个项目,可以包含一些html,css,js
这些内容都是在webapp目录中的

第一级目录是Context path,第二级目录是html这个文件名(html)了
在这里插入图片描述

编写前端代码,发送请求

编写前端代码,发送http post请求
构造如下这样的请求:
在这里插入图片描述

  1. 使用ajax,需要引入jquery这个库(在搜索栏中搜jquery cdn这个网站)
    把第二个网址复制一下
    在这里插入图片描述

前端引入第三方库,往往就是通过 script 标签,写一个地址即可
放入html代码中的这里:
在这里插入图片描述
2. 构造请求并发送请求
在这里插入图片描述
(1) 让前端发起一个 ajax 请求
这个代码在用户点击按钮的回调函数中,会在点击按钮值之后被调用到
前端ajax请求,url路径,写作 message,前面不带 /,此时这是一个相对路径的写法
后端处理ajax请求,url路径,写作 /message,前面带 /,此时是Servlet要求的写法
在这里插入图片描述

(2) 服务器读取上述请求,并计算出响应
前端发来的json字符串,需要使用jackson读取到前端这里的数据,并且进行解析

要确保java代码中类的属性的名字和json中属性的名字保持一致
在这里插入图片描述
读取请求,处理响应的流程:
在这里插入图片描述

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

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

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

public class MessageServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    private ArrayList<Message> meassageList = new ArrayList<>();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据,把这个数据保存到服务器中
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        System.out.println("请求中收到的message " + message);
        // 最简单的方式就是存储在内存中,可以使用一个集合类,把所有收到的message都保存在内存中
        // 保存在内存中并不是一个合理的方法,后续一旦重启服务器,数据就丢失了
        // 相比之下,把数据存储在持久化的数据库中是更合理的
        meassageList.add(message);
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");
    }
}

(3) 回到前端代码,处理服务器返回的响应
在这里插入图片描述
请求的body和响应的body是不一样的
在这里插入图片描述
为了和请求对应上,一般服务器返回的数据,也会按照json格式来组织数据
在这里插入图片描述
前端把日志打印到控制台上
在这里插入图片描述

提交消息的过程(结果)

  1. 不能在浏览器里直接访问message这个路径(message这个路径是按照post请求来处理的,此时还没到post请求这个时机),开始是get方法需要获取这里的网页
    在这里插入图片描述
    post请求处理的时机(在用户点击按钮时才能访问这个message这个路径):
    在这里插入图片描述
    服务器收到的请求和浏览器收到的响应:
    在这里插入图片描述

相对路径会被转化为绝对路径
在这里插入图片描述
前端代码构造出一个请求(json字符串格式的)
在这里插入图片描述
后端这里返回一个响应:
在这里插入图片描述

页面加载时,能够在服务器中拿到数据到浏览器中显示出来

  1. 当前已经把数据提交到服务器保存了。目前还需要能够把服务器的数据拿回到客户端页面上,并显示。

页面加载的时候,发起这个get请求
在这里插入图片描述
为什么要从服务器拿消息?
1.当前页面上显示的数据,也是在浏览器内存中保存的。刷新页面/关闭了重新打开,之前的数据就没了。
2.其他的客户端打开页面也是有数据的

(1) 客户端在页面加载的时候,发起一个http请求
在这里插入图片描述
(2) 服务器收到这个请求,要处理这个请求并生成响应
在这里插入图片描述
把list里面的元素都转成json格式的字符串了
在这里插入图片描述
要确保这几个代码的执行顺序,setStatus和setContentType必须在getWriter前面,否则可能不会生效(构造出一个非法的http响应报文)这个事情可以认为是Servlet bug
几行代码就可以搞定
在这里插入图片描述
(3) 客户端收到响应,就需要针对响应数据进行解析处理。把响应中的信息,构成页面元素(html片段),并显示出来。
这里是最复杂的代码,需要拼接出html片段
在这里插入图片描述

总结

前后端交互的总体逻辑
在这里插入图片描述
页面加载
在这里插入图片描述
把返回的数据构造成html片段,显示到网页上
在这里插入图片描述
把messageList里的数据返回给浏览器
在这里插入图片描述
点击按钮,提交消息
在这里插入图片描述
把这个数据保存到messageList中在这里插入图片描述

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.util.ArrayList;

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 ArrayList<Message> meassageList = new ArrayList<>();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据,把这个数据保存到服务器中
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        System.out.println("请求中收到的message " + message);
        // 最简单的方式就是存储在内存中,可以使用一个集合类,把所有收到的message都保存在内存中
        // 保存在内存中并不是一个合理的方法,后续一旦重启服务器,数据就丢失了
        // 相比之下,把数据存储在持久化的数据库中是更合理的
        meassageList.add(message);
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");

//        resp.setContentType("application/json");
//        resp.getWriter().write("{ ok : true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        String respJson = objectMapper.writeValueAsString(meassageList);
        resp.getWriter().write(respJson);
    }
}

把消息数据存储到数据库中

如果把服务器关闭,再刷新页面,消息就消失了,因为消息是存在内存中的,这里就需要引入数据库保存这些消息

如何把消息数据存储到数据库中
把数据库引入到代码中

  1. 引入数据库的依赖

  2. 建库建表
    建库建表,需要用到sql,都可以写到文件中,后续如果需要把表什么的往其他的机器上迁移,建表操作就会比较方便

create database if not exists MessageWall charset utf8;

use message_wall;

-- 删表的目的是为了,防止之前数据库中有一样的表,对我们的代码产生影响
drop table if exists massage;
-- from和 to 是sql中的关键字,可能执行不了,所以加上反引号
create table message(`from` varchar(1024),`to` varchar(1024),message varchar(1024));
  1. 编写数据库代码
    JDBC
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.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 javax.xml.transform.Result;
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 DataSource dataSource = new MysqlDataSource();
    // private MysqlDataSource mysqlDataSource = new MysqlDataSource();
    @Override
    public void init() throws ServletException{
        // 1. 创建数据源
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message_wall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        // TODO 找了很久的问题,原来是数据库密码不一致的问题,导致登录的不是这个数据库(123456)
        // TODO 登上了其他的数据库(123)
        ((MysqlDataSource)dataSource).setPassword("123456");
        // this.dataSource = mysqlDataSource;
    }

    // 引入数据库,就不需要meassageList了
    // private ArrayList<Message> meassageList = new ArrayList<>();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据,把这个数据保存到服务器中
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        System.out.println("请求中收到的message " + message);
        // 最简单的方式就是存储在内存中,可以使用一个集合类,把所有收到的message都保存在内存中
        // 保存在内存中并不是一个合理的方法,后续一旦重启服务器,数据就丢失了
        // 相比之下,把数据存储在持久化的数据库中是更合理的

        // 插入数据库
        try {
            save(message);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        // meassageList.add(message);
        // 返回一个响应
        resp.setStatus(200);
        resp.getWriter().write("ok");

//        resp.setContentType("application/json");
//        resp.getWriter().write("{ ok : true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json; charset=utf8");
        // 从数据库查询
        List<Message> messageList = null;
        try {
            messageList = load();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        String respJson = objectMapper.writeValueAsString(messageList);
        resp.getWriter().write(respJson);
    }



    private void save(Message message) throws SQLException {
        // 通过这个方法把message插入到数据库中

        // 2.建立连接
        Connection connection = dataSource.getConnection();

        // 3.构造sql
        String sql = "insert into message values(?,?,?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1,message.from);
        statement.setString(2,message.to);
        statement.setString(3,message.message);

        // 4. 执行sql
        statement.executeUpdate();

        // 5. 回收资源
        statement.close();
        connection.close();
    }

    private List<Message> load() throws SQLException {
        // 通过这个方法从数据库中读取到 message

        // 1. 建立连接
        Connection connection = dataSource.getConnection();

        // 2. 构造sql
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 3. 执行sql
        ResultSet resultSet = statement.executeQuery();

        // 4. 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while(resultSet.next()){
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }
        // 5. 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 6. 返回这里的messageList
        return messageList;
    }
}

Cookie和Session

  1. Cookie:http请求header中的一个属性
    在这里插入图片描述
    比如:你去医院看病,你的就诊卡就是cookie,就诊卡里就存储了用户的身份信息(身份标识,也只有身份id),通过就诊卡刷卡,就可以在医生的电脑上显示你的完整的基本信息了
    在这里插入图片描述
  2. session:服务器存储数据的机制(不算持久化存储)
    Cookie:是客户端存储数据的机制
    这两者往往会相互配合使用,在cookie中存储的用户身份标识,也经常会理解成sessionId
    每个用户都有一个自己的session,也有不同的sessionId(服务器会存储很多的session)

在这里插入图片描述
用户可以通过就诊卡(cookie),就诊卡中就存有一份身份标识(sessionId),通过身份标识可以在服务器中找到它的基本信息(Session),Session中可以存储用户的自定义的信息

描述一下上面的过程:
在这里插入图片描述
我们学过的键值对结构:
在这里插入图片描述
3. 通过Servlet api来操作上述结构,Cookie是浏览器的机制,Servlet提供了api获取到Cookie。
Session 是服务器的机制,Servlet内部已经实现好了,也提供了api可以让我们进行使用

(1) HttpServletRequest
在这里插入图片描述
HttpSession getSession()
在这里插入图片描述
(2) HttpServletResponse
服务器给浏览器返回一个响应的信息,使用addCookie
在这里插入图片描述
(3) HttpSession
在这里插入图片描述
(4) Cookie
在这里插入图片描述

用户登录

基于上述的api实现 用户登录的功能
cookie / session 机制,其中很重要的作用,就是辅助完成登录功能的实现

  1. 登录页面(html)
    用户发起一个HTTP请求,触发登录逻辑(带上用户输入的用户名和密码)
    在这里插入图片描述
  2. 通过一个Servlet处理上述的登录请求
    通过这个Servlet读取用户名和密码,并且验证是否登录成功

如果登录成功,就会给当前这个用户,创建一个会话(保存用户当前的信息),并且把得到的sessionId,通过cookie返回给客户端。(客户端就把cookie保存起来了)

  1. 网站主页,通过另一个Servlet生成的动态页面。在这个页面中,就会把刚才这里的用户数据给显示到页面上。

比如是使用 zhangsan 用户来登录,主页上就会显示 欢迎你zhangsan 这样的标语

登录页面(html)

  1. 先写一个登录页面
    会构造一个形如下面的页面

在这里插入图片描述
预期发送的请求:
在这里插入图片描述

写一个Servlet处理请求

  1. equals方法的另一种使用方式
    在这里插入图片描述

  2. 创建一个session对象,getSession在背后做的事情
    在这里插入图片描述

  3. Attribute的作用
    在这里插入图片描述

  4. 通过Servlet获取不同浏览器中的数据

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

  1. 重定向跳转到index这个页面上(后续会写一个Servlet生成这个页面)
    在这里插入图片描述
package login;

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 {
        // 1. 读取请求传来的数据(用户名和密码)
        // 最好先设置一下字符集,否则如果usename是中文的话,此处 getParameter 可能会乱码
        req.setCharacterEncoding("utf8");
        String usename = req.getParameter("usename");
        String password = req.getParameter("password");

        // 2.验证用户名密码,是否是正确的,一般来说,验证用户名和密码是要通过数据库的
        // 此处为了简单一点,使用固定的用户名和密码的方式,比如此处假设用户名是zhangsan 密码是 123

        // 此处还需要注意,如果getParameter拿到的值时null,为了避免空指针异常,下面这种比较方式是更合适的写法
        if(!"zhangsan".equals(usename) || !"123".equals(password)){
            // 登录失败
            // 给用户返回一个提示
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("用户名或密码错误");
            return;
        }

        // 3.登录成功了,给用户创建一个会话出来
        // 在会话中可以保存一些自定义的数据,通过Attribute的方式来保存
        HttpSession session = req.getSession(true);
        // 此处Attribute 也是键值对,这里的内容存储什么都可以,是程序员自定义的
        // 这样的数据存储好之后,后续跳转到其他页面,也是随时可以把这里的数据从会话中取出来的
        session.setAttribute("usename",usename);
        session.setAttribute("loginTime",System.currentTimeMillis());

        // 4.此处相当于登录成功,让页面跳转到网站首页
        resp.sendRedirect("index");
    }
}

实现登录后的主页

  1. 获取当前用户的会话对象
    在这里插入图片描述
  2. getSession内部使用同一个sessionId,所以才能够拿到同一个session对象,这里是封装好的,所以我们体现的不那么明显

在这里插入图片描述
3. 这是两个不同的Servlet,一个是登录的页面,一个是登录后跳转的页面,不同的Servlet之间,数据的共享

在这里插入图片描述
4. 这里需要进行强转
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package login;

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;

// 通过这个Servlet生成一个主页
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      // 重定向操作会发送一个get请求
        // 1. 先获取当前用户对应的会话对象,生成的页面要根据当前的用户信息来构造
        HttpSession session = req.getSession(false);
        if(session == null){
            // 如果session不存在,或者 sessionId 在哈希表中没有找到
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("您当前还没有登录!");
            return;
        }

        // 2. 从会话中拿到之前存储的用户信息
        //    这里的强转,需要程序员自行保证,类型是靠谱的
        String usename = (String) session.getAttribute("usename");
        Long longTime = (Long) session.getAttribute("loginTime");

        // 3. 生成一个页面,把上述的数据显示到页面上
        // 构造一个显示的字符串
        resp.setContentType("text/html; charset=utf8");
        String respBody = "欢迎你 " + usename + "!" + "上次的登录时间为: " + longTime;
        resp.getWriter().write(respBody);
    }
}

总结上述的代码逻辑

  1. 点击登录后就会触发一个 POST请求

在这里插入图片描述
2. 到达服务器这边
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
getSession的作用:
在这里插入图片描述
3. 重定向到主页(index)
在这里插入图片描述

总结

  1. 利用Cookie和Session写一个登录的逻辑:

在这里插入图片描述
2. 登录时间过期的问题
在这里插入图片描述
上述就是一个网站的基本开发流程了!!!

评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值