Servlet响应【httpServletResponse】


前言:
Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应,然后把响应的数据设置到 HttpServletResponse 对象中;然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串,并通过 Socket 写回给浏览器

核心方法汇总

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

注意:

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

代码示例

1.setStatus 设置状态码

前端代码:

<body>
    <h3>设置状态码</h3>
    <input type="text" id="status">
    </br>
    <button onclick="setStatus()">提交</button>
</body>

<script>
    function setStatus() {
    
    
        // js中发请求: 1)ajax; 2)直接修改url
        let status = document.querySelector("#status");
        // 后端会设置这个文本框输入的值为响应状态码: 严格来说 需要验证,此处省略
        window.location.href = "response?status=" + status.value;
    }
</script>

后端代码:

@WebServlet("/response")
public class ResponseServletStudy extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取请求发送的 queryString数据: status=xxx
        String status = req.getParameter("status");
        resp.setStatus(Integer.parseInt(status));
        resp.getWriter().write("设置响应状态码成功");
    }
}

启动之后,打开页面:
(提交之后出现乱码,暂时不管)

在这里插入图片描述

使用 fiddler 抓包:

在这里插入图片描述
变换不同的 status 的值,就可以看到不同的响应结果

注意: 如果没有调用这个方法,默认返回200状态码(若正常执行,则没有异常;若出现异常,就返回500)

2.setHeader 设置响应头 [了解]

修改前端代码:

<body>
    <h3>设置状态码</h3>
    <input type="text" id="status">
    </br>
    <button onclick="setStatus()">提交</button>
    
    <h3>设置响应头</h3>
    <a href="response">设置</a>
</body>
<script>
    function setStatus() {
    
    
        // js中发请求: 1)ajax; 2)直接修改url
        let status = document.querySelector("#status");
        // 后端会设置这个文本框输入的值为响应状态码: 严格来说 需要验证,此处省略
        window.location.href = "response?status=" + status.value;
    }
</script>

修改后端代码:

@WebServlet("/response")
public class ResponseServletStudy extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取请求发送的 queryString数据: status=xxx
        String status = req.getParameter("status");
        // 若请求数据包含需要设置的状态码,才执行
        if(status != null) {
    
    
            resp.setStatus(Integer.parseInt(status));
            resp.getWriter().write("设置响应状态码成功");
        }
        // 设置响应头的键值对,键可以是标准的Http响应头的键,也可以是自定义的
        // 响应状态码是301,302,307,响应头有Location字段,才是重定向
        resp.setHeader("Location","http://www.baidu.com");
        resp.setHeader("username","花花");
    }
}

重新启动,刷新页面:

在这里插入图片描述
点击后,发现我们设置了 location,但并没有跳转,使用 fiddler 抓包:
发现状态码是200

在这里插入图片描述
注意: 若响应头 name 键已有,就会覆盖原有的键值对

3.addHeader 设置响应头 [了解]

和 setHeader 唯一不同的是:name键已有,不会影响添加一个新的

4.setContentType

设置响应头 Content-Type 的值,等同于 setHeader(“Content-Type”, String type)
因为 Content-Type 是标识 body 的数据格式,所以调用该方法还需要设置 body 的内容

  1. 返回简单的一个网页

修改前端代码:

<body>
    <h3>响应正文为简单的html网页</h3>
    <a href="html?type=1">查看</a>
</body>

后端代码:

@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
    
    
    // html?type=1...
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 响应html: 设置响应的Content-Type
        resp.setContentType("text/html;charset=utf-8");

        PrintWriter pw = resp.getWriter();

        // 获取queryString中type的值
        String type = req.getParameter("type");
        // 返回简单的 html
        if("1".equals(type)) {
    
    
            pw.println("<h3>获取网页成功</h3>");
        }
    }
}

重新启动,刷新页面,点击跳转:

在这里插入图片描述

  1. 返回一个动态网页(复杂的html)

前端代码:

<body> 
    <h3>响应正文为复杂的html(动态变化的)</h3>
    <input type="text" id="username" placeholder="输入用户名">
    </br>
    <button onclick="toWelcome()">跳转</button>
