目录
一. Servlet运行原理
二. Servlet常用API
1. HttpServlet
1.1. Servlet的生命周期
1.2. Post请求的构造
2. HttpServletRequest
Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成 HttpServletRequest 对象.我们就通过这个类来获取到请求的各个方面的信息,尤其是前端传过来的自定义数据,这个自定义数据的方式有三种,下面来介绍:
下面是获取请求信息的常用方法:
2.1. 获取请求信息
通过上面的API,我们就可以获取请求的各种信息,现在我们构造一个GET请求:在浏览器输入URL:localhost:8080/servlet-FirstHelloWorld/hello123?a=12313&t=777
然后通过HttpServletRequest类的一系列API,我们就可以获取请求的协议版本,方法类型,URL,查询字符串,Header属性等等。其中Header信息的获取先要使用 getHeaderNames方法获取所有的头部信息的所有key
值, 这s是一个枚举对象, 然后在根据 getHeader 方法通过key
值遍历枚举对象获取value
.
String Builder 的用法: http://t.csdn.cn/yyGda
String,String Buffer,String Builder的区别: http://t.csdn.cn/IfUwI
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("/hello123")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应的body格式
resp.setContentType("text/html;charset=utf8");
StringBuilder stringBuilder = new StringBuilder();
// 返回请求协议的名称和版本。
stringBuilder.append("协议版本:");
stringBuilder.append(req.getProtocol());
stringBuilder.append("<br>");
// 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
stringBuilder.append("方法:");
stringBuilder.append(req.getMethod());
stringBuilder.append("<br>");
// 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的层次路径部分。
stringBuilder.append("URL:");
stringBuilder.append(req.getRequestURI());
stringBuilder.append("<br>");
// 返回指示请求上下文的请求 URI 部分(一级路径)。
stringBuilder.append("一级路径:");
stringBuilder.append((req.getContextPath()));
stringBuilder.append("<br>");
// 返回包含在路径后的请求 URL 中的查询字符串。
stringBuilder.append("查询字符串:");
stringBuilder.append(req.getQueryString());
stringBuilder.append("<br>");
// 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。
stringBuilder.append("<h3>获得头部的键值对</h3>");
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = req.getHeader(headerName);
stringBuilder.append(headerName + ":" + headerValue + "<br>");
}
// 在页面上输出信息
resp.getWriter().write(stringBuilder.toString());
}
}
2.2. 前端给后端传输数据的三种方式
2.2.1通过Query String的方式传递
构造GET请求的方法:在浏览器地址栏输入URL
构造Query String的方法: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("/methodOne")
public class getParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null){
System.out.println("username 不存在");
}
if (password == null){
System.out.println("password 不存在");
}
System.out.println("username=" + username + ", password=" + password);
resp.getWriter().write("ok");
}
}
以上是基于程序员了解前端会返回什么样的key值(键值对的属性名)的情况
但 如果不知道value对应的key是什么,比如 不知道密码的 key 是 password 还是 number
也可以通过枚举获取所有的Query String的key与value再将它们一 一打印出来
注意要记得设置响应格式,不然<br>实现不了换行
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("/methodOne")
public class getParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置好响应格式,浏览器才知道是html文本
resp.setContentType("text/html;charset=utf8");
StringBuilder stringBuilder = new StringBuilder();
// 获取请求报头中所有的 key 值 赋值给 queryStringKeys
Enumeration<String> queryStringKeys = req.getParameterNames();
// 但枚举容器里还有 key 值 时,循环继续
while (queryStringKeys.hasMoreElements()){
// 拿到下一个 key 值 赋值给 key
String key = queryStringKeys.nextElement();
// 将key 及其所对应的 value 放到stringBuilder容器中
stringBuilder.append(key + ": " + req.getParameter(key));
stringBuilder.append("<br>");
}
resp.getWriter().write(stringBuilder.toString());
}
}
2.2.2通过body,以form表单的格式传递
以POST请求为例,代码格式与GET请求相似,数据此时是在 HTTP 格式中的 body 部分的, 使用 from 表单进行构造, 此时 body 中的请求内容的格式 (Content-Type) 是 application/x-www-form-urlencode 格式, 在形式上和 query string 是一样的, 后端仍然使用 getParameter 来获取.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 前端通过body,以form表单的形式,将username password传递给服务器
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null){
System.out.println("不存在 username");
}
if (password == null){
System.out.println("不存在 password");
}
resp.getWriter().write("ok");
System.out.println("username =" + username + "; password =" + password);
}
然后我们通过postman构造post请求 postman的用法:http://t.csdn.cn/q9mu4
这里的代码要注意, 需要设置拿到请求的编码格式, 显式的告诉后端代码, 请求使用的是 utf8 编码, 要不然Tomcat 服务器在进行 urldecode 解码时可能会由于编码问题解析错误, 尤其是请求内容中有中文存在的情况下, 也就是说, 我们在写后端代码时, 最好将请求和响应的编码格式都进行设置, 保证前后端解析的统一.
如图,数据含有中文,传递到后端就变成了乱码了
加上这一行代码,即可正常解析
2.2.3通过body,以json的格式传递
实际中,使用json是最常见的,要重点掌握。需要下载第三方库 jackson
下载地址: Maven Repository: com.fasterxml.jackson.core » jackson-databind » 2.14.2 (mvnrepository.com)
将代码复制到pom.xml再刷新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;
class User{
public String username;
public String password;
}
@WebServlet("/getJsonServlet")
public class getJsonServlet extends HttpServlet {
// 使用jackson 最核心的对象就是ObjectMapper
// 通过这个对象,就可以把json字符串解析成 java对象
// 也可以把一个java对象转换成一个 json格式的字符串
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过POST请求的body 传递过来一个json格式的字符串
User user = objectMapper.readValue(req.getInputStream(),User.class);
System.out.println("username = " + user.username + "; password = " + user.password);
resp.getWriter().write("ok");
}
}
详解代码:
首先我们要构造一个ObjectMapper对象,这是使用Json最核心的对象
然后使用这个对象的 readValue()方法
第一个参数用来表示请求的来源, 可以是路径字符串, 也可以是InputSream
对象,
第二个参数表示接收 json 数据的实体类对象.
readValue()方法详解:
1.通过第一个参数,解析 json 字符串,转换成若干个键值对
2.根据第二个参数 User.class 去找到 User里所有的public属性,并依次遍历
.class是类对象,可以理解为这个类的图纸 ~ ~运用了反射的机制,
将这个类中所有的public的属性都展现出来
3.遍历属性,根据属性的名字去上述准备好的键值对里查询,如果这个属性的名字存在对应的value值,就把value赋值到该属性
意思是:如果找到 key == 属性名,就把 属性名 = value (赋值)。
写完后,通过postman构造POST请求,且body是json格式
发送后收到响应 ok ,同时服务器也获取到了信息
3. HttpServletResponse
HttpServletResponse表示一个响应
关键API:
3.1. 设置响应状态码
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("/status")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");
//设置状态码
resp.setStatus(404);
resp.getWriter().write("<h1>404 没找到</h1>");
}
}
3.2. 自动页面刷新
通过header实现自动刷新效果
给HTTP响应中,设置 Refresh:时间
@WebServlet("/refresh")
public class refreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 每隔一秒时,刷新一次
resp.setHeader("Refresh","1");
resp.getWriter().write("time = " + System.currentTimeMillis());
}
}
抓包效果:
响应效果:
也可以设置成格式化的时钟刷新:
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.text.SimpleDateFormat;
@WebServlet("/refresh")
public class refreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset = utf8");
//设置Refresh, 第二个参数表示刷新频率, 单位是秒, 每隔 1s 刷新一次
resp.setHeader("Refresh", "1");
//响应 获取毫秒级别的时间戳
// resp.getWriter().write("时间戳:" + System.currentTimeMillis());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
resp.getWriter().write("格式化时间戳 = " + format.format(System.currentTimeMillis()));
}
}
3.3. 重定向
使用 HttpServletResponse
类中的 sendRedirect
方法, 参数传入重定向的 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("/redirect")
public class Redirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://www.sogou.com");
}
}