[JavaWeb] Detailed explanation of Servlet API——HttpServlet

Let’s study the key APl in Servlet in detail~~
Mainly introduce these three categories:

  • HttpServlet
  • HttpServletRequest
  • HttpServletResponse

1. HttpServlet

1. Polymorphism

The code we write ourselves is executed by Tomcat by inheriting this class and rewriting the methods in it.

That is polymorphism! Example:

Collection class:

List<String> list = new ArrayList<>();
list.add(.......

Multithreading:

class MyThread extends Thread {
    
    
    public void run() {
    
    
    }
}

Servlet is also a good example here~~

        // 调用 Servlet 对象的 service 方法
        // 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
        try {
    
    
            ins.service(req, resp); // 4)
        } catch (Exception e) {
    
    
            // 返回 500 页面,表示服务器内部错误
        }
class Servlet {
    
    
    public void service(HttpServletRequest req, HttpServletResponse resp) {
    
    
        String method = req.getMethod();
        if (method.equals("GET")) {
    
    
            doGet(req, resp); // 调用的是我们自己重写的
        } else if (method.equals("POST")) {
    
    
            doPost(req, resp);
        } else if (method.equals("PUT")) {
    
    
            doPut(req, resp);
        } else if (method.equals("DELETE")) {
    
    
            doDelete(req, resp);
        }
        ......
    }
}

2. Core methods

method name Calling time
init Called once after HttpServlet is instantiated
destory Called once when the HttpServlet instance is no longer in use
service Called when an HTTP request is received
doGet Called when a GET request is received (called by the service method)
doPost Called when receiving a POST request (called by the service method
doPut/doDelete/doOptions/… Called when other requests are received (called by the service method)

When we actually develop, we mainly rewrite the doXXX method, and rarely rewrite init / destruction / service

The timing of calling these methods is called the Servlet life cycle (which describes the process of a Servlet instance from birth to death)

Note: The instance of HttpServlet is only created once when the program starts , instead of re-creating the instance every time an HTTP request is received.


3. Code example: Processing POST requests

Create MethodServlet class

When we specify /hellopaths for both classes, Tomcat itself exits~~

结果:Process finished with exit code 0

The exit code of the process~~
The return value of the main function in C language.
Generally, the exit code is 0 to indicate a normal exit, and other non-0 values ​​indicate an abnormal exit (kill tomcat directly, and the exit code is -1)

Insert image description here

People tell you clearly that these two categories cannot be mapped to the same url-pattern.

@WebServlet("/method") // 在同一个 webapp 里 多个 Servlet 关联的路径 不能相同!
public class MethodServlet extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // super.doPost(req, resp);
        resp.getWriter().write("POST 响应");
    }
}

If it is a GET request, it can be constructed directly in the browser through the URL.

Construct a POST request:

  1. form
  2. ajax

Insert image description here

<!-- jquery cdn -->
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    $.ajax({
      
      
        type:'post',
        url: 'method',
        success: function(body) {
      
      
            console.log(body);
        }
    });
</script>

Insert image description here

Start and observe the response:

Insert image description here

The response we expected was: "POST response", but garbled characters appeared!!!
In the current scenario, the string mainly has two links:

  • Generate (write this string in IDEA through hard coding)

  • Display (the browser displays this string to the console)

If these two operations cannot be performed according to a unified encoding method, garbled codes will easily appear~~

The post method we wrote above:
The encoding here depends on what encoding IDEA currently uses to organize our projects . Generally, the default is utf8~~ You can see it by searching for encoding in settings

Browser display string:
By default, it usually follows the system's default encoding~~ (for Windows Simplified Chinese version, the usual default encoding is gbk), explicitly telling the browser that you should not read it in the gbk way. Read according to utf8!!

Solution : In the post method, add:

resp.setContentType("text/html; charset=utf8");

2. Postman

Is there a simpler way to construct a POST request without writing code?
There are a series of third-party tools that can construct any HTTP request . One of the more well-known ones is postman.
Postman was originally a plug-in of chrome. It accidentally became popular, and now I have a separate postman program, and I also have a partner! (postwoman)

Insert image description here


3. HttpServletRequest

  • When Tomcat reads the HTTP request (string) through the Socket API, and parses the string into an HttpServletRequest object according to the format of the HTTP protocol
  • HttpServletRequest corresponds to an HTTP request, and whatever is in the HTTP request, it is here.
    HttpServletResponse corresponds to an HTTP response, and whatever is in the HTTP response, it is here.

