目录
前言
HttpServlet
HttpServletRequest
代码实例
打印请求信息
通过URL中的queryString进行传递。
通过post请求的body,使用form表单传递
通过POST 请求中的 body 按照 JSON 的格式进行传递
HttpServletResponse
核心方法代码实例
设置状态码
自动刷新
重定向
总结
Servlet的API是非常多的,但是我们只需要重点掌握三个类即可!!!
我们在写Servlet代码的时候,第一步都是先创建一个类,然后让这个类去继承HttpServlet,并重写其中的某些方法。那么我们就需要知道,HttpServlet这个类中都有那些方法,都是干啥的。
方法名称 | 调用时机 |
---|---|
init | 在HttpServlet实例化之后被调用一次 |
destroy | 在HttpServlet实例之后不再使用的时候调用一次 |
service | 在收到HTTP请求时调用 |
doGet | 在收到get请求的时候由service调用 |
doPost | 在收到post请求的时候由service调用 |
doPut/doDlete/...... | 在收到其他请求时由service调用 |
@WebServlet("/hello1")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello world");
resp.getWriter().write("hello");
}
}
init():
HttpServlet被实例化之后,会调用一次,使用这个方法来做一些初始化的工作。
需要注意的是,init方法不是被实例化的时候调用,而是首次收到请求的时候调用。
这个请求就会触发HelloServlet类的doGet方法的执行。但是会在执行doGet方法之前,先调用init方法。
init方法的调用时机:
只会在首次收到请求的时候调用一次,等下次再次收到请求,就不会再调用init方法了。也就是说,init方法在Servlet整个生命周期中,只会调用一次。
destroy():
这个方法时在HttpServlet实例销毁之前调用一次,来做一些收尾工作。
这个方法需要注意的是,如果通过Servlet的管理端口8005来停止Servlet服务,此时的destroy方法就会执行,如果要是通过直接杀死进程的方式来停止Servlet服务,那么destroy就不会执行。
service():
service方法是当收到一个路径匹配的请求时,就会执行一次。
我们的doGet/doPost/doDelete....等方法,都是在service方法中进行调用的。
所以我们在重写方法的时候,一般不会去重写service方法,而是重写doXXX方法。
一道面试题:Servlet的生命周期?
上述方法的调用时机,就成为Servlet的生命周期。
这个类对应一个HTTP请求,一个HTTP请求中有什么,这个类中就有什么。
这个类中的方法是比较多的,但是都比较清晰。
方法 | 描述 |
String getProtocol() | 返回请求协议的名称和版本。 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请 求的 URL 的一部分。 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分。 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串。 |
Enumeration getParameterNames() |
返回一个 String 对象的枚举,包含在该请求中包含的参数的名 称。 |
String getParameter(String name) |
以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
String[] getParameterValues(String name) |
返回一个字符串对象的数组,包含所有给定的请求参数的值,如 果参数不存在则返回 null。 |
Enumeration getHeaderNames() |
返回一个枚举,包含在该请求中包含的所有的头名。 |
String getHeader(String name) |
以字符串形式返回指定的请求头的值。 |
String getCharacterEncoding() |
返回请求主体中使用的字符编码的名称。 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
int getContentLength() | 以字节为单位返回请求主体的长度,并提供输入流,或者如果长 度未知则返回 -1。 |
InputStream getInputStream() |
用于读取请求的 body 内容. 返回一个 InputStream 对象. |
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("/ShowRequest")
public class ShowRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
StringBuilder result = new StringBuilder(); //使用StringBuilder来显示请求的内容
result.append(req.getProtocol()); //请求的协议和版本
result.append("
");
result.append(req.getMethod()); //请求的方法
result.append("
");
result.append(req.getRequestURI()); //请求的uri 唯一资源标识符
result.append("
");
result.append(req.getQueryString()); //请求的queryString
result.append("
");
result.append(req.getContextPath()); //请求URL的上下文路径
result.append("
");
result.append("=========================
");
Enumeration headerNames = req.getHeaderNames(); //请求的header头名,返回值是一个枚举类型
while (headerNames.hasMoreElements()) { //遍历这个headerNames枚举对象
//header中是一个个的键值对
String headerName = headerNames.nextElement(); //获取到header中键值对的键
String headerValue = req.getHeader(headerName); //通过键值对中的键获取到对应的值
result.append(headerName+": " + headerValue+"
");
}
//设置响应到浏览器的类型和字符格式
resp.setContentType("text/html;charset=utf8");
//把响应写会浏览器
resp.getWriter().write(result.toString());
}
}
上述在进行append的时候,我们并不是使用的\n来表示换行,因为我们返回的String在浏览器页面上是以HTML的格式进行解析的,所以我们要想换行,就得使用HTML中的换行标签。
Enumeration headerNames = req.getHeaderNames(); //请求的header头名,返回值是一个枚举类型
while (headerNames.hasMoreElements()) { //遍历这个headerNames枚举对象
//header中是一个个的键值对
String headerName = headerNames.nextElement(); //获取到header中键值对的键
String headerValue = req.getHeader(headerName); //通过键值对中的键获取到对应的值
result.append(headerName+": " + headerValue+"
");
}
上述代码则是把整个header中内容全部拼接到stringbuilder中
下面来看看运行结果。
接下来介绍下一组API
获取get请求中的值
前端先后端传递值的方法有多种。
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("/getParameter")
public class getParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//前端通过URL中的query String 来传递username和password两个属性
String username = req.getParameter("username");
if (username == null) {
System.out.println("username 这个key在queryString中不存在");
}
String password = req.getParameter("password");
if (password == null) {
System.out.println("password 这个key在queryString中不存在");
}
System.out.println("username " + username + " password" + password);
resp.getWriter().write("ok");
}
}
我们知道QueryString是键值对的方式来向后端传递数据的。
比如前端通过QueryString的方式传递username和password两个属性。我们要获取这两个属性中对于的value。就可以通过getParameter()方法来获取键对应的值。
下面我们运行程序,来看效果。
我们在URL中通过QueryString的方法向后端传递了两个键值对,分别是username=zhangsan,
password=123。
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("/getParameter")
public class getParameter extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
//前端通过body form表单 传递username和password
String username = req.getParameter("username");
if (username == null) {
System.out.println("username 这个key在queryString中不存在");
}
String password = req.getParameter("password");
if (password == null) {
System.out.println("password 这个key在queryString中不存在");
}
System.out.println("username " + username + " password" + password);
resp.getWriter().write("ok");
}
}
我们通过Postman来构造通过form表单传递数据的请求。
可以看出服务器成功的返回了一个ok。这个OK并不能代表什么,我们来看看服务器控制台的输出。
服务器也成功的打印出来了对应的value。
我们需要引入 Jackson 这个库, 进行 JSON 解析。
1:在Maven中央仓库搜索Jackson,选择Jackson Databind
然后选择版本2.15.0
2:把中央仓库中的依赖配置添加到 pom.xml 中, 形如
com.fasterxml.jackson.core
jackson-databind
2.15.0
然后就可以编写代码了。
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("/JSON")
public class JsonServlet extends HttpServlet {
// 创建 ObjectMapper 对象. 这个是 Jackson 中的核心类
public ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过post请求的body传递过来一个JSON格式请求的字符串
// 通过 readValue 方法把 body 这个字符串转成 User对象
User user = objectMapper.readValue(req.getInputStream(), User.class);
System.out.println("username "+ user.username +", password"+user.password);
resp.getWriter().write("ok");
}
}
可以看出我们代码中用多了一个User类,这个类用于解析生成后的JSON对象,这个类中的属性名字要和传递过来的key对应。
User user = objectMapper.readValue(req.getInputStream(), User.class);
通过反射的机制把body中的key对应的value全部放在User对象中去,这就是为什么User类的属性和类型、名称都要和body中的key保持一致的原因。
然后就可以通过user对象来获取到username和password两个属性的value了。
下面我们运行代码看效果:
我们通过Postman构造了JSON的请求并发送,服务器也成功的响应了。
接下来我们看服务器控制台输出的内容。
服务器也是成功的获取到了key对应的value。
这个类是一个HTTP的响应,一个响应中有什么,这个类中就有什么。
Servlet中的doXXX方法就是根据请求计算响应,然后把响应的数据写回到HttpServletResponse这个对象中。
然后Tomcat会把这个对象按照HTTP协议相应的格式,转成一个字符串,并通过socket写回给浏览器。
方法 | 描述 |
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 中写入二进制格式数据. |
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 Status extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(200); //给响应设置状态码
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("返回设置状态码200");
}
}
服务器返回数据,下面我们通过fiddler来抓包看看。
可以看到,状态码确实设置为了200。
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("/Refresh")
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//在响应里面设置自动刷新字段 1秒刷新一次
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;
@WebServlet("/redirect")
public class Redirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//当用户访问这个路径的时候,自动重定向到百度的主页
resp.setStatus(302); //设置重定向状态码302
resp.setHeader("location","https://www.baidu.com");
}
}
当我们在地址栏输出URL时,就会重定向到百度的页面。
以上就是Servlet API的讲解,不足之处,希望各位大佬多多指教。