【Servlet】HttpServletRequest、HttpServletResponse

目录

🎁1 HttpServletRequest 

💥1.1 通过 query string 来进行传递

🐷1.2 通过 body (form) 来进行传递

🛸1.3 通过 body(json) 来进行传递

🍘2. HttpServletResponse

👶2.1 为响应设置状态码 200

🍝2.2 header 实现自动刷新效果

🌏2.3 跳转到 搜狗页面

🚑3. 表白墙

🏳‍🌈3.2 小结


1 HttpServletRequest 

前端给后端传递数据是非常常见的需求。HttpServletRequest 这个类,主要用来获取到请求的各个方面的信息,尤其是前端传来的自定义数据,如下文介绍的 query string、post body(form)、post body(json) 等。getParameter 是 Servlet 最常用的接口之一。

1.1 通过 query string 来进行传递

以下代码,前端通过 query string 传递 username 和 password 。query string 中的键值对是程序员自定义的!在 URL 中,键值对是通过字符串来表示的,tomcat 收到请求之后,自动把 url 中的键值对转换成 Map,方便人们查询。

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 GetParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 前端通过 url 中的 query string 传递 username 和 password 两个属性
        String username = req.getParameter("username");
        if(username == null){
            System.out.println("username 这个 key 在 query string 中不存在!");
        }

        String password = req.getParameter("password");
        if(password == null){
            System.out.println("password 这个 key 在 query string 中不存在!");
        }

        System.out.println("username = " + username +", password = "+ password);

        resp.getWriter().write("ok!");
    }

    
}

 

 URL 的键值对中,value 的值是中文可以吗?

在 URL 中,query string 如果包含中文或者特殊字符,务必使用 urlencode 的方式进行转码!!!如果直接写中文或者特殊符号,会存在非常大的风险!!!!在不转码的情况下,有些浏览器或者 http 服务器下,对中文支持不好的话,就会出现问题。

比如 张三,就需要转码成  %E5%BC%A0%E4%B8%89%0A

1.2 通过 body (form) 来进行传递

body 里存的数据格式,就和 query string 一样,但其 Content-Type 是 application/x-www-form-urlencoded,此时也是通过 getParameter 来获取键值对!

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        // 前端通过 body,以 form 表单的格式,把 username 和 password 传给服务器
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        if(username == null){
            System.out.println("username 这个 key 在 query string 中不存在!");
        }

        if(password == null){
            System.out.println("password 这个 key 在 query string 中不存在!");
        }

        System.out.println("username = " + username +", password = "+ password);

        resp.getWriter().write("ok!");
    }

由于是个一个 Post 请求,使用 Postman 来构造。

 

 如果 post 请求,发送的也是中文,结果会怎么样?

 出现了乱码,那怎么样才能传入中文呢?

 只需在 doPost 方法内,添加以下代码即可,指定传入参数的字符编码(给请求):

        req.setCharacterEncoding("utf8");

1.3 通过 body(json) 来进行传递

这是未来开发中,最常用的开发方式。

json 中的数据是以键值对格式存在,由于 Servlet 不能解析 json,因此需要引入第三方库。这样子的第三方库有许多,常见的有:fastjson、gson、jackson 等,其中 jackson 属于 spring 官方指定产品。本篇文章中,使用 jackson。

Maven 中央仓库中,搜索 jackson,点开 Jackson Databind:

随便点开一个版本:

复制粘贴以下这段代码到 pom.xml 中: 

打开 IDEA 中 maven 面板,刷新即可完成 jackson 的引入。

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;

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

@WebServlet("/json")
public class JsonServlet extends HttpServlet {
    // 使用 jackson,最核心的对象就是 ObjectMapper
    // 这个对象可以把 json 字符串解析成 java 对象;也可以把一个 java 对象转成一个 json 格式字符串.
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过 post 请求的 body 传递过来一个 json 格式的字符串
        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. 根据第二个参数 User.class,去找到 User 里所有 public 的属性,依次遍历(.class 是类对象,记载一个类里的各种信息)

3. 遍历属性,根据属性的名字,去已经解析的键值对里查询(通过反射这个机制完成,反射类似于照镜子,.class 的信息是人,通过照镜子,看到镜子里的自己)。查询属性名字是否存在对应的 value ,如果存在,就把 value 赋值给该属性。

 

2. HttpServletResponse

HttpServletResponse 这个类,表示一个 HTTP 响应。

方法描述
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 中写入二进制格式数据

直接给例子感受一下吧:

2.1 为响应设置状态码 200

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.setStatus(200);
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("返回 200 响应!");
    }
}

 

 

 

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

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 每隔 1 s 自动刷新一次
        resp.setHeader("Refresh", "1");
        resp.getWriter().write("time = "+System.currentTimeMillis());
    }
}

 

2.3 跳转到 搜狗页面

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.setStatus(302);
        resp.setHeader("location","https://sogou.com");
    }
}

 doGet 中的两行代码还可以简写成:

        resp.sendRedirect("https://sogou.com");

介绍了 HttpServlet 和上面两个类之后,我们就具备写一个网站的基础了。下面的一节中,就带着同学们一起来写一个简单的网站——表白墙。

3. 表白墙

在 JavaScript 这篇文章的最后,给了一个表白墙的例子。这个案例存在以下问题:1. 随着网页一关,数据会丢失。2. 数据只能在本地看。

解决上述问题,只需引入后端服务器即可。每次当用户打开页面时,服务器加载当前已经提交过的表白墙数据;每当用户新增一个表白时,让数据提交给服务器,让服务器持久化保存。

首先将写好的前端代码放在 Webapp 目录下,与 WEB-INF 是同级的:

 这样就可以通过以下方式访问:

接下来就可以写后端代码了,但在那之前,需要明确前后端交互接口,即网页给服务器发什么样的请求。请求具体怎么设定,规定一下字段、格式等;服务器给网页返回什么样的响应。响应具体怎么样设定,规定字段和格式等。

在表白墙这个例子中,前后端交互接口规定如下:

接口一:页面获取当前所有的留言信息

请求:

GET / message

  

响应:

HTTP/1.1 200 OK

Content-Type: application/json

[

    {

         from:"从哪里来的",

         to:"到哪里去",

         message:"消息是啥"

    },

    {

         from:"从哪里来的",

         to:"到哪里去",

         message:"消息是啥"

    },

    {

         from:"从哪里来的",

         to:"到哪里去",

         message:"消息是啥"

    },

]

json 中使用 [ ] 表示数组,[ ] 中的每个元素,是一个 { } json 对象,而每一个对象中,又有三个属性,分别是 from、to 以及 message。

接口二:提交新消息给服务器

请求:

POST / message

Content-type: application/json

{

    from:"从哪里来",

    to:"到哪里去",

    message:"消息是啥"

 

响应:

HTTP/1.1 200 OK

先写一个大概的:

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

class Message{
    // 这几个属性必须设置成 public 的!!
    // 如果设置 private,必须生成 public 的 getter 和 setter!!

    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 LoveWall 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 直接帮我们处理好了格式
        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);
        messageList.add(message);
        System.out.println("消息提交成功!message = "+message);
        // 响应只返回 200 报文,body 为空,此时不需要额外处理,默认就是返回 200 的
    }
}

 利用 postman 验证上述程序的可行性:

 

 在前端代码中,加入以下内容:

<script>
function submit(){
......
......
 // 4. 通过 ajax 构造 post 请求,把这个新的消息提交给服务器
            // 此处的 body 是一个 js 对象
            // js 对象通过 {} 的方式来表示
            // {} 里面是一组键值对
            // 键值对中的键,固定就是 String 类型!
            let body = {
                from:from,
                to:to,
                message:msg
            };

            jQuery.ajax({
                type:'post',
                url:'message',
                contentType:"application/json;charset:utf8",
                data:JSON.stringify(body),
                success:function(body){
                    // 响应成功返回之后,调用的回调函数
                    console.log("消息发送给服务器成功!");
                }
            });
}

....

// 在页面加载的时候,希望从服务器获取所有的信息,并显示在网页中
        jQuery.ajax({
            type:'get',
            url:'message',
            // body  是收到响应的正文部分,是 json 数组
            // 由于响应的 Content-Type 是 application/json,此时收到的 body 会被 jquery 自动地从 字符串
            // 转换成 js 对象数组,此处就不需要手动进行 JSON.parse 了
            // 此处的 body 已经是一个 JSON.parse 之后得到的 js 对象数组了
            success:function(body){
                // 标记获取已有信息放置的地方
                let container = document.querySelector('.container');
                for(let message of body){
                    let rowDiv = document.createElement('div');
                    rowDiv.className = "row";
                    rowDiv.innerHTML = message.from + "对"+message.to+"说"+message.message;
                    container.appendChild(rowDiv);
                }
            }
        });
</script>

 JS 中使用 JSON 这个特殊对象,完成对象和 json 字符串的转换:

JSON.stringify 把 js 对象转换成 json 格式字符串

JSONparse 把 json 格式字符串转成 js 对象。

在服务器不关闭的情况之下,多次刷新页面,之前提交的内容依旧存在:

根据前面的代码,我们知道服务器的数据是保存在一个 ArrayList 中的,即数据保存在内存中的。当服务器关闭再开启,内存数据就没有了。所有,为了让数据更好的持久化,最好把数据写到硬盘上,要么写到文件里,要么写到数据库中。下面便介绍后一种方式。

再次来到 Maven 中央仓库,引入 MySQL 依赖:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>
mysql> create database lovewall charset utf8;
Query OK, 1 row affected (0.01 sec)

mysql> use lovewall;
Database changed

mysql> create table message(`from` varchar(100),`to` varchar(100),message varchar(1024));
Query OK, 0 rows affected (0.07 sec)

 修改之前的代码如下:

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

class Message{
    // 这几个属性必须设置成 public 的!!
    // 如果设置 private,必须生成 public 的 getter 和 setter!!

    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 LoveWall 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);
        //messageList.add(message);
        save(message);
        System.out.println("消息提交成功!message = "+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/lovewall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123");

        try {
            Connection connection = dataSource.getConnection();
            String sql = "select * from message";
            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("message");
                messageList.add(message);
            }

            resultSet.close();
            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return messageList;
    }

    // 这个方法用来往数据库中存一条记录
    private void save(Message message){
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/lovewall?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123");

        try {
            Connection connection = dataSource.getConnection();
            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);
            statement.executeUpdate();

            statement.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }


    }


}
mysql> select * from message;
+------------+------+----------+
| from       | to   | message  |
+------------+------+----------+
| 金刚葫芦娃 | 蛇精 | 还我爷爷 |
| 地球       | 火星 | hello    |
+------------+------+----------+
2 rows in set (0.00 sec)

这样一来,关闭服务器再打开时:

 

3.2 小结

写网站的套路总结如下:

1. 约定前后端交互接口

2. 实现服务器代码(通常会操作数据库)

3. 实现客户端代码(通常使用 ajax 构造请求,使用一些 js 的 webapi 操作页面内容)


 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值