1.HttpServlet
我们在写Servlet代码的时候,首先第一步就是先创建类,继承自HttpServlet,并重写其中的某些方法。
1.1 核心方法
方法名称 | 调用时机 |
init | 在HttpServlet实例化后被调用一次 |
destory | 在HttpServlet实例不再使用的时候调用一次 |
service | 收到HTTP请求的时候调用 |
doGet | 收到GET请求的时候调用(由service方法调用) |
doPost | 收到POST请求的时候调用(由service方法调用) |
doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由service方法调用) |
在实际开发中主要重写doXXX方法,很少会重写init/destory/service.
这些方法的调用时机,就称为“Servlet生命周期”(也就是描述了一个Servlet实例从生到死的过程)
注意:HttpServlet的实例只是在程序启动时创建一次,而不是每次收到HTTP请求都重新创建实例。
1.2 代码示例:处理GET请求
创建MethodServlet.java,创建doGet方法
@WebServlet("/method") public class MethodServlet extends HelloServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("GET RESPONSE"); } }
我们知道,在浏览器中直接输入URL就可以构造一个GET请求,现在我们来测试服务器的处理:
1.3 代码示例:处理POST请求
在MethodServlet.java中,新增doPost方法
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("POST 响应"); }
完成了服务器端的代码,我们需要构造一个POST请求来进行测试,并不能通过直接输入URL的方式来构造POST请求;构造POST请求的方法有两种,一种是基于form表单,另一种是通过ajax来构造,下边我们主要来看看通过ajax的方式来如何构造post请求。(该流程的详情也可见文章【构造HTTP请求与HTTPS加密1.2】)
首先我们需要先在webapp目录下创建一个HTML文件,用来构造POST请求。
然后我们需要引入jQuery依赖(搜索jQuery mdn进行复制,也可本地导入)
最后使用jQuery的ajax函数.
注意:ajax请求中的URL不能加/,但是Servlet类的注解必须加上/.
我们访问http://localhost:8080/maven_1102/testMethod.html,出现如下图结果:
也就是出现了乱码的情况,这是因为我们在编译器上,字体的默认编码方式为utf-8,而浏览器字体的默认编码方式为GBK,因为编码方式的不同,导致同一文件在不同地方的展示出现了乱码的情况;我们可以通过响应,显式的告诉浏览器通过utf8的方式进行编码,就可以避免乱码的情况了。
上述红框中代码的含义为,响应的返回类型为html格式,编码方式为utf8.
重新打包部署并测试:
2.HttpServletRequest
当Tomcat通过Socket API读取HTTP请求(字符串),并且按照HTTP协议格式把字符串解析成HttpServletRequest对象。
2.1 核心方法
方法 描述
String getProtocol() 返回请求协议的名称和版本
String getMethod() 返回请求的HTTP方法的名称,例如,GET、POST或PUT
String getRequestURI() 从协议名称直到HTTP请求的第一行的查询字符串中,返回该请求的URL的一部分。
String getContextPath() 返回指示请求上下文的请求URI部分
String getQueryString() 返回包含在路径后的请求URL中的查询字符串
Enumeration getParameterNames() 返回一个String 对象的枚举,包含在该请求中包含的参数的名称(即键值对中的Key)
String getParameter(String name) 以字符串形式返回请求参数的值,或者如果参数不存在则返回null(即通过键值对中的Key来拿到value)
String[] getParameterValues(String name) 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回null
Enumeration getHeaderNames() 返回一个枚举,包含在该请求中包含的所有的头名(请求报头也是键值对结构,此处活动Key)
String getHeader(String name) 以字符串形式返回指定的请求头的值(Value)
String getCharacterEncoding()返回请求主体中使用的字符编码的名称
String getContentType() 返回请求主体的MIME类型,如果不知道类型则返回null
int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回-1.
InputStream getInputStream() 用于读取请求的body内容,返回一个InputStream对象
通过这些方法可以获取到一个请求中的各个方面的信息。
注意:请求对象是服务器收到的内容,不应该修改。因此上面的方法也都只是“读”方法,而不是“写”方法。
2.2 代码示例:打印请求信息
创建ShowRequestServlet类,调用一下在2.1中涉及到的几个关键API,并把得到的结果组织到一个html中,并作为响应的body.
@WebServlet("/showRequest") public class ShowRequestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //把API执行的结果,放到stringBuilder中 StringBuilder stringBuilder=new StringBuilder(); stringBuilder.append("<h3>首行部分</h3>"); stringBuilder.append(req.getProtocol()); stringBuilder.append("<br>"); stringBuilder.append(req.getMethod()); stringBuilder.append("<br>"); stringBuilder.append(req.getRequestURI()); stringBuilder.append("<br>"); stringBuilder.append(req.getContextPath()); stringBuilder.append("<br>"); stringBuilder.append(req.getQueryString()); stringBuilder.append("<br>"); stringBuilder.append("<h3>header部分</h3>"); Enumeration<String> headerNames=req.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName= headerNames.nextElement(); String headerValue=req.getHeader(headerName); stringBuilder.append(headerName+":"+headerValue+"<br>"); } resp.setContentType("text/html;charset=utf8"); resp.getWriter().write(stringBuilder.toString()); } }
2.3 代码示例:获取GET请求中的参数
最常用的其实是getParameter这个方法,能够获取到query string中的详细内容。创建一个GetParameterServlet类来获取Get请求中的参数。
@WebServlet("/getParameter") public class GetParameterServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userId=req.getParameter("userId"); String classId=req.getParameter("classId"); resp.getWriter().write("userId="+userId+",classId="+classId); } }
浏览器构造如下请求。
2.4 代码示例:获取POST请求中的参数
POST请求的body格式主要有以下三种:
1.x-www-form-urlencoded
2.form-data
3.json
最为常见的是第一种和第三种,我们可以通过form表单或者ajax的方式来构造出这样的POST请求。
1)基于form表单构造x-www-form-urlencoded这种格式的请求。
<!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="postParameter" method="post" accept-charset="utf-8"> <span>userId</span> <input type="text" name="userId"> <span>classId</span> <input type="text" name="classId"> <input type="submit" value="提交"> </form> </body> </html>
服务器获取参数的方式和GET方法一样,也是getParameter。
@WebServlet("/postParameter") public class PostGetParameterServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置请求与响应中的编码格式 req.setCharacterEncoding("utf-8"); resp.setContentType("text/html; charset=utf8"); //获取post请求中的参数 String userId=req.getParameter("userId"); String classId=req.getParameter("classId"); resp.getWriter().write("userId="+userId+",classId="+classId); } }
测试效果演示:
2)基于ajax构造json这种格式的请求.
1.在浏览器的前端代码中,通过js构造出body为json格式的请求
<!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> <!-- 前端HTML部分内容 --> <input type="text" id="userId"> <input type="text" id="classId"> <input type="button" id="submit" value="提交"> <!-- 导入jQuery --> <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script> <script> let userIdInput=document.querySelector('#userId'); let classIdInput=document.querySelector('#classId'); let button=document.querySelector('#submit'); button.onclik=function() { $.ajax({ type:'post', url:'postJson', contentType:'application/json', data:JSON.stringify({ userId:userIdInput.value, classId:classIdInput.value }), success:function(body) { console.log(body); } }) } </script> </body> </html>
2.因为json格式的数据,不便于手动解析,所以在Java后端代码中,通过Jackson来进行处理(在maven中央仓库引入Jackson依赖)
后端代码:
class User { public int userId; public int classId; } @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的对象 //第一个参数,表示对哪个字符串进行转换(这个参数可以是一个String,也可以是 // 一个InputStream对象,还可以是一个File对象) //第二个参数,表示要把这个JSON格式的字符串,转换成哪个Java对象 User user=objectMapper.readValue(req.getInputStream(),User.class); resp.getWriter().write("userId:"+user.userId+",classId:"+user.classId); } }
readValue方法解析:
1.先把getInputStream对应的流对象里面的数据读取出来。
2.针对这个json字符串进行解析,从字符串转换成键值对
3.遍历键值对,依次获取到每一个Key,根据这个Key的名字,和类对象Uer里面的属性名字对比(利用反射获取到内部属性),如果匹配就进行赋值,不匹配就跳过
4.当把所有的键值对都遍历过后,此时User对象就被构造好了。
测试效果演示:
3.HttpServletResponse
Servlet中的doXXX方法的目的是根据请求计算得到响应,然后把响应的数据设置到HttpServletResponse对象中。
然后Tomcat就会把这个HttpServletResponse对象按照HTTP协议的格式,转成一个字符串,并通过Socket写回给浏览器。
3.1 核心方法
方法 描述
void setStatus(int sc) 为该响应设置状态码
void setHeader(String name,String value) 设置一个带有给定的名称和值的header,如果name已经存在,则覆盖掉旧的值,可以实现页面的刷新
void addHeader(String name,String value) 添加一个带有给定的名称和值的header,如果name已经存在,不覆盖旧的值,并列添加新的键值对
void setContentType(String type) 设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME字符集)例如,UTF-8
void sendRedirect(String location) 设置指定的重定向位置URL,发送临时重定向响应到客户端
PrintWriter getWriter() 用于往body中写入文本格式数据
OutputStream getOutputStream() 用于往body中写入二进制格式数据
注意:响应对象是服务器要返回给浏览器的内容,这里的重要信息都是程序猿设置的。因此上面的方法都是“写”方法
对于状态码/响应头的设置,要放到getWriter/getOutputStream之前,否则可能设置失效
3.2 代码示例:设置状态码
代码:
@WebServlet("/status") public class StatusServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(404); resp.getWriter().write("hello"); } }
效果预览:
状态码虽然是404,但页面仍然能够显示出内容,就像b站的404页面一样
3.3 代码示例:自动刷新
代码:
@WebServlet("/autoRefresh") public class AutoRefreshServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Refresh","1"); resp.getWriter().write("timeStamp:"+System.currentTimeMillis()); } }
效果预览:
3.4 代码示例:重定向
代码:
@WebServlet("/redirect") public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("https://www.bilibili.com"); } }
效果预览: