spring source code analysis (four) Web

spring source code analysis (four) Web

sschrodiner

2018/5/11


servlet Introduction


What is the servlet


Java Servlet is a program running on the Web server or application server, it is used as an intermediate layer between the database or application server and HTTP requests from the Web browser or other HTTP clients.

The use of Servlet, you can collect user input from a Web form, showing records from a database or other source, you can also create dynamic web pages.

Java Servlet general procedures using CGI (Common Gateway Interface, common gateway interface) can be implemented to achieve the same purpose. But compared to CGI, Servlet has the following advantages:

  • Significantly better performance.
  • Servlet execution within the address space of the Web server. So that it is not necessary to create a separate process to handle each client request.
  • Servlet is a platform-independent, because they are written in Java.
  • Java Security Manager on the server to perform a series of restrictions to protect the resources on the server computer. Therefore, Servlet is credible.
  • All features of the Java class libraries are available for Servlet it. It can interact through sockets and RMI mechanism and applets, database or other software.

servlet architecture


The following figure shows the position Servlet Web application.

16678540-357953ef1f9fcb50.jpg
servlet-arch.jpg

Servlet task


Servlet performs the following major tasks:

  • Read client (browser) of the transmitted data explicit. Form HTTP client program that includes HTML form on a web page, or may be derived from or customized applet.
  • Read client (browser) sends implicit HTTP request data. This includes cookies, media types and browsers can understand compression format, and so on.
  • Processing the data and generate a result. This process may need to access the database, the implementation of RMI or CORBA calls, call the Web service, or directly calculated corresponding response.
  • Transmitting the explicit data (i.e., document) to the client (browser). The format of the document can be varied, including text files (HTML or XML), binary files (GIF images, Excel and so on.
  • Sending an implicit HTTP response to the client (browser). This includes tells the browser or other client is returned the document type (eg HTML), set cookies and cache parameters, and other similar tasks.

Servlet 包


Java Servlet is running on a web server with support for Java Servlet specification interpreter of the Java class.

Servlet can use javax.servlet and javax.servlet.http create a package, it is a standard part of the Java Enterprise Edition, Java Enterprise Edition supports large-scale development projects expanded version of the Java class libraries.

Java Servlet just like any other Java class has been created and compiled. After you install the Servlet package and add them to the Classpath class path on your computer, you can compile the Java Servlet by JDK compiler or any other compiler.


servlet life cycle


Servlet life cycle can be defined as the entire process from creation to destruction. The following is the procedure to follow Servlet:

  • Servlet is initialized by calling a method init ().
  • Servlet call service () method to process client requests.
  • Servlet by calling destroy () method is terminated (end).

Finally, Servlet is garbage collected by the JVM's garbage collector.


init () method


The init method is designed to call only once. It is called when you first create a Servlet, it is no longer called upon each subsequent user request. Therefore, it is used for one-time initialization, like the init method of Applet.

When the Servlet to create a user corresponding to the first call to the Servlet URL, but you can also specify the Servlet is loaded when the server is first started.

When the user invokes a Servlet, it will create a Servlet instance, each user request results in a new thread, transfer the appropriate time to doGet or doPost methods. init () method simply create or load some data that will be used throughout the life cycle Servlet.

Init method defined as follows:

public void init() throws ServletException {
  // 初始化代码...
}

service () method


service () method is the main method performs the actual task. Servlet container (ie, Web server) calls the service () method to handle requests from the client (browser), and to write the formatted response back to the client.

Each time the server receives a request Servlet, the server will generate a new thread and call service. service () method checks the type of HTTP request (GET, POST, PUT, DELETE, etc.), and calls doGet, doPost, doPut, doDelete the like at the appropriate time.

The following is the method characterized by:

public void service(ServletRequest request, 
                    ServletResponse response) 
      throws ServletException, IOException{
}

service () method is called by a container, service method calls doGet, doPost, doPut, doDelete the like at the appropriate time. So, you do not have to do any action on the service () method, you only need to override the doGet () or doPost () according to the type of request from the client.

doGet () and doPost () method of each service request is the most commonly used method. The following is a feature of these two methods.

doGet () method

GET request from a normal URL request, or from an unspecified METHOD HTML form, it is processed by doGet () method.

public void doGet(HttpServletRequest request,
                  HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

doPost () method

POST request from a particular designated as METHOD POST HTML form, it is processed by doPost () method.

public void doPost(HttpServletRequest request,
                   HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

destroy () method


destroy () method is invoked only once, to be called at the end of the Servlet life cycle. destroy () method allows you to turn off the Servlet database connection, stop the background thread, list, or click on the Cookie counter is written to disk, and perform other similar clean-up activities.

After calling destroy () method, servlet object is marked as garbage collection. destroy method defined as follows:

public void destroy() {
    // 终止化代码...
}

Chart


The following figure shows a typical life cycle Servlet scheme.

  • The first HTTP request arrives at the server is delegated to the Servlet container.
  • Servlet Servlet container loaded before calling the service () method.
  • Then Servlet container handle multiple requests generated by a plurality of threads, each thread of execution in a single Servlet examples of service () method.
16678540-14248932bfe80391.jpg
Servlet-LifeCycle.jpg

Servlet examples


Servlet service HTTP requests and implement javax.servlet.Servlet interface Java classes. Web application developers often write Servlet to extend javax.servlet.http.HttpServlet, and implement the abstract class Servlet interface designed to handle HTTP requests.


httpServlet source code analysis


We look javax.servlet.http.HttpServlet source implementation of service

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                /**
                 在http中Last-Modified 与If-Modified-Since 都是用于记录页面最后修改时间的 HTTP 头信息,注意,在这 Last-Modified 是由服务器往客户端发送的 HTTP 头,另一个 If-Modified-Since是由客户端往服务器发送的头,可以看到,再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 头将先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则 返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器的负担。而且在一些ajax应用中,要求获取的数据永远是最新的,而不是读取位于缓存中的数据,做这样的设置是非常有必要的
                 **/
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

It can be seen httpServlet logic implemented when the call doGet the like.

Concrete realization of the init function and destroy the function of function httpServlet is empty, without any action, we can rewrite the init function and destroy function to achieve our objective.


hello world example


The following is an example of Hello World Servlet output source code

// 导入必需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class HelloWorld extends HttpServlet {
 
  private String message;

  public void init() throws ServletException
  {
      // 执行必需的初始化
      message = "Hello World";
  }

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
            throws ServletException, IOException
  {
      // 设置响应内容类型
      response.setContentType("text/html");

      // 实际的逻辑是在这里
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
  }
  
  public void destroy()
  {
      // 什么也不做
  }
}

Servlet deployment


By default, Servlet application in the path <Tomcat-installation-directory> / webapps / ROOT lower, and the class files in <Tomcat-installation-directory> / webapps / ROOT / WEB-INF / classes in.

If you have a fully qualified class name com.myorg.MyServlet, then the Servlet class must be in the WEB-INF / classes / com / myorg / MyServlet.class in.

Now, let's copy HelloWorld.class to <Tomcat-installation-directory> / webapps / ROOT / WEB-INF / classes in, and are located in <Tomcat-installation-directory> / webapps / ROOT / WEB-INF / the web .xml file, create the following entries:

<web-app>      
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>HelloWorld</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>
</web-app>  

Servlet Filters


Servlet filters may be dynamically intercept request and response, or to transform the information contained in the request or response.

Servlet one or more filters can be attached to a group or a Servlet Servlet. Servlet filters may be attached to the JavaServer Pages (JSP) file and HTML pages. Calling all additional filter before calling Servlet Servlet.

Servlet filters are available for Java Servlet programming classes, can achieve the following purposes:

  • Before accessing the back-end resources to the client's request, to intercept these requests.
  • Before being sent back to the client in the server's response, process the response.

Various types of filters based on the recommended specification:

  • Authentication filter (Authentication Filters).
  • Data Compression Filter (Data compression Filters).
  • Filter encryption (Encryption Filters).
  • Resource Access trigger event filter.
  • The image conversion filter (Image Conversion Filters).
  • Logging and auditing filters (Logging and Auditing Filters).
  • MIME-TYPE chain filter (MIME-TYPE Chain Filters).
  • Labeled filters (Tokenizing Filters).
  • XSL / T filter (XSL / T Filters), converting XML content.
  • XML tags filter deployment descriptor (web.xml) in the statement via the Web, and then mapped to the deployment descriptor of your application in Servlet the name or URL pattern.

When the Web container to start a Web application, it will be for you in the deployment descriptor of each filter declared create an instance.

Filter the execution order in accordance with the arrangement order web.xml configuration file, the general configuration before all Filter Servlet.


Servlet filter method


A filter is a Java class that implements javax.servlet.Filter interface. javax.servlet.Filter and defines three methods:

No. Method and Description
1 public void doFilter (ServletRequest, ServletResponse, FilterChain) which do the actual filtering operation, when a client requests a method is provided that matches the filter URL, Servlet container will first call the method doFilter filter. FilterChain for accessing a subsequent filter.
2 public void init (FilterConfig filterConfig) when the web application starts, the web server creates an instance of an object Filter and calls the init method thereof, read web.xml configuration, complete initialization function object, thereby to make subsequent user request interception preparation (filter object is created only once, init method is executed only once). Developers parameter init method, obtained on behalf of FilterConfig objects in the current filter configuration information.
3 void the destroy public () Servlet container calls the destroy method before filter instances, filter Servlet release occupied resources in the process.
Use FilterConfig

Filter the init method provides a FilterConfig object.

The web.xml document are as follows:

<filter>
    <filter-name>LogFilter</filter-name>
    <filter-class>com.runoob.test.LogFilter</filter-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>value</param-value>
    </init-param>
    </filter>

FilterConfig use objects in the init method to get parameters:

public void  init(FilterConfig config) throws ServletException {
    // 获取初始化参数
    String site = config.getInitParameter("name"); 
    // 输出初始化参数
    System.out.println("网站名称: " + site); 
}

Servlet filter instance


The following are examples Servlet filter function is to output name and website address.

/**
 * filter实现
 */

package com.runoob.test;

//导入必需的 java 库
import javax.servlet.*;
import java.util.*;

//实现 Filter 类
public class LogFilter implements Filter  {
    public void  init(FilterConfig config) throws ServletException {
        // 获取初始化参数
        String site = config.getInitParameter("Site"); 

        // 输出初始化参数
        System.out.println("网站名称: " + site); 
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {

        // 输出站点名称
        System.out.println("站点网址:http://www.runoob.com");

        // 把请求传回过滤链
        chain.doFilter(request,response);
    }
    public void destroy( ){
        /* 在 Filter 实例被 Web 容器从服务移除之前调用 */
    }
}

Here is a concrete implementation of the servlet

//导入必需的 java 库
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/DisplayHeader")

//扩展 HttpServlet 类
public class DisplayHeader extends HttpServlet {

    // 处理 GET 方法请求的方法
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        String title = "HTTP Header 请求实例 - 菜鸟教程实例";
        String docType =
            "<!DOCTYPE html> \n";
            out.println(docType +
            "<html>\n" +
            "<head><meta charset=\"utf-8\"><title>" + title + "</title></head>\n"+
            "<body bgcolor=\"#f0f0f0\">\n" +
            "<h1 align=\"center\">" + title + "</h1>\n" +
            "<table width=\"100%\" border=\"1\" align=\"center\">\n" +
            "<tr bgcolor=\"#949494\">\n" +
            "<th>Header 名称</th><th>Header 值</th>\n"+
            "</tr>\n");

        Enumeration headerNames = request.getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String paramName = (String)headerNames.nextElement();
            out.print("<tr><td>" + paramName + "</td>\n");
            String paramValue = request.getHeader(paramName);
            out.println("<td> " + paramValue + "</td></tr>\n");
        }
        out.println("</table>\n</body></html>");
    }
    // 处理 POST 方法请求的方法
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

The Servlet filters Web.xml map (Servlet Filter Mapping)
define a filter, and then mapped to a URL or Servlet, which is defined Servlet, and then mapped to a URL pattern substantially the same manner. Create the following entry for the filter tag in the deployment descriptor file web.xml:

<?xml version="1.0" encoding="UTF-8"?>  
<web-app>  
<filter>
  <filter-name>LogFilter</filter-name>
  <filter-class>com.runoob.test.LogFilter</filter-class>
  <init-param>
    <param-name>Site</param-name>
    <param-value>菜鸟教程</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>LogFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>  
  <!-- 类名 -->  
  <servlet-name>DisplayHeader</servlet-name>  
  <!-- 所在的包 -->  
  <servlet-class>com.runoob.test.DisplayHeader</servlet-class>  
</servlet>  
<servlet-mapping>  
  <servlet-name>DisplayHeader</servlet-name>  
  <!-- 访问的网址 -->  
  <url-pattern>/TomcatTest/DisplayHeader</url-pattern>  
</servlet-mapping>  
</web-app> 

The filter applied to all of the Servlet, as we specified in the configuration / *. If you want to apply a filter on a small number of Servlet, you can specify a specific Servlet path.


The filter order


Order filter-mapping element of the web.xml Web container application determines the filter to the sequence of the Servlet. To reverse the order of the filter, you only need to reverse filter-mapping element in the web.xml file.


each node web.xml configuration described


  • <Filter> specify a filter.
    • <Filter-name> is used to specify a name for the filter, the contents of the element can not be empty.
    • <Filter-class> element is used to specify the fully qualified class name of the filter.
    • <Init-param> element specifies the initialization parameters for the filter, its children <param-name> Name of the parameter, <param-value> specified parameter values.
    • In the filter, you may be used to access the interface object FilterConfig initialization parameters.
  • <Filter-mapping> element is used to set up a Filter resource is responsible intercepted. Filter intercept a resource can be specified in two ways: Request Servlet path name and resource access
    • <Filter-name> name of the sub-element filter is used to set the register. This value must be declared in the <filter> name of the filter element
    • <Url-pattern> provided the intercepted request path filter (filter associated URL style)
  • Servlet Name <servlet-name> specifies the intercepted filter.
  • <Dispatcher> specify the filter intercepted resource is invoked Servlet container, it can be one of REQUEST, INCLUDE, FORWARD, and ERROR, default REQUEST. The user can set a plurality of <dispatcher> subelement is called intercept more Filter is used to specify the resource.
  • The value and significance of <dispatcher> sub-element can be set
    • REQUEST: When users direct access to pages, Web container will call the filter. If the target resource is a way to access, then the filter will not be called by RequestDispatcher's include () or forward ().
    • INCLUDE: If the target resource is accessed via the RequestDispatcher's include (), then the filter will be called. In addition, the filter will not be called.
    • FORWARD: If the target resource is accessed through the RequestDispatcher forward () method, then the filter will be called, in addition, the filter will not be called.
    • ERROR: If the target resource is the mechanism by calling declarative exception handling, then the filter will be called. In addition, the filter will not be called.

servlet error handling


When an exception is thrown when a Servlet, Web container used exception-type element web.xml search to match the type of exception thrown configuration.

You must web.xml using the error-page to specify the elements of a specific abnormal or HTTP status code to the appropriate Servlet calls.

web.xml written as follows

<!--假设,有一个 ErrorHandler 的 Servlet 在任何已定义的异常或错误出现时被调用。以下将是在 web.xml 中创建的项。-->
<!-- servlet 定义 -->
<servlet>
        <servlet-name>ErrorHandler</servlet-name>
        <servlet-class>ErrorHandler</servlet-class>
</servlet>
<!-- servlet 映射 -->
<servlet-mapping>
        <servlet-name>ErrorHandler</servlet-name>
        <url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<!-- error-code 相关的错误页面 -->
<error-page>
    <error-code>404</error-code>
    <location>/ErrorHandler</location>
</error-page>
<error-page>
    <error-code>403</error-code>
    <location>/ErrorHandler</location>
</error-page>

<!-- exception-type 相关的错误页面 -->
<error-page>
    <exception-type>
          javax.servlet.ServletException
    </exception-type >
    <location>/ErrorHandler</location>
</error-page>

<error-page>
    <exception-type>java.io.IOException</exception-type >
    <location>/ErrorHandler</location>
</error-page>

If you want all the exceptions have a generic error handler, the following should be defined error-page, rather than for each individual anomalies defined error-page element.

<error-page>
    <exception-type>java.lang.Throwable</exception-type >
    <location>/ErrorHandler</location>
</error-page>

Request attribute - Error / Exception


We have the following common attribute list display list request error process can access, to analyze error / exception properties.

No. Properties & Description
1 javax.servlet.error.status_code
\ This attribute gives the status code, status code may be stored, and can be analyzed after storage for the data type java.lang.Integer
2 javax.servlet.error.exception_type
\ This attribute gives the exception type of information may be stored exception type, and can be analyzed after storage for the data type java.lang.Class.
3 javax.servlet.error.message
\ This attribute gives exact information on the error message, the information may be stored, and can be analyzed after storage for the data type java.lang.String.
4 javax.servlet.error.request_uri
\ This attribute gives information about the URL call Servlet, the information may be stored, and can be analyzed after storage for the data type java.lang.String.
5 javax.servlet.error.exception
\ This attribute gives information of the abnormality occurrence, the information may be stored, and can be analyzed after storage for the data type java.lang.Throwable.
6 javax.servlet.error.servlet_name
\ This attribute gives the name of the Servlet, names may be stored, and can be analyzed after storage for the data type java.lang.String.

The following gives a specific example to demonstrate the operation of errorHandler

//导入必需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

//扩展 HttpServlet 类
public class ErrorHandler extends HttpServlet {

    // 处理 GET 方法请求的方法
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        //通过提取request的错误的属性,得到错误的具体类型
        Throwable throwable = (Throwable)
        request.getAttribute("javax.servlet.error.exception");
        Integer statusCode = (Integer)
        request.getAttribute("javax.servlet.error.status_code");
        String servletName = (String)
        request.getAttribute("javax.servlet.error.servlet_name");
        if (servletName == null){
            servletName = "Unknown";
        }
        String requestUri = (String)
        request.getAttribute("javax.servlet.error.request_uri");
        if (requestUri == null){
            requestUri = "Unknown";
        }
        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");
    
        PrintWriter out = response.getWriter();
        String title = "菜鸟教程 Error/Exception 信息";
       
        String docType = "<!DOCTYPE html>\n";
        out.println(docType +
            "<html>\n" +
             "<head><title>" + title + "</title></head>\n" +
             "<body bgcolor=\"#f0f0f0\">\n");
           out.println("<h1>菜鸟教程异常信息实例演示</h1>");
           if (throwable == null && statusCode == null){
              out.println("<h2>错误信息丢失</h2>");
              out.println("请返回 <a href=\"" + 
            response.encodeURL("http://localhost:8080/") + 
                "\">主页</a>。");
           }else if (statusCode != null) {
              out.println("错误代码 : " + statusCode);
        }else{
               out.println("<h2>错误信息</h2>");
              out.println("Servlet Name : " + servletName + 
                              "</br></br>");
              out.println("异常类型 : " + 
                              throwable.getClass( ).getName( ) + 
                              "</br></br>");
              out.println("请求 URI: " + requestUri + 
                              "<br><br>");
              out.println("异常信息: " + 
                                  throwable.getMessage( ));
           }
           out.println("</body>");
           out.println("</html>");
    }
    // 处理 POST 方法请求的方法
    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
       throws ServletException, IOException {
        doGet(request, response);
    }
}

servlet cookie handling


Cookie is usually set in the HTTP header information (although JavaScript can also be set directly in a browser Cookie). Set Cookie Servlet sends the header information following:

HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: name=xyz; expires=Friday, 04-Feb-07 22:03:38 GMT; 
                 path=/; domain=runoob.com
Connection: close
Content-Type: text/html

Set-Cookie header contains a name value pair, a GMT date, a path and a domain. Names and values ​​are URL encoded. expires field is an instruction that tells the browser after a given period of time and date "forget" the Cookie.

If your browser is configured to store Cookie, it will retain this information until the expiration date. If the user's browser to any page that matches the Cookie path and domain, it will re-Cookie sent to the server. Browser header information may be as follows:

GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name=xyz

Reproduced in: https: //www.jianshu.com/p/9a4c81cc2554

Guess you like

Origin blog.csdn.net/weixin_34061555/article/details/91227171