上一篇Java for the Web With Servlets, JSP, and EJB(Part1-Chapter2)javax.servelt和jakarta.servlet
软件版本:
- jakarta.servlet/jakarta.servlet-api/6.0.0
- tomcat 10.1.19
- jdk-11.0.20
一、HttpServlet
1.1 7个doXxx方法
-
doHead,doPost, doPut, doGet, doDelete, doOptions and doTrace
-
GET在HTTP请求中是默认的方法
-
(在上一篇中,浏览器输入网址访问某个servlet时没有指定方法,按f12打开DevTools,可以看到默认方法就是GET)
1.2 HttpServlet源码片段
1.2.1 类定义使用abstract class
抽象类——不能进行实例化(new)的类。想要用abstract class里面的变量和函数,就需要自定义一个继承该类的子类。
package http;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class PostHttpServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
out.println("doPost");
}
}
1.2.2 重写了servlet的service函数
在service函数中对request报文头method进行判断。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
1.2.3 除了HEAD/OPTIONS/TRACE,其他method默认是不允许使用的
GET/POST/PUT/DELETE默认状态都是调用sendMethodNotAllowed。如果HttpServlet的子类没有重写对应的doXxx方法,则默认返回的都是“sendMethodNotAllowed——该方法不允许访问”。
所以如果需要这个servlet能处理该方法,就得在HttpServlet的子类中重写,否则就会报“该方法不允许访问”。
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (!DispatcherType.INCLUDE.equals(req.getDispatcherType()) && this.cachedUseLegacyDoHead) {
NoBodyResponse response = new NoBodyResponse(resp);
this.doGet(req, response);
if (req.isAsyncStarted()) {
req.getAsyncContext().addListener(new NoBodyAsyncContextListener(response));
} else {
response.setContentLength();
}
} else {
this.doGet(req, resp);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_put_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_delete_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
String protocol = req.getProtocol();
if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String allow = this.getCachedAllowHeaderValue();
if (HttpServlet.TomcatHack.getAllowTrace(req)) {
if (allow.length() == 0) {
allow = "TRACE";
} else {
allow = allow + ", TRACE";
}
}
resp.setHeader("Allow", allow);
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String CRLF = "\r\n";
StringBuilder buffer = (new StringBuilder("TRACE ")).append(req.getRequestURI()).append(' ').append(req.getProtocol());
Enumeration<String> reqHeaderNames = req.getHeaderNames();
while(true) {
String headerName;
do {
if (!reqHeaderNames.hasMoreElements()) {
buffer.append(CRLF);
int responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
return;
}
headerName = (String)reqHeaderNames.nextElement();
} while(this.isSensitiveHeader(headerName));
Enumeration<String> headerValues = req.getHeaders(headerName);
while(headerValues.hasMoreElements()) {
String headerValue = (String)headerValues.nextElement();
buffer.append(CRLF).append(headerName).append(": ").append(headerValue);
}
}
}
}
1.3 HttpServletRequest
就是说,Http 请求报文里面的数据都能通过这个类获取到。
1.4 HttpServletResponse
就是说,Http 返回报文所有的可能性都在这里可以进行设置。
1.4.1 重定向sendRedirect
通知浏览器,重定向到另一个页面(如用户登录后需要重定向到首页)。
1.4.2 Buffering the Response
先把返回数据放在response缓冲区中,只有缓冲区满了或者request被处理完成了,才开始向客户端(浏览器)返回数据。
1.4.3 关于返回的数据中特殊字符
浏览器接收到返回数据时,会自动当成html文本解析,要注意html中的特殊字符,如果不需要被浏览器解析成html,那么需要进行转编码。
/**
* Encode an HTML tag so it will be displayed
* as it is on the browser.
* Particularly, this method searches the
* passed in String and replace every occurrence
* of the following character:
* '<' with "<"
* '>' with ">"
* '&' with "&"
* //'"' with """
* ' ' with " "
*/
1.4.4 RequestDispatcher 一个servlet需要使用另一个servlet的功能
RequestDispatcher部分源码:
package jakarta.servlet;
import java.io.IOException;
public interface RequestDispatcher {
void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}
- forward
把request 转发到另一个servlet进行处理 - include
使用另一个servlet产生的数据
1.4.5 重定向sendRedirect和RequestDispatcher.forward
- 重定向sendRedirect
servlet1号通知浏览器:“你的诉求俺这里的部分搞定了,接下来你直接去找servlet2号吧!”
这时候servlet2号是拿不到浏览器给servlet1号的request数据的。 - RequestDispatcher.forward
servlet1号内心os:“oh,这事儿还得麻烦我的好兄弟servlet2号,直接把request打包给我的好兄弟”。
这时候servlet2号是直接拿到了servlet1号的request数据。