HttpSerlvet类:
核心方法:
方法名 | 调用时机 |
---|---|
init | 在 HttpServlet 实例化之后被调用一次(类似懒汉模式;实例化再创建、调用.) |
destory | 在 HttpServlet 实例不再使用的时候调用一次 |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
doPost | 收到 POST 请求的时候调用(由 service 方法调用) |
doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由 service 方法调用) |
init方法
init方法:tomcat在收到我们的注解里写的路径请求时;就会调用hello_Servlet进行实例化(实例化只进行一次;后续继续收到这样的路径请求就直接利用之前实例化的即可)。也可以让服务器启动时就执行;修改web.xml配置;让tomcat立即实例化servlet;但是当前我们无所谓的。
我们将这个方法重写;增加一些我们相关逻辑测试一下:只要服务器tomcat不重新启动;init就不会再执行、
destory
当我们结束这个服务器时;就会执行这个方法里的内容;有时候destory未必能被执行到。取决于你停止服务器的姿势。
smart tomcat的停止按钮:这个操作本质上是通过tomcat的8005端口主动停止;能够触发destroy
直接杀进程:可能来不及执行destroy就没了;所以这个方法并不太靠谱;都不一定能执行到
service
收到http请求就触发;这个方法会根据你的请求是get还是post等等;决定调用哪个。doget方法t等就是在service中调用的。
servlet生命周期
虽然这三个方法我们不太直接会用到;但是它们却是依然无比的重要。
servlet生命周期:什么时候创建;什么时候消失(一个servlet程序可以有很多个servlet;单个servlet的生死不影响整个servlet程序)
1:开始时;执行init
2:每次收到请求;执行service
3:销毁之前执行destroy
演示(构建一个POST请求示例)
两种方式:postman;ajax。
ajax:
首先我们在webapp目录下创建一个html页面:然后使用vscode打开。idea这个html下右击Open In选择Explore
1:写标签
2:引入jquery ;进入官网http://releases.jquery.com/;现在3.x版本;选择minified右击;复制链接。
将此链接复制粘贴入src ;https://code.jquery.com/jquery-3.6.4.min.js
3:编写我们的请求
这个URL得写小写的url。上述写URL是不行的。
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.6.4.min.js">
</script>
<script>
$.ajax({
type:'post',
// 相对路径;相当于在http://127.0.0.1:8080/servlet_2基础上拼上一个hello
// 还可以写绝对路径;'/servlet_2/hello' 前面带/是绝对路径;不带/相对路径。http的url是网络路径和盘符无关;文件系统的绝对路径带盘符
// 访问的是hello这个servlet构成的动态页面。
url:'hello' ,
success:function(body,status) {
console.log(body);
}
}
)
</script>
</body>
</html>
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("/hello")
public class hello_servlet extends HttpServlet {
@Override
public void init() throws ServletException {
//super.init();
System.out.println("init");
}
//servlet api提供现成的类;写servlet代码一般继承这个类
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//我们写这个doGet方法不需要我们去调用;而是自动由tomcat在收到get请求时调用
//super.doGet(req, resp);
resp.getWriter().write("hello world");
System.out.println("doget");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
resp.getWriter().write("哈哈 world");
System.out.println("dopst");
}
}
效果:
启动方式:
先梳理一下几个路径:
http://127.0.0.1:8080/servlet_2/hello 这个servlet程序对应的servlet资源;
C:\mayun\javase_review\servlet_2\src\main\webapp\test.html 这个是我们在webapp上的前端代码;构造POST请求的地方。
因为第一次学习ajax构造POST请求;写的是搜狗的服务器;还没有学习tomcat;所以是双击页面启动就能成功发送请求(看你要请求的资源在哪;像我们写的.html的资源是放在tomcat服务器上的;我们虽然URL本质是发一个get请求获取这个html资源;然后通过这个html里面的代码发其它自定义请求和响应处理。)
这种ajax构造请求确实也是双击浏览器打开就能发送请求。虽然可以写完整的路径 http://127.0.0.1:8080/servlet_2/hello ;这样子我们就不需要通过tomcat启动test.html 也能发送成功。但是违背我们的初衷跨主机交互。如果我把程序打包给别人使用;别人依然没法使用;因为我们得把test.html带上;别人才可以访问。所以干脆把它们放一起;使用tomcat启动。
因为我们这里写的是相对路径 url:‘hello’ 。如果我使用C:\mayun\javase_review\servlet_2\src\main\webapp\test.html启动发送请求;发现我们tomcat服务器收不到请求;这里的基准路径变化了;服务器就不是tomcat的。所以需要用tomcat来启动。(总结;这里URL写绝对路径也是能通过双击的方式成功把请求发送)
localhost:8080/项目名/xxx.html
需要注意:ajax相对路径就不需要带/;绝对路径需要带/。后端注解则必须要带/;servlet要求
HttpServletRequest 请求处理
tomcat自动构造的;tomcat会实现监听、接收连接、读取请求、解析请求、构造请求等一系列操作
前端给后端三种传参方式解析(GET,query string;POST,form;POST,json)
GET,query string:req.getQueryString();
直接获取到a=b这个字符串;要具体内容你得自己拆分。如果多个;获取到字符串a=b&c=d。或者通过下面的方法获取对应的值
示例1:通过url请求;响应的结果是将这些属性拼接显示浏览器上
//测试三种数据格式的读取响应
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("/ServletTest")
public class ServletTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
//返回的结果是字符串;我们把它拼接起来;最后写到浏览器
StringBuilder stringBuilder=new StringBuilder();
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.toString()写在浏览器的body部分;所以我们使用br才是显示换行
resp.getWriter().write(stringBuilder.toString());
//告诉浏览器;响应body的格式是什么样的;得按照html格式解析
resp.setContentType("text/html");
}
}
效果:
POST,form:getParameter/
2:form表单:如果是form表单post请求;里面的格式数据也是键值对;只是这部分内容在body里。
getParameter即可获取query string的键值对;还能获取到form表单构造body中键值对。整体过程:前端from提交到tomcat服务器;tomcat构造req和resp对象;我们通过req对象获取其中内容;通过resp编写响应;tomcat发送响应给浏览器。
使用枚举方式返回处理;获取里边的键值对;跟迭代器很相似:
form请求的构建:
//测试三种数据格式的读取响应
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.Enumeration;
@WebServlet("/ServletTest")
public class ServletTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
//返回的结果是字符串;我们把它拼接起来;最后写到浏览器
StringBuilder stringBuilder=new StringBuilder();
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.toString()写在浏览器的body部分;所以我们使用br才是显示换行
resp.getWriter().write(stringBuilder.toString());
//告诉浏览器;响应body的格式是什么样的;得按照html格式解析
resp.setContentType("text/html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
StringBuilder stringBuilder2=new StringBuilder();
// 返回请求的所有头名;我们把这个头名打印到浏览器。
Enumeration<String> headerNames=req.getHeaderNames();
while (headerNames.hasMoreElements()){
stringBuilder2.append(headerNames.nextElement());
stringBuilder2.append("<br>");
}
// 通过key获取value;req.getParameter能获取query string和form表单value;key不存在就返回null
stringBuilder2.append(req.getParameter("liao"));
resp.getWriter().write(stringBuilder2.toString());
//告诉浏览器;响应body的格式是什么样的;得按照html格式解析
resp.setContentType("text/html");//注意别写成test;test是测试;text才是文本;
System.out.println("123");
}
}
效果:
POST,json:getInputStream()
3:json:键值对格式;可以把body按照这个格式来组织;比如在postman里构建
InputStream getInputStream()返回一个输入流对象;对这个对象read就把body读出来。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
//处理body为json格式
public class Postman extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
//流对象里面长度是多少;我们是可以获取的;Content-Length
int length=req.getContentLength();
byte[]buffer=new byte[length];
//把读到的东西填充到这个数组里
InputStream inputStream= req.getInputStream();
inputStream.read(buffer);
//然后我们再构造成String;打印出来
String body=new String(buffer,0,length,"utf8");
System.out.println("body:"+body);
//写入浏览器页面去
resp.getWriter().write(body);
}
}
三种方法是等价的;目前使用json格式做法更多。但是json我们只是把整个body读出来;并没有按照键值对方式处理;也就是不能根据key获取value(form表单可以根据key获取value;getParameter支持)
我们可以使用第三方库解析json格式;后面学到的spring mvc支持json格式;内置jackson这个库解析json格式。
jackson(解析json格式的库)
前面说过;maven是管理依赖的;我们创建的是maven项目;就不必像最初的JDBC导入第三方库一样;下载再拖进来。
找到官网:https://mvnrepository.com/search?q=jackson
注意放的位置:刚复制粘贴后;会标红;等待依赖下载完就不会了;如果还是标红就右边maven刷新一下
使用:
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.io.InputStream;
//处理body为json格式
@WebServlet("/Postman_json1")
public class Postman_json1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ObjectMapper objectMapper=new ObjectMapper();
// readValue 就是把一个 json 格式的字符串转成 Java 对象.注意这两个参数分别是啥;转成学生类的对象
//先从body读出json格式字符串;再看转成什么对象;会把json格式解析成键值对方式
//遍历键值对;看键的名字和Student的实例中哪个属性的名字匹配;就把这个值赋予该属性。最后返回这个对象
Student student = objectMapper.readValue(req.getInputStream(), Student.class);
System.out.println(student.studentId + ", " + student.classId);
}
}
class Student {
public int studentId;
public int classId;
}
如果没有请求参数Student没有的字段会怎么样;或者Student的字段比json的参数多出来的会怎么样???
writeValue:把java对象转成json格式字符串;
HttpServletResponse 响应处理
核心方法:
状态码
状态码的效果;就什么效果也没有;并不会产生我们对应响应效果;只是一个标识规定而已。比如我们在做统一功能处理的时候;我可以统一给你返回的都是状态码200;但是你响应内容得提示用户是不是出错;还是正常响应页面。需要自己在响应里做出相应的处理。
header
针对响应进行操作:
set:设置一个header;如果name存在就替换
add:增加一个header;如果name存在就增加一个新的;而不是覆盖。所以应该http响应中的报头key是可以存在多个重复的
编码与ContentType
设置编码: resp.setCharacterEncoding(“utf8”)
如果不设置;浏览器对中文不知道是哪种编码理解;浏览器是胡乱猜的编码理解(浏览器每次猜的都是一样;就是错也会一直错下去);我们得告诉浏览器响应的编码格式。
设置ContentType: resp.setContentType(“text/html”);
注意:设置字符集和ContentType得在write上面;写在write下面是不会生效的。两个一起设置也行resp.setContentType(“text/html”;“utf8”);
构造重定向响应
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Response extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(302);
resp.sendRedirect("https://www.sogou.com");
//resp.sendError(404);//返回tomcat自带错误页面;我们也可以设置状态码;然后自己写错误信息返回写入到body显示浏览器的页面
}
}
抓包效果:这里的时区是美国的;跟我们这里是不一样的。状态码的描述不是必须的。loaction代表的是我们要定向到那个路径。