</body>

<script>
    function toWelcome() {
    
    
        let username = document.querySelector("#username");
        window.location.href = "html?type=2&username=" + username.value;
    }
</script>

后端代码:

@WebServlet("/html")
public class HtmlTypeServlet extends HttpServlet {
    
    
    // html?type=1...
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 响应html: 设置响应的Content-Type
        resp.setContentType("text/html;charset=utf-8");

        PrintWriter pw = resp.getWriter();

        // 获取queryString中type的值
        String type = req.getParameter("type");
        // 返回简单的 html
        if("1".equals(type)) {
    
    
            pw.println("<h3>获取网页成功</h3>");
        }
        // 返回复杂的动态html
        else if("2".equals(type)) {
    
    
            // html>type=2&username=
            String username = req.getParameter("username");
            pw.println("<p>");
            pw.println("欢迎你!!" + username);
            pw.println("</p>");
        }
    }
}

重新启动,刷新页面:

在这里插入图片描述
输入别的用户名尝试:

在这里插入图片描述
关于动态网页,在 Java 代码中,写很多的 html 代码,耦合性太强(两个完全不同的编程语言,放在一起开发),维护性、扩展性很差
解决方法: ①模板技术 (也存在一些问题,进一步发展就有了 ②ajax 技术的产生) (模板技术后边继续讲解)

返回已有的一个网页

1.重定向

前端代码:

<body>
    <h3>重定向到hello.html</h3>
    <a href="goto?type=1">跳转</a>
    <h3>转发到hello.html</h3>
    <a href="goto?type=2">跳转</a>
</body>

后端代码:

@WebServlet("/goto")
public class GoToServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // goto?type=xxx
        String type = req.getParameter("type");
        // 重定向
        if("1".equals(type)) {
    
    
            resp.setStatus(301);
            resp.setHeader("Location","hello.html");
            // 以上代码可以简化为 sendRedirect
//            resp.sendRedirect("hello.html");
        }
        // 转发
        else if("2".equals(type)) {
    
    
            req.getRequestDispatcher("hello.html").forward(req,resp);
        }
    }
}

重新启动服务,刷新页面:

在这里插入图片描述
点击重定向的跳转,使用 fiddler 抓包:

在这里插入图片描述

我们可以看到 url 地址栏的改变:

在这里插入图片描述
特点: ①url 地址栏会变;②发送两次请求
原理:
第一次返回 301 / 302 / 307 响应状态码,以及响应头 Location: 网页的地址;
第二次浏览器自动跳转到 Location 设置的地址

2.转发

前后端代码同上

点击转发的跳转,使用 fiddler 抓包:

在这里插入图片描述
观察 url 地址栏:

在这里插入图片描述

特点:①url 地址栏不变;②只有一次请求
原理: 当此请求 Servlet 时,由 Servlet 获取到转发路径的资源,把这个路径的内容设置到响应正文

返回一个文件

设置一下 Content-Type 和 Content-Length,然后把文件的二进制数据,放在响应正文即可

例1.渲染展示

以图片和音乐文件为例

前端代码:

<body>
    <h3>获取一个图片(渲染展示)</h3>
    <img src="file?type=photo&show=1">
    <h3>获取一个音乐(渲染展示)</h3>
    <audio src="file?type=music&show=1" controls></audio>
</body>

后端代码:

@WebServlet("/file")
public class FileServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        // 获取响应对象的字节输出流
        OutputStream os = resp.getOutputStream();

        // 返回的文件类型: photo-图片 music-音乐
        String type = req.getParameter("type");
        // 返回时的操作: 1-渲染; 2-下载
        String show = req.getParameter("show");

        File file = null;
        // <img src="file?type=photo&show=1">
        if("photo".equals(type)) {
    
    
            resp.setContentType("image/jpeg"); //jpg格式
            file = new File("保存在 resources 里的完整路径");
        }
        // <audio src="file?type=music&show=1" controls></audio>
        else if("music".equals(type)) {
    
    
            resp.setContentType("audio/mp3"); // MP3格式
            file = new File("保存在 resources 里的完整路径,如: D:\\xxx\\xxx\\Servlet-Study\\src\\main\\resources\\love.mp3");
        }
        // 返回一个文件类型: Content-Length, body
        byte[] data = Files.readAllBytes(file.toPath());
        resp.setContentLength(data.length); // 等同于 setHeader("Content-Length",xxx)
        os.write(data);
    }
}

