我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法
我们实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service .在前面我们讲了这些方法的调用时机就成为"Servlet的生命周期"
1.2.1 处理 Get 请求:
创建一个 doGetMethod 类并重写doGet方法
@WebServlet("/hellodoGet")
public class doGetMethod extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 不能调用父类的 doGet !!!要自己实现一个
// super.doGet(req, resp);
// 把 hello world 字符串, 放到 http 响应的 body 中. 浏览器就会把 body 的内容显示到页面上了,如果不给响应对象中设置任何内容, 这个时候就会出现 空白页面
//resp:响应对象 write:真正写数据的方法
//getWriter:返回一个Writer对象(字符流对象),此时Writer是往http响应的body中写入数据
resp.getWriter().write("hello world");
}
}
部署项目后,在浏览器输入 http://localhost:8080/HelloWorldservlet/hellodoGet 访问页面
1.2.2 处理 POST 请求:
创建 doPostMethod 类并重写doPost
@WebServlet("/hellodoPost")
public class doPostMethod extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
resp.setContentType("text/html;charset = utf8");
resp.getWriter().write("post 响应");
}
}
创建test.html,并放入webapp中
一个 Servlet 程序中可以同时部署静态文件. 静态文件就放到 webapp 目录中即可。但是注意此时
test.html文件在webapp中,与WEB-INF是同级
test.html 的代码如下:
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
$.ajax({
type:'post',
url:'hellodoPost',
success:function(body){
console.log(body);
},
});
</script>
重新部署程序, 使用 URL http://localhost:8080/HelloWorldservlet/test.html 访问页面
通过 Fiddler 抓包, 可以看到:
1.当浏览器中输入 URL 之后, 浏览器先给服务器发送了一个 HTTP GET 请求,请求一个test.html页面,而这个页面中没有写内容,所以是空白
2.浏览器又通过 ajax 给服务器发送了一个 HTTP POST 请求
并返回了一个响应:post 响应
注意:
这个 ajax 请求的 URL 路径. 代码中写的 url: 'hellodoPost’不加 /, 为一个相对路径, 相对于当前test.html的路径,而test.html的路径就是HelloWorldservlet 最终真实发送的请求的 URL 路径为 /HelloWorldservlet/hellodoPost
关于乱码问题
上述代码已经做过处理所以没有乱码!如果不处理,我们直接在响应中写中文此时浏览器就会看到乱码,如下
为什么呢? 中文的编码方式有很多种. 其中最常见的就是 utf-8 .如果没有显式的指定编码方式, 则浏览器不能正确识别编码, 就会出现乱码的情况
如何解决! 可以在代码中, 通过 resp.setContentType(“text/html; charset=utf-8”); 显式的指定编码方式
此时通过抓包可以看到, 当加上了 resp.setContentType(“text/html; charset=utf-8”); 代码之后, 响应中多了 Content-Type 字段, 内部指定了编码方式. 浏览器看到这个字段就能够正确解析中文了
当 Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成
HttpServletRequest 对象
注意: 请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 "写"方法
2.2.1 打印请求信息
@WebServlet("/showRequest")
public class HttpServletRequestMethod extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
//使用 HttpServletRequest 的各种方法获取到 http请求的各种属性,在作为响应的body返回到页面中
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("首行部分
");
stringBuilder.append(req.getProtocol());//协议名和版本
stringBuilder.append("
");
stringBuilder.append(req.getMethod());//请求 方法
stringBuilder.append("
");
stringBuilder.append(req.getRequestURI());//URL /helloservelet/showRequest
stringBuilder.append("
");
stringBuilder.append(req.getContextPath());// helloservelet
stringBuilder.append("
");
stringBuilder.append(req.getQueryString());// 查询字符串内容
stringBuilder.append("
");
stringBuilder.append("header 部分
");
Enumeration<String> headerNames = req.getHeaderNames();//返回一个枚举,包含在该请求中包含的所有的头名
while (headerNames.hasMoreElements()) {//遍历枚举里面的每一个元素
String headerName = headerNames.nextElement(); //请求头的名字
String headerValue = req.getHeader(headerName);//以字符串形式返回指定的请求头的值
stringBuilder.append(headerName + ": " + headerValue + "
");
}
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write(stringBuilder.toString());
}
}
部署后通过URL访问页面,如下
2.2.2 获取 GET 请求中的参数
GET 请求中的参数一般都是通过 query string 传递给服务器的. 形如
https://v.bitedu.vip/personInf/student?userId=1111&classId=100
此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId, 值分别是 1111 和 100
在服务器端就可以通过 getParameter 来获取到参数的值.
创建 etParameterTest 类
@WebServlet("/getParameter")
public class getParameterTest extends HttpServlet {
@Override
//Post请求一般通过body传递,如果body中的格式是 form表单的形式,任然可以通过 getParameter 获取参数!
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
//预期浏览器传来一个请求:/getParameter?userID=123&classID=456
String userId = req.getParameter("userID");//获取到请求中的参数
String classId = req.getParameter("classID");
resp.getWriter().write("userId=" + userId + ", classId=" + classId);
}
}
重新部署程序, 在浏览器中通过 http://localhost:8080/HelloWorldservlet/getParameter 访问页面
当没有 query string的时候, getParameter 获取的值为null.再在浏览器输入URL
http://localhost:8080/HelloWorldservlet/getParameter?userId=123&classId=456
此时说明服务器已经获取到客户端传递过来的参数
getParameter 的返回值类型为 String. 必要的时候需要手动把 String 转成 int
2.2.3 获取 POST 请求中的参数(1)
POST 请求的参数一般通过 body 传递给服务器. body 中的数据格式有很多种. 如果是采用 form 表单的形式, 仍然可以通过 getParameter 获取参数的值.
创建类 PostParameter
@WebServlet("/postParameter")
public class PostParameter extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
String userId = req.getParameter("userId");
String classId = req.getParameter("classId");
resp.getWriter().write("userId: " + userId + ", " + "classId: " +
classId);
}
}
创建 testPost.html, 放到 webapp 目录中
<form action="postParameter" method="POST">
<input type="text" name="userId">
<input type="text" name="classId">
<input type="submit" value="提交">
</form>
重新部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/testPost.html 访问, 可以
看到 HTML
在输入框中输入内容, 点击提交
可以看到跳转到了新的页面, 并显示出了刚刚传入的数据
此时通过抓包可以看到, form 表单构造的 body 数据的格式为:
Content-Type: application/x-www-form-urlencoded, 对应的 body 数据格式就形如
userId=123&classId=456
2.2.4 获取 POST 请求中的参数(2)
如果 POST 请求中的 body 是按照 JSON 的格式来传递json里面可能涉及到嵌套,所以手动解析其实并不容易。此时就可以使用第三方库来协助
引入 Jackson 这个库, 进行 JSON 解析.,步骤如下:
后端:
class User{
public int userId;
public int classId;
}
//如果post请求body中是 json格式!就需要通过第三方库 Jackson 来获取里面的数据
@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
//1.创建 Jackson 的核心对象
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//2.读取body中的请求,使用 objectMapper 来解析成需要的对象
//readValue:将 json格式的字符串转换成 java的对象
//第一个参数:对哪个字符串转换(形如:{"userId":"123","classId":"456"}),//第二个参数:转换成哪个 java 对象(根据键值对里的key与user类里面的属性进行对比,看是否匹配,
//如果匹配就将此key对应的value赋值给此时的属性,同时进行类型转换。不匹配就跳过,进行下一个key)
User user = objectMapper.readValue(req.getInputStream(),User.class);//通过反射可以拿到这个类对象,获取里面所以的属性
resp.getWriter().write("userId:" + user.userId + ", classId:" + user.classId);
}
}
注意:
引入jackSon三方库后,创建ObjectMapper 对象,并调用readValue方法将 json格式的字符串转换成 java的对象.其中第一个参数:对哪个json字符串转换,第二个参数:转换成哪个 java 对象
因为要将 json格式的字符串转换成 java的对像,所以需要提前准备一个User类。然后根据键值对里的key与user类里面的属性进行对比,看是否匹配,如果匹配就将key对应的value赋值给User类中的属性同时进行类型转换,若不匹配就跳过取下一个key,最终就会构造一个User对象
readValue 的第二个参数为 User 的 类对象. 通过这个类对象, 在 readValue 的内部就可以借
助反射机制来构造出 User 对象, 并且根据 JSON 中key 的名字, 把对应的 value 赋值给
User 的对应字段.
或者说:
User.class就是通过反射来拿到User类当中的属性,然后拿json字符串中的key值与这些属性进行匹配,若匹配就将key对应的value赋值给这个属性,最终将所有 json 键值对放到了一个User对象,需要用的时候就直接User.属性
前端:同样在webapp中创建test.html文件并写入以下代码:
<input type="text" id="userId">
<input type="text" id="classId">
<input type="button" value="提交" id="submit">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
let userIdInput = document.querySelector('#userId');
let classIdInput = document.querySelector('#classId');
let button = document.querySelector('#submit');
button.onclick = function(){
$.ajax({
type:'post',
url:'postJson',
contentType: 'application/json',
//post这样的请求,ajax允许用data来构造请求的body部分(此时构造的其实是一个js对象,通过JSON.stringfy将这个js对象转换成一个json格式的字符串)
data:JSON.stringify({
userId:userIdInput.value,
classId:classIdInput.value
}),
success:function(body){
console.log(body);
}
})
}
</script>
注意:
在浏览器中输入URL访问页面:
通过抓包来观察:
Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应, 然后把响应的数据设置到
HttpServletResponse 对象中.然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串, 并通过Socket 写回给浏览器
注意:
3.2.1 设置状态码
@WebServlet("/statusServlet")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String statusString = req.getParameter("status");
if (statusString != null) {
resp.setStatus(Integer.parseInt(statusString));
}
resp.getWriter().write("status: " + statusString);
}
}
部署程序, 在浏览器中通过 URL http://127.0.0.1:8080/ServletHelloWorld/statusServlet?
status=200 访问, 可以看到
3.2.2 重定向
实现一个程序, 返回一个重定向 HTTP 响应, 自动跳转到另外一个页面
@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendRedirect("http://www.sogou.com");
}
}
部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/redirectServlet 访问, 可以看
到, 页面自动跳转到 搜狗主页 了
抓包结果:
3.2.3 自动刷新
实现一个程序, 让浏览器每秒钟自动刷新一次. 并显示当前的时间戳
@WebServlet("/autoRefreshServlet")
public class AutoRefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setHeader("Refresh", "1");
long timeStamp = new Date().getTime();
resp.getWriter().write("timeStamp: " + timeStamp);
}
}
1.通过 HTTP 响应报头中的 Refresh 字段, 可以控制浏览器自动刷新的时机.
2.通过 Date 类的 getTime 方法可以获取到当前时刻的毫秒级时间戳
部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/autoRefreshServlet 访问, 可
以看到浏览器每秒钟自动刷新一次