前言
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>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>设置状态码</h3>
<input type="text" id="status">
<br>
<button onclick="setStatus()">提交</button>
</body>
<script>
function setStatus(){
let 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")
public class ResponseStudyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String status = req.getParameter("status");
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("响应状态码设置成功");
}
}
启动Tomcat 后,页面如下所示:
将 404 状态码进行提交后,fiddler 抓包工具可查看到如下信息:
2.设置响应头
修改前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>设置状态码</h3>
<input type="text" id="status">
<br>
<button onclick="setStatus()">提交</button>
<h3>设置响应头</h3>
<a href="response">设置</a>
</body>
<script>
function setStatus(){
let 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")
public class ResponseStudyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String status = req.getParameter("status");
if (status != null) {
resp.setStatus(Integer.parseInt(status));
resp.getWriter().write("响应状态码设置成功");
}
resp.setHeader("location","http://www.baidu.com" );
resp.setHeader("username","晓茹");
}
}
重新启动Tomcat ,刷新页面:
fiddler 抓包结果: 会发现设置了location 字段,但并没有跳转,发现响应状态码为200 (原因:只有3xx 的状态码才会重定向);
注意:
- 若响应头
name 键已有,就会覆盖原有的键值对; addHeader ,当name 键已存在时,不会覆盖,会添加一个新的;
3.设置响应内容
(1)响应一个网页(简单HTML)
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>响应正文为简单的html网页</h3>
<a href="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")
public class HtmlTypeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
String type = req.getParameter("type");
if("1".equals(type)){
pw.println("<h3>获取网页成功</h3>");
}
}
}
重新启动,刷新页面,点击跳转:
(2)响应一个网页(复杂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>
后端代码:
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")
public class HtmlTypeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
String type = req.getParameter("type");
if("1".equals(type)){
pw.println("<h3>获取网页成功</h3>");
}else if("2".equals(type)){
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>
<a href="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")
public class GoToServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String type = req.getParameter("type");
if("1".equals(type)){
resp.setStatus(301);
resp.setHeader("Location","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>
<a href="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")
public class GoToServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String type = req.getParameter("type");
if("1".equals(type)){
resp.setStatus(301);
resp.setHeader("Location","request.html");
}else if("2".equals(type)){
req.getRequestDispatcher("request.html")
.forward(req,resp);
}
}
}
启动Tomcat ,刷新页面: 会发现URL 地址栏并没有发生变化!!!
小结:
特点 :URL 地址栏不会发生变化,只有一次请求; 原理 :当次请求servlet 时,由servlet 获取到转发路径的资源,并把这个路径的内容设置到响应正文;
返回一个文件
需要设置一下 content-Type 和 content-Length ,然后将文件的二进制数据放在响应正文即可;
渲染展示与下载
示例 :图片与音乐
前端代码:
<body>
<h3>获取一个图片(渲染展示)</h3>
<img src="file?type=photo&show=1">
<h3>获取一个音乐(渲染展示)</h3>
<audio src="file?type=music&show=1" controls></audio>
<h3>获取一个图片(下载)</h3>
<a href="file?type=photo&show=0">下载</a>
<h3>获取一个音乐(下载)</h3>
<a href="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")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
OutputStream os = resp.getOutputStream();
String type = req.getParameter("type");
String show = req.getParameter("show");
File file = null;
if("photo".equals(type)){
if("1".equals(show)) {
resp.setContentType("image/jpeg");
}else {
resp.setContentType("application/octet-stream");
}
file = new File("D:\\servlet-study\\src\\main\\resources\\doge.jpg");
}else if("music".equals(type)){
if("1".equals(show)) {
resp.setContentType("audio/mp3");
}else {
resp.setContentType("application/octet-stream");
}
file = new File("D:\\servlet-study\\src\\main\\resources\\ 晴天.mp3");
}
byte[] data = Files.readAllBytes(file.toPath());
resp.setContentLength(data.length);
os.write(data);
}
}
启动Tomcat ,刷新页面,渲染方式如下:
下载方式如下:
打开文件,修改文件后缀名即可查看图片;
http 菜鸟教程:查看 content-type 对应格式;
思考:图片、音乐、视频等都是静态文件,直接放在webapp 下,就可以直接访问,还需要servlet来返回嘛?
’ 如果文件总的大小非常大,放在web 应用的webapp 下就不合适(打包比较费劲),但通过servlet 去读取本地其他地方的文件来返回就比较合适;
返回 json 数据
常用于ajax 请求,返回一些数据,用于动态的填充网页;
前端代码:
<body>
<h3>获取ajax响应数据,动态生成网页内容</h3>
<button onclick="gen()">试试呗</button>
<div id="content"></div>
</body>
<script>
function gen(){
let content = document.querySelector("#content");
ajax({
url: "ajax-response",
method: "get",
callback: function(status, resp){
console.log(resp);
let array = JSON.parse(resp);
for(json of array){
let p = document.createElement("p");
p.innerHTML = json.from+" 对 "+json.to+" 说:"+json.info;
content.appendChild(p);
}
}
});
}
function ajax(args){
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
args.callback(xhr.status, xhr.responseText);
}
}
xhr.open(args.method, args.url);
if(args.contentType){
xhr.setRequestHeader("Content-Type", args.contentType);
}
if(args.body){
xhr.send(args.body);
}else{
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")
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();
String json = mapper.writeValueAsString(messages);
System.out.println("转换的json字符串:"+json);
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;
}
}
}
启动Tomcat ,打开网页,查看: 点击试试呗按钮,网页显示:
后端输出:
请求响应流程小结
简单来说就是:
- 客户端浏览器发起
HTTP 请求
客户端浏览器发起请求的方式: (1)queryString : (2)表单: (3)form-data : (4)json :
如下表所示:
- 服务端进行处理
服务端处理步骤: (1)通过 HttpServletRequest 先获取到请求数据;
获取方式 | queryString | 表单 | form-data | json |
---|
getParameter | √ | √ | 可以获取简单类型 | | getPart | | | √(上传的文件) | | getInputStream(可以获取任意格式的请求正文的数据) | | | | √ |
注意:json 字符串中的键需要和自定义类型中的成员变量名一致(一般为自定义格式)!
(2)根据请求数据进行校验,业务逻辑处理(如:数据库操作或者根据请求数据的某个字段,执行不同的逻辑,如上面的type=1 ,返回简单的html ,type=2 ,返回动态变化的 html ); (3)返回响应的内容;内容有:
- 网页:一般使用模板技术,而不是在
servlet 拼接动态的html ; - 文件:根据需要;
- json:前端发送的是
ajax 请求;
3.返回HTTP响应给客户端
客户端接收并处理服务端HTTP 响应
三种方式 :
(1)网页:渲染方式(浏览器自动完成); (2)文件:下载或渲染(浏览器自动完成) (3)json:JavaScript 写 ajax 代码完成;
|