详解 HttpServletResponse
前言
Servlet
中的
doXXX
方法的目的就是根据请求计算得到响应, 然后把响应的数据设置到
HttpServletResponse
对象中,
然后
Tomcat
就会把这个
HttpServletResponse
对象按照
HTTP
协议的格式, 转成一个字符串, 并通过
Socket
写回给浏览器;
核心方法
方法描述void setStatus(int sc)设置响应状态码void setHeader(String name,String value)设置一个带有给定的名称和值的
Header
,如果
name
已经存在,则覆盖旧的值void addHeader(int sc)设置一个带有给定的名称和值的
Header
,如果
name
存在,不会覆盖旧的值,并列添加新的值void setContentType(String type)设置被发送到客户端的响应的内容类型void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(
MIME
字符集)void sendRedirect(String location)使用指定的重定向位置
URL
发送临时重定向响应到客户端PrintWriter getWriter()用于往
body
中写入文本格式数据OutputStream getOutStream()用于往
body
中写入二进制格式数据
需要注意的是:
- 响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的, **
因此上面的方法都是 "写" 方法
**; - 对于状态码/响应头的设置要放到
getWriter /getOutputStream
之前, 否则可能设置失效;
代码示例
1.设置响应状态码
前端代码:
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h3>设置状态码</h3><inputtype="text"id="status"><br><buttononclick="setStatus()">提交</button></body><script>functionsetStatus(){//js中发请求:(1)ajax (2)直接修改地址栏URLlet status = document.querySelector("#status");//后端将文本框输入的值作为响应状态码
window.location.href="response?status="+status.value;}</script></html>
后端代码:
package response;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("/response")publicclassResponseStudyServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {//获取请求发送的queryString数据:status=xxx
String status = req.getParameter("status");
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("响应状态码设置成功");}}
启动
Tomcat
后,页面如下所示:
将
404
状态码进行提交后,
fiddler
抓包工具可查看到如下信息:
2.设置响应头
修改前端代码:
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h3>设置状态码</h3><inputtype="text"id="status"><br><buttononclick="setStatus()">提交</button><h3>设置响应头</h3><ahref="response">设置</a></body><script>functionsetStatus(){//js中发请求:(1)ajax (2)直接修改地址栏URLlet status = document.querySelector("#status");//后端将文本框输入的值作为响应状态码
window.location.href="response?status="+status.value;}</script></html>
修改后端代码:
package response;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("/response")publicclassResponseStudyServletextendsHttpServlet{@OverrideprotectedvoiddoGet(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","晓茹");}}
重新启动
Tomcat
,刷新页面:
fiddler
抓包结果:
会发现设置了
location
字段,但并没有跳转,发现响应状态码为
200
(原因:只有
3xx
的状态码才会重定向);
注意:
- 若响应头
name
键已有,就会覆盖原有的键值对; addHeader
,当name
键已存在时,不会覆盖,会添加一个新的;
3.设置响应内容
(1)响应一个网页(简单HTML)
前端代码:
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h3>响应正文为简单的html网页</h3><ahref="html?type=1">查看</a></body></html>
后端代码:
package response;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.PrintWriter;@WebServlet("/html")publicclassHtmlTypeServletextendsHttpServlet{@OverrideprotectedvoiddoGet(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");if("1".equals(type)){//返回简单的html
pw.println("<h3>获取网页成功</h3>");}}}
重新启动,刷新页面,点击跳转:
(2)响应一个网页(复杂HTML)
前端代码:
<body><h3>响应正文为复杂的html(动态变化的)</h3><inputtype="text"id="username"placeholder="输入姓名"><br><buttononclick="toWelcome()">跳转</button></body><script>functiontoWelcome(){let username = document.querySelector("#username");
window.location.href ="html?type=2&username="+username.value;}</script>
后端代码:
package response;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.PrintWriter;@WebServlet("/html")publicclassHtmlTypeServletextendsHttpServlet{@OverrideprotectedvoiddoGet(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");if("1".equals(type)){//返回简单的html
pw.println("<h3>获取网页成功</h3>");}elseif("2".equals(type)){//返回复杂的html//html?type=2&username=xxx
String username = req.getParameter("username");
pw.println("<p>");
pw.println("欢迎你:"+username);
pw.println("</p>");}}}
启动
Tomcat
,刷新页面:
输入
Java小菜鸟
,点击跳转:
当输入另一个姓名(张三)时:
思考:如上
Java
代码中,写入了许多
HTML
代码,这样开发好嘛?
答案是肯定不好的,**
缺点
**:耦合性太强(两个完全不同的编程语言在一起开发),导致维护性和可扩展性变差!
解决方案
:(1)模板技术(也存在一些问题) (2)进一步产生
ajax
技术;
返回已有的一个网页
1.重定向
前端代码:
<body><h3>重定向到request.html</h3><ahref="goto?type=1">跳转</a></body>
后端代码:
package response;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("/goto")publicclassGoToServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {//重定向到request.html//goto?type=xxx
String type = req.getParameter("type");if("1".equals(type)){//重定向//设置响应状态码:301//设置location字段
resp.setStatus(301);
resp.setHeader("Location","request.html");//以上代码可简化为:// resp.sendRedirect("request.html");}}}
启动
Tomcat
,点击跳转,
URL
地址栏由
http://localhost:8080/servlet-study/response.html
跳转到
http://localhost:8080/servlet-study/resquest.html
结果如下所示:
URL
地址栏发生了变化
小结:
特点
:
URL
地址栏会发生变化,会发起两次请求:
原理
:
第一次返回301、302、307
响应状态码及响应头
location
:网页的地址;
第二次:浏览器自动跳转到location
设置的地址;
可以用在登陆成功后跳转到某个页面!
2.转发
前端代码:
<body><h3>转发到request.html</h3><ahref="goto?type=2">跳转</a></body>
修改后的后端代码:
package response;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("/goto")publicclassGoToServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {//重定向到request.html//goto?type=xxx
String type = req.getParameter("type");if("1".equals(type)){//重定向//设置响应状态码:301//设置location字段
resp.setStatus(301);
resp.setHeader("Location","request.html");//以上代码可简化为:// resp.sendRedirect("request.html");}elseif("2".equals(type)){//转发
req.getRequestDispatcher("request.html").forward(req,resp);}}}
启动
Tomcat
,刷新页面:
会发现
URL
地址栏并没有发生变化!!!
小结:
特点
:
URL
地址栏不会发生变化,只有一次请求;
原理
:当次请求
servlet
时,由
servlet
获取到转发路径的资源,并把这个路径的内容设置到响应正文;
返回一个文件
需要设置一下
content-Type
和
content-Length
,然后将文件的二进制数据放在响应正文即可;
渲染展示与下载
**
示例
**:图片与音乐
前端代码:
<body><h3>获取一个图片(渲染展示)</h3><imgsrc="file?type=photo&show=1"><h3>获取一个音乐(渲染展示)</h3><audiosrc="file?type=music&show=1"controls></audio><h3>获取一个图片(下载)</h3><ahref="file?type=photo&show=0">下载</a><h3>获取一个音乐(下载)</h3><ahref="file?type=music&show=0">下载</a></body>
后端代码:
package response;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.File;import java.io.IOException;import java.io.OutputStream;import java.nio.file.Files;@WebServlet("/file")publicclassFileServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {// 获取响应对象的字节输出流
OutputStream os = resp.getOutputStream();// 返回的文件类型:1.图片
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{//文件下载
resp.setContentType("application/octet-stream");}
file =newFile("D:\\servlet-study\\src\\main\\resources\\doge.jpg");//<audio src="file?type=music&show=1" controls></audio>}elseif("music".equals(type)){//返回音乐if("1".equals(show)){
resp.setContentType("audio/mp3");//mp3格式}else{
resp.setContentType("application/octet-stream");}
file =newFile("D:\\servlet-study\\src\\main\\resources\\ 晴天.mp3");}//返回一个文件类型:Content-Length,bodybyte[] data = Files.readAllBytes(file.toPath());
resp.setContentLength(data.length);//=setHeader("Content-Length", xxx)
os.write(data);}}
启动
Tomcat
,刷新页面,渲染方式如下:
下载方式如下:
打开文件,修改文件后缀名即可查看图片;
http 菜鸟教程:查看
content-type
对应格式;
思考:图片、音乐、视频等都是静态文件,直接放在webapp 下,就可以直接访问,还需要servlet来返回嘛?
’
如果文件总的大小非常大,放在web
应用的
webapp
下就不合适(打包比较费劲),但通过
servlet
去读取本地其他地方的文件来返回就比较合适;
返回 json 数据
常用于
ajax
请求,返回一些数据,用于动态的填充网页;
前端代码:
<body><h3>获取ajax响应数据,动态生成网页内容</h3><buttononclick="gen()">试试呗</button><divid="content"></div></body><script>functiongen(){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);}}});}functionajax(args){//var ajax = function(){}let xhr =newXMLHttpRequest();// 设置回调函数
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>
后端代码:
package response;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;import java.util.List;@WebServlet("/ajax-response")publicclassAjaxJsonServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
List<Message> messages =newArrayList<>();
Message m1 =newMessage("花花","小菜鸟","你一定可以顺利毕业");
Message m2 =newMessage("小菜鸟","花花","我肯定行!!!");
messages.add(m1);
messages.add(m2);
ObjectMapper mapper =newObjectMapper();//把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);}staticclassMessage{private String from;//谁private String to;//对谁private String info;//说了什么//构造方法publicMessage(String from, String to, String info){this.from = from;this.to = to;this.info = info;}//getter与setterpublic String getFrom(){return from;}publicvoidsetFrom(String from){this.from = from;}public String getTo(){return to;}publicvoidsetTo(String to){this.to = to;}public String getInfo(){return info;}publicvoidsetInfo(String info){this.info = info;}}}
启动
Tomcat
,打开网页,查看:
点击试试呗按钮,网页显示:
后端输出:
请求响应流程小结
简单来说就是:
- 客户端浏览器发起
HTTP
请求
客户端浏览器发起请求的方式:
(1)queryString
:
(2)表单:
(3)form-data
:
(4)json
:
如下表所示:
- 服务端进行处理
服务端处理步骤:
(1)通过HttpServletRequest
先获取到请求数据;
获取方式queryString表单form-datajsongetParameter√√可以获取简单类型getPart√(上传的文件)getInputStream(可以获取任意格式的请求正文的数据)√
注意:
json
字符串中的键需要和自定义类型中的成员变量名一致(一般为自定义格式)!
(2)根据请求数据进行校验,业务逻辑处理(如:数据库操作或者根据请求数据的某个字段,执行不同的逻辑,如上面的
type=1
,返回简单的
html
,
type=2
,返回动态变化的
html
);
(3)返回响应的内容;内容有:
- 网页:一般使用模板技术,而不是在
servlet
拼接动态的html
; - 文件:根据需要;
- json:前端发送的是
ajax
请求;
3.返回HTTP响应给客户端
客户端接收并处理服务端
HTTP
响应
三种方式
:
(1)网页:渲染方式(浏览器自动完成);
(2)文件:下载或渲染(浏览器自动完成)
(3)json:JavaScript
写
ajax
代码完成;
版权归原作者 编程小菜鸟~ 所有, 如有侵权,请联系我们删除。