可在 菜鸟教程 里查看 content-type 对照表:

在这里插入图片描述

启动服务,打开页面:
(此处 gif 格式,听不到声音)

在这里插入图片描述

例2.下载

修改前端代码:

<body>
    <h3>获取一个图片(下载)</h3>
    <a href="file?type=photo&show=2">下载</a>
    <h3>获取一个音乐(下载)</h3>
    <a href="file?type=photo&show=2">下载</a>
</body>

修改后端代码:

@WebServlet("/file")
public class FileServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        // 获取响应对象的字节输出流
        OutputStream os = resp.getOutputStream();

        // 返回的文件类型: photo-图片 music-音乐
        String type = req.getParameter("type");
        // 返回时的操作: 1-渲染; 2-下载
        String show = req.getParameter("show");

        File file = null;
        // <img src="file?type=photo&show=1">
        if("photo".equals(type)) {
    
    
            if("1".equals(show)) {
    
    
                resp.setContentType("image/jpeg"); //jpg格式

            }
            else if("2".equals(show)) {
    
    
                resp.setContentType("application/octet-stream");
            }
            file = new File("D:\\Bite\\JAVA\\JavaCode\\Servlet-Study\\src\\main\\resources\\doge.jpg");
        }
        // <audio src="file?type=music&show=1" controls></audio>
        else if("music".equals(type)) {
    
    
            if("1".equals(show)) {
    
    
                resp.setContentType("audio/mp3"); // MP3格式
            }
            else if("2".equals(show)) {
    
    
                resp.setContentType("application/octet-stream");
            }
            file = new File("D:\\Bite\\JAVA\\JavaCode\\Servlet-Study\\src\\main\\resources\\love.mp3");
        }
        // 返回一个文件类型: Content-Length, body
        byte[] data = Files.readAllBytes(file.toPath());
        resp.setContentLength(data.length); // 等同于 setHeader("Content-Length",xxx)
        os.write(data);
    }
}

重新启动,刷新页面:

在这里插入图片描述

打开文件:

在这里插入图片描述
修改文件后缀名:

在这里插入图片描述

思考: 图片、音乐、视频是静态文件,直接放在 webapp 下,就可以直接访问,还需要Servlet来返回吗?这样是否多此一举?
.
若文件总的大小非常大,放在 web 应用的 webapp 下就不合适了(之后打包比较费劲),使用 Servlet 去读取本地其他地方的文件来返回,就比较合适

返回 json 数据

常用于 ajax 请求,返回一些数据;用于动态的填充网页

后端代码:

@WebServlet("/ajax-response")
public class AjaxJsonServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        List<Message> messages = new ArrayList<>();
        Message m1 = new Message("杰杰子","花花","你一定能找到好工作的!");
        Message m2 = new Message("花花","杰杰子","我一定可以的!");
        messages.add(m1);
        messages.add(m2);
        ObjectMapper mapper = new ObjectMapper();
        // 把 java对象转换为 json字符串; List和数组会转换为[],一个对象{成员变量名: 值}
        String json = mapper.writeValueAsString(messages);
        System.out.println("转化的json字符串:" + json);

        // 设置json, 可以不设置 Content-Length, tomcat会设置
        resp.setContentType("application/json; charset=utf-8");
        resp.getWriter().println(json);
    }
    static class Message {
    
    
        private String from; //谁
        private String to;   //对谁
        private String info; //说了什么

        public Message(String from, String to, String info) {
    
    
            this.from = from;
            this.to = to;
            this.info = info;
        }

        public String getFrom() {
    
    
            return from;
        }

        public void setFrom(String from) {
    
    
            this.from = from;
        }

        public String getTo() {
    
    
            return to;
        }

        public void setTo(String to) {
    
    
            this.to = to;
        }

        public String getInfo() {
    
    
            return info;
        }

        public void setInfo(String info) {
    
    
            this.info = info;
        }
    }
}

启动服务,打开网页,查看:
在这里插入图片描述
后端输出:

在这里插入图片描述
加上前端代码:

<body>
    <h3>获取ajax响应数据,动态生成网页内容</h3>
    <button onclick="generate()">试试丫</button>
    <div id="content"></div>
</body>

<script>
    function generate() {
    
    
        let content = document.querySelector("#content");
        ajax({
    
    
            url: "ajax-response",
            method: "get",
            callback: function(status,resp){
    
    
                console.log(resp);
                // resp 是一个字符串
                //转换为 json对象
                let array = JSON.parse(resp);
                // 遍历
                for(json of array) {
    
    
                    // 每个json对象,创建一个dom来保存信息
                    let p = document.createElement("p");
                    p.innerHTML = json.from + "对" + json.to + "说" + json.info;
                    content.appendChild(p);
                }
            }
        });
    }
    function ajax(args){
    
    //var ajax = function(){}
        let xhr = new XMLHttpRequest();
        // 设置回调函数
        xhr.onreadystatechange = function(){
    
    
            // 4: 客户端接收到响应后回调
            if(xhr.readyState == 4){
    
    
                // 回调函数可能需要使用响应的内容,作为传入参数
                args.callback(xhr.status, xhr.responseText);
            }
        }
        xhr.open(args.method, args.url);
        //如果args中,contentType属性有内容,就设置Content-Type请求头
        if(args.contentType){
    
    //js中,if判断,除了判断boolean值,还可以判断字符串,对象等,有值就为true
            xhr.setRequestHeader("Content-Type", args.contentType);
        }
        //如果args中,设置了body请求正文,调用send(body)
        if(args.body){
    
    
            xhr.send(args.body);
        }else{
    
    //如果没有设置,调用send()
            xhr.send();
        }
    }
</script>

刷新页面:

在这里插入图片描述

过程分析:

在这里插入图片描述
客户端发起 Http 请求:

几种请求数据对应可以使用的数据格式:

数据格式 queryString 表单 from-data json
js: window.location.href = " "
html: <a href=" " / <img src=" " / …
浏览器地址栏直接输入 url
< form> 表单标签 √ (post) √ (post,form-data)
js: ajax

服务端处理:

  1. HttpServletRequest 获取请求数据:
获取方式 queryString 表单 from-data json
getParameter 简单类型可以获取
getPart √ (上传的文件)
getInputStream (可以获取任意格式的请求正文的数据) × (自己解析比较复杂,一般不用) ×

注意: json 字符串中的键需要和自定义类型中的成员变量名一致(一般解析为自定义类型)

  1. 根据请求数据进行校验,业务逻辑处理 (如,数据库操作)

校验: 如,账号手机号的格式
业务逻辑处理:根据请求数据的某个字段,执行不同的逻辑,如 type=1 返回登陆页面;type=0 返回首页,还可能执行数据库操作等

  1. 返回响应的内容
    返回 http 响应给客户端
  • 网页: 一般使用模板技术返回网页,而不是在 Servlet 拼接动态的 html
  • 文件: 根据需要
  • json: 前段是发送的 ajax 请求,比较常见

客户端处理 Http 响应:

  • 网页: 渲染 (浏览器自动执行)
  • 文件: 渲染或下载 (浏览器自动执行)
  • json: JavaScript 写 ajax 代码来完成
    涉及前端代码:
    解析响应正文 (json字符串) 为一个 json 对象: JSON.parse(body);
    使用 JavaScript DOM api,生成一些些 DOM 元素,把 json 对象中的字段设置到 DOM 元素中(属性,标签内容…)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_47988201/article/details/123322630