1. Core method

Through these methods, you can obtain various aspects of information in a request.
Note: The request object is the content received by the server and should not be modified. Therefore, the above methods are only "read" methods, not "write" methods.

method describe
String getProtocol() Returns the name and version of the requested protocol.
String getMethod() Returns the name of the requested HTTP method, for example, GET, POST, or PUT.
String getRequestURI() Returns a portion of the request's URL in the query string starting from the protocol name up to the first line of the HTTP request.
String getContextPath() Returns the portion of the request URI indicating the request context .
String getQueryString() Returns the query string contained in the request URL following the path .
Enumeration getParameterNames() Returns an enumeration of String objects containing the names of the parameters included in this request.
String getParameterNames() Returns the value of the request parameter as a string, or null if the parameter does not exist.
String[] getParameterValues(String name) Returns an array of string objects containing the values ​​of all given request parameters, or null if the parameter does not exist.
Enumeration getHeaderNames() Returns an enumeration containing all header names included in this request.
String getHeader(String name) Returns the value of the specified request header as a string.
String getCharacterEncoding() Returns the name of the character encoding used in the request body.
String getContentType() Returns the MIME type of the request body, or null if the type is not known.
int getContentLength() Returns the length of the request body in bytes and the input stream provided, or -1 if the length is unknown.
InputStream getInputStream() Used to read the body content of the request. Returns an InputStream object

getRequestURI()The method name is URI, not URL. These are actually two concepts, but they are very similar and can even be used interchangeably.

getQueryStringThe result is a complete query string in the form of ?a=10&b=20. The following two methods are equivalent to parsing the query string into key-value pairs .
getParameterNamesThey get all the keys , expressed in the form of EnumgetParameter , and get the value based on the key.

getHeaderNames, getHeaderobtain the request header , which is also a key-value pair structure. Here the servlet also parses the request header and obtains the key-value pair structure.

getInputStreamThis will get an input stream object . Reading data from this object actually reads the body of the request.


1.1. Code example: Print request information

Create ShowRequestServlet class

@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // super.doGet(req, resp);
        // 调用刚才涉及的几个关键 API,把结果组织到一个 html 中,并作为响应的 body
        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.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());
    }
}

Visit http://127.0.0.1:8080/hello102/showRequest

result:

Insert image description here

Change to http://127.0.0.1:8080/hello102/showRequest?a=10, queryString is no longer null, but a=10

  • Every time we modify the code, we need to repackage and deploy it to take effect. Is there
    a mechanism that automatically repackages and deploys as soon as the code is modified? Wouldn't it save trouble?
  • Yes!!! Creatures like programmers hate "repetitive" operations!! ——Hot reloading

2. Get the parameters in the GET request

The above API allows us to get all aspects of the HTTP request~~ but it is not so commonly used
. The more commonly used method is actually getParameterthis method (getting the detailed content in the query string)

  • The parameters in the GET request are generally passed to the server through query string, in the form of

  • https://v.bitedu.vip/personInf/student?userId=1111&classId=100

  • At this time, the browser passes two parameters to the server through query string, userId and classId, the values ​​​​are 1111 and 100 respectively.

  • On the server side, you can get the parameter value through getParameter.

Create GetParameterServlet class

@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // super.doGet(req, resp);
        // 预期浏览器传来的请求:/getParameter?userId=123&classId=789
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId=" + userId + ", classId=" + classId);
    }
}

When there is no query string, the value obtained by getParameter is null.

  • Visit: http://127.0.0.1:8080/hello102/getParameter

    Result: userId=null, classId=null

  • Visit: http://127.0.0.1:8080/hello102/getParameter?userId=10&&classId=20

    Result: userId=10, classId=20


3. Get the parameters in the POST request

The parameters of POST requests are generally passed to the server through the body . There are many data formats in the body.

POST request body format:

  1. x-www-form-urlencoded
  2. form-data
  3. json

3.1. The body in the POST request is in the form of a form.

If it is in the form of a form, how does the server get the parameters? You can still get the parameter value through getParameter like GET.

Create PostGetParameterServlet class

@WebServlet("/postGetParameter")
public class PostGetParameterServlet extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // super.doPost(req, resp);
        // 假设前端传过来的参数是:userId=10&classId=20
        // 服务器也是通过 req.getParameter 来获取内容的
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId=" + userId + ", classId=" + classId);
    }
}

Construct a POST request:

Note: test.html is in the webapp directory, which is in the same directory as WEB-INF , not inside WEB-INF
. If you put test.html in the wrong location, there is a high probability that you will get 404~

Insert image description here

<form action="postGetParameter" method="post">
    <input type="text" name="userId">
    <input type="text" name="classId">
    <input type="submit" value="提交">
</form>

<!-- jquery cdn -->
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>

</script>

Visit: http://127.0.0.1:8080/hello102/test.html

Insert image description here

Result: userId=111, classId=222

At this time, you can see through packet capture that the format of the body data constructed by the form is:

POST http://127.0.0.1:8080/hello102/postGetParameter HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:8080/hello102/test.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

userId=111&classId=222

3.2. The body in the POST request is in JSON format.

{
    
    
	userld: 1234,
    classld: 5678
}

For this format where the body is json, it is not easy to parse it manually (fields in JSON can be nested ~)
In this case, manual processing is more troublesome. You can use a third-party library to Directly process json format data~~

In the Java ecosystem, there are many third-party libraries used to process JSON.
The library we use is called Jackson (the library officially recommended by Spring)

Download the Jackson library locally through maven and introduce it into the project (still in the dependencies tag of pom.xml)~~

Insert image description here

Insert image description here

1). In the browser front-end code, construct a request with the body in json format through js.

<!-- 要想构造一个 json 格式的请求, 就不再使用 form 而是使用 ajax 了 -->
<input type="text" id="userId">
<input type="text" id="classId">
<input type="button" value="提交" id="submit">

<!-- jquery cdn -->
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    let userIdInput = document.querySelector("#userId");
    let classIdInput = document.querySelector("#classId");
    let buttom = document.querySelector("#submit");
    buttom.onclick = 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>

For requests like post, ajax allows the use of dataattributes to construct the body part of the request
. The content to be constructed here is actually a js object.

This js object needs to be converted JSON.stringifyfrom the js object into a string
. The format of this string is exactly the format of json.

—— {“userld”:“123”,“classld”:“456”}

2). In the java back-end code, process it through jackson

You need to use jackson to read the data in the request body and parse it into objects in Java

User user = objectMapper.readValue(req.getInputStream(), User.class);

readValue(): Convert JSON-formatted strings into Java objects

  • The first parameter: Indicates which string to convert. This parameter can be filled in as a String / an InputStream object / a File object
  • The second parameter: Indicates which Java object you want to convert this JSON format string into

Above we said that the format of Json is as follows: - {"userld": "123", "classld": "456"} This string is obtained
throughgetInputStream()

So how is readValue converted?

  1. First readgetInputStream all the data in the corresponding stream object

  2. Parse this json string , from string => key-value pair

    • key: userld; value: 123

    • key: classld; value: 456

    • Here it is required that the attribute name of the User class must match the name of the key in the key-value pair~~
      (name matching is required, this is just the default behavior of Jackson). If you insist on making a mismatched name, that is not the case. Can't

  3. Traverse this key-value pair, and obtain each key
    in turn according to the name of this key and the attribute name in the User class. Compare it~~ to see if there is a matching name!!

    • If a matching attribute is found , the value corresponding to the current key is assigned to the attribute of the User class~~ ( Type conversion is also performed during the assignment process )
    • If there is no matching attribute, skip it and get the next key ~~
  4. After all key-value pairs have been traversed , the User object is almost constructed~~

class User {
    
    
    // 当前都设为 public,如果是 private,但是同时提供了 getter setter,效果等同
    public int userId;
    public int classId;
}

@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
    
    
    // 1、创建一个 Jackson 的核心对象
    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    
    
        // 2、读取 body 中的请求,然后使用 ObjectMapper 来解析成需要的对象
        User user = objectMapper.readValue(req.getInputStream(), User.class);
        // 源代码是.java => 二进制字节码.class => 被加载到内存中就成为了 class 对象
        // getInputStream():读取请求的 body 内容. 返回一个 InputStream 对象
        resp.getWriter().write("userId: " + user.userId + ", classId: " + user.classId);
    }
}

After clicking Submit, you can see that the server's response data is printed in the browser console~~
Currently, the ajax method is used to submit data. This operation will not cause a page jump by default , just like we use form The styles are very different~~

Insert image description here

Capturing the packet can observe the format of the body data:

POST http://127.0.0.1:8080/hello102/postJson HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 32
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
Accept: */*
Content-Type: application/json
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
sec-ch-ua-platform: "Windows"
Origin: http://127.0.0.1:8080
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:8080/hello102/test.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

{"userId":"111","classId":"222"}

Postman constructs JSON request

Insert image description here


四、HttpServletResponse

  • The purpose of the doXXX method in Servlet is to calculate the response based on the request , and then set the response data into the HttpServletResponse object
  • Then Tomcat will convert this HttpServletResponse object into a string according to the HTTP protocol format , and write it back to the browser through Socket

1. Core method

method describe
void setStatus(int sc) Set the status code for this response.
void setHeader(String name, String value) Sets a header with the given name and value. If name already exists, the old value is overwritten .
void addHeader(String name, String value) Adds a header with the given name and value. If name already exists, the old value is not overwritten and a new key-value pair is added.
void setContentType(String type) Sets the content type of the response sent to the client .
void setCharacterEncoding(String charset) Sets the character encoding (MIME charset) of the response sent to the client. For example, UTF-8.
void sendRedirect(String location) Sends a temporary redirect response to the client using the specified redirect location URL.
PrintWriter getWriter() Used to write text format data into the body.
OutputStream getOutputStream() Used to write binary format data into the body

addHeaderFor example, Set-Cookie can have multiple Set-Cookie keys in one response.

sendRedirectConstruct a 302 redirect response

getWriter getOutputStreamWriting data into the body is more about using text.

  • Note: The response object is the content to be returned by the server to the browser. The important information here is set by the programmer, so the above methods are all "write" methods.
  • Note: The status code/response header settings must be placed before getWriter/getOutputStream, otherwise the settings may be invalid.

2. Code example: setting status code

Create StatusServlet class

——200:

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // super.doGet(req, resp);
        resp.setStatus(200);
        resp.getWriter().write("hello");
    }
}

Visit: http://127.0.0.1:8080/hello102/status

Display: hello

Packet capture results:

HTTP/1.1 200
Content-Length: 5
Date: Sat, 21 May 2022 13:30:30 GMT
Keep-Alive: timeout=20
Connection: keep-alive

hello

——404:

resp.setStatus(200);

Redeploy, access

Display: hello

Developer tools display: GET http://127.0.0.1:8080/hello102/status 404

Packet capture results:

HTTP/1.1 404
Content-Length: 5
Date: Sat, 21 May 2022 13:33:09 GMT
Keep-Alive: timeout=20
Connection: keep-alive

hello

Note: The status code returned by the server only tells the browser what the current response status is , and does not affect the browser's ability to display the content in the body as usual~~


3. Code example: automatic refresh

Implement a program that allows the browser to automatically refresh once every second and display the current timestamp

For example "text live broadcast":

This was around ten years ago, when 4G and 5G had not yet come out, and data traffic was still very expensive. It was unrealistic to use mobile phones to watch videos/watch live broadcasts. A popular method at the time was text live broadcasts.

For various competitions, there were some special text live broadcast platforms at that time, with staff, and the situation at the competition site was described in text, which often
involved page refresh~~

Create AutoRefreshServlet class

@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());
    }
}

Visit: http://127.0.0.1:8080/hello102/autoRefresh

Display result: timeStamp: 1653140698925
This number is constantly changing every second or so.

Packet capture results:

HTTP/1.1 200
Refresh: 1
Content-Length: 24
Date: Sat, 21 May 2022 13:48:02 GMT
Keep-Alive: timeout=20
Connection: keep-alive

timeStamp: 1653140882691

4. Code example: redirection

Implement a program that returns a redirect HTTP response and automatically jumps to another page

Create the RedirectServlet class

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 返回一个 302 重定向,让浏览器自动跳转到 sogou 主页
        resp.setStatus(302);
        resp.setHeader("Location", "https://www.sogou.com");
    }
}

Packet capture results:

Insert image description here

HTTP/1.1 302
Location: http://www.sogou.com
Content-Length: 0
Date: Mon, 21 Jun 2021 08:17:26 GMT
Keep-Alive: timeout=20
Connection: keep-alive

Servlet provides a simpler way to implement redirection:

resp.sendRedirect("https://www.sogou.com");
  • Just understanding the Servlet API is not enough to support us in writing a fully functional website.
  • You also need to understand what the development process of a website is like, and understand some basic programming thinking and design ideas here~~ (strengthened through more cases)

Guess you like

Origin blog.csdn.net/qq_56884023/article/details/125684506