Servlet API
Servlet中有很多的类,其中HttpServlet
,HttpServletRequest
,HttpServletResponse
对于一开始使用Servlet是最关键的三个类。
HttpServlet
首先一个类需要继承HttpServlet
类,然后重写其中的方法,这其实就是一种将我们自定义的代码逻辑插入到Tomcat框架的实现方式。
核心方法
init
:在HttpServlet对象被创建的时候被调用destroy
:HttpServlet对象不再被使用的时候被调用service
:收到HTTP请求的时候调用doGet/doPost/doPut/doDelete...
:收到对应的请求时被调用
Servlet生命周期(创建出对象之后会自动调用哪些方法)也可以从上面的核心方法中看出来:Servlet对象被创建出来之后会调用init()
,Servlet对象每一次收到请求都会调用service()
,Servlet对象在被销毁之前会调用destroy()
。
注意:实际开发中的时候主要重写doxxx方法,很少会重写init
,destroy
,service
方法
使用HttpServlet对象实现一个简单的Servlet项目:使用ajax模拟发送请求,服务器处理GET和POST请求
- 前端代码
<!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>
<button onclick="sendGet()">发送 GET 请求</button>
<button onclick="sendPost()">发送 POST 请求</button>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
// 使用ajax发送HTTP请求
const sendGet = () => {
$.ajax({
type: "GET",
// 注意:ajax的url有两种写法:
// 1.相对路径,和当前地址前面的部分是相同的。
// 例如当前地址是/test_servlet/index.html,那么就会跳转到/test_servlet/method
// 2.绝对路径,从web根目录开始
// 例如:直接写成url: "/test_servlet/method"
url: "method",
success: function(resp) {
console.log(resp);
}
});
}
const sendPost = () => {
$.ajax({
type: "POST",
url: "method",
data: "post request body",
success: function(resp) {
console.log(resp);
}
})
}
</script>
</body>
</html>
注意:ajax中路径的写法。如果一旦使用/
开头,就一定要写成绝对路径。这个要和Servlet
中的@WebServlet
注释不同,在@WebServlet
注解中一定要给相对路径的前面加上/
,例如@WebServlet("/method")
- Servlet代码
@WebServlet("/method")
public class MethodServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 因为windows上的浏览器默认的编码格式是GBK,而Tomcat和IDEA中的编码格式是UTF-8,如果直接发送中文过去的话
// 就会看到乱码。因为UTF-8是通用编码格式,所以我们就不修改IDEA中的编码格式了,而是修改浏览么渲染的编码格式
// 通过在HTTP中的header调价Content-Type: charset=utf-8就可以修改浏览器的编码格式了
resp.setContentType("text/html; charset=utf-8;");
resp.getWriter().write("get 响应");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/plain; charset=utf-8;");
resp.getWriter().write("post 响应");
}
}
注意:给浏览器设置编码格式。在HTTP请求的header中设置Content-Type: charset=utf-8
HttpServletRequest
HttpServletRequest就是Tomcat对HTTP请求的封装。Tomcat将字符串结构的HTTP请求转化成了一个一个键值对的结构化数据,即将HTTP请求字符串进行了“反序列化”操作,这样更方便对数据进行操作。
HTTP请求中包括:请求行(请求方法+URL(ContextPath+QueryString)+版本号)+ 请求头部 + 空行 + 请求正文
其中的每一个部分,HttpServletRequest对象都有对应的方法可以获取到。
核心方法
使用HttpServletRequest获取HTTP请求信息
- Servelt代码
@WebServlet("/showrequest")
public class ShowRequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8;");
StringBuilder respBody = new StringBuilder();
respBody.append("请求方法: " + req.getMethod() + "<br/>");
respBody.append("请求路径URI: " + req.getRequestURI() + "<br/>");
respBody.append("ContextPath: " + req.getContextPath() + "<br/>");
respBody.append("QueryString: " + req.getQueryString() + "<br/>");
respBody.append("版本号: " + req.getProtocol() + "<br/>");
respBody.append("<h3>HttpHeader</h3>");
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
respBody.append(headerName + ": " + req.getHeader(headerName) + "<br/>");
}
resp.getWriter().write(respBody.toString());
}
}
- 实验结果
案例:使用HttpServletRequest获得GET请求传递的参数信息
- Servlet代码
@WebServlet("/getparameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; chatset=utf-8;");
// 获取url中querystring
String userId = req.getParameter("userId");
String userName = req.getParameter("userName");
// 对参数不存在需要判断querystring是否为null或者是否为""
// 因为不传递userId和userName这两个参数的话,那么对象就为null
// 如果传递的参数为userId=&userName=的话,那么对象就位""
if (userId == null || userId.equals("")) {
System.out.println("不存在userId");
}
resp.getWriter().write(String.format("userId: %s, userName: %s <br/>", userId, userName));
}
}
- 实验结果
案例:使用HttpServletRequest获得POST请求传递的参数信息
发起POST请求的时候,正文参数有三种格式:
- application/x-www-form-urlencoded格式,这种格式类似于直接使用url进行传递的参数。
- mutlipart/form-data,这种格式比较复杂,主要是用来提交文件的。(这种不详讲了)
- application/json,json类似于一个对象中有很多个key-value键值对。
解析application/x-www-form-urlencoded格式的正文参数
- Servlet代码
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8;");
// 直接使用getParamter就可以获取x-www-form-urlencoded格式的请求正文中的数据了
String userId = req.getParameter("userId");
String userName = req.getParameter("userName");
resp.getWriter().write(String.format("userId: %s, userName: %s <br/>", userId, userName));
}
}
- 前端发起post请求代码
<!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>
<form action="/test_servlet/postParameter" method="POST">
userId: <input type="text" name="userId">
<br/>
userName: <input type="text" name="userName">
<br/>
<input type="submit" name="提交">
</form>
</body>
</html>
- 实验结果
解析applicaion/jsno格式的正文参数
- Servlet代码
class JsonData {
public int userId;
public String userName;
}
@WebServlet("/postParameterJson")
public class PostParameterJsonServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 将请求正文先读取出来
String body = readBody(req);
// 使用第三方库jackson(Spring中的内置的json解析库)对json格式的字符串进行解析
ObjectMapper objectMapper = new ObjectMapper();
JsonData jsonData = objectMapper.readValue(body, JsonData.class);
resp.getWriter().write(String.format("userId: %d, userName: %s <br>", jsonData.userId, jsonData.userName));
}
private String readBody(HttpServletRequest req) throws IOException {
// 使用HttpServletRequest中的inputStream流对象进行正文数据读取
InputStream inputStream = req.getInputStream();
int contentLength = req.getContentLength();
byte[] buffer = new byte[contentLength];
inputStream.read(buffer);
return new String(buffer, "utf-8");
}
}
Jackson库基本原理就是先将json格式的字符串转换为键值对保存起来,然后根据类对象,通过反射机制获得类中有哪些属性,最后在前面保存的键值对结果中找类中属性对应的value。所以我们需要定义的JsonData
这个类中属性和发送的json数据属性要一一对应,最后获得objectMapper.readValue()
返回的对象就是解析完成的json数据。
- 发送POST请求的前端代码
<!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>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<button onclick="sendJson()">发送 POST 请求</button>
<script>
const sendJson = () => {
let body = {
userId: 666,
userName: "zhy"
};
$.ajax({
url: "postParameterJson",
type: "POST",
contentType: "application/json",
data: JSON.stringify(body),
success: function(resp) {
console.log(resp);
}
});
}
</script>
</body>
</html>
注意:使用ajax发送POST请求并带上json格式的数据的时候,需要将json数据使用JSON.stringify()
转换为字符串进行发送。
小结HttpServletRequest
平时使用HttpServletRequest
最常见的工作就是获得HTTP请求中的参数
- 如果是GET请求,可以通过
getParameter()
获得url中的query string。 - 如果是POST请求并且请求正文参数是
application/x-www-form-urlencoded
格式的,也是通过getParameter()
获得正文中的参数 - 如果是POST请求并且请求正文参数是
application/json
格式的,就需要先将请求正文数据读取出来,然后通过第三方库Jackson
进行解析。
HttpServletResponse
核心方法
案例:使用HttpServletResponse对象设置响应状态码
- Servlet代码
@WebServlet("/status")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
// 用户通过query string传递一个status,然后服务器返回对应状态码的响应
String statusString = req.getParameter("status");
if (statusString == null || statusString.equals("")) {
resp.getWriter().write("status 参数不存在");
return ;
}
resp.setStatus(Integer.parseInt(statusString));
resp.getWriter().write("status: " + statusString);
}
}
- 实验结果
案例:使用HttpServletResponse对象在响应报头中添加键值对
- Servlet代码
@WebServlet("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf8");
// 在响应报头中添加一个键值对,将Refresh设置为1,那么浏览器就会每隔一秒进行一次刷新
resp.setHeader("Refresh", "1");
// 通过返回一个时间戳来看到浏览器每隔一秒都在进行刷新
long timeStamp = System.currentTimeMillis();
resp.getWriter().write("timeStamp: " + timeStamp);
}
}
- 实验结果
案例:使用HttpServletResponse对象设置页面重定向
- Servlet代码
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置重定向有两种方法:
// 1.设置响应报文的状态码,并添加Location字段在响应报头中
// resp.setStatus(302);
// resp.setHeader("Location", "https://www.baidu.com");
// 2.使用sendRedirect()进行重定向
resp.sendRedirect("https://www.baidu.com");
}
}