javaweb笔记3---理解http协议


http

http简介

规定了一种申请和请求数据的规范
(http规范并非必须遵守的,这只是为了方便开发而产生的)
服务器和客户端传递数据的形式有以下这些:

客户端请求服务器

  • post请求(常用)
    • 请求行
      • 请求方式
      • URI----请求的服务器软件中的资源
      • 协议版本号
    • 请求头
      • 请求的主机
      • 主机的端口
      • 浏览器信息
      • 平台信息
      • cookie等信息(其中最为关键的是session机制)
    • 空白行
      • 空白行是用来区分“请求头”和“请求体”
    • 请求体
      • 向服务器发送的具体数据。

GET /servlet05/getServlet?username=lucy&userpwd=1111 HTTP/1.1
// 请求行 (up)
Host: localhost:8080
// 请求头 (up)
Connection: keep-alive
sec-ch-ua: “Google Chrome”;v=“95”, “Chromium”;v=“95”, “;Not A Brand”;v=“99”
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: “Windows”
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 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://localhost:8080/servlet05/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
// 空白行 (this)
// 请求体 (this)

  • get请求(常用)

POST /servlet05/postServlet HTTP/1.1
// 请求行 (up)
Host: localhost:8080
// 请求头 (up)
Connection: keep-aliveContent-Length: 25Cache-Control: max-age=0sec-ch-ua: “Google Chrome”;v=“95”, “Chromium”;v=“95”, “;Not A Brand”;v="99"sec-ch-ua-mobile: ?0sec-ch-ua-platform: "Windows"Upgrade-Insecure-Requests: 1Origin: http://localhost:8080Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36Accept: 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.9Sec-Fetch-Site: same-originSec-Fetch-Mode: navigateSec-Fetch-User: ?1Sec-Fetch-Dest: documentReferer: http://localhost:8080/servlet05/index.htmlAccept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9
// 空白行 (this)
username=lisi&userpwd=123
// 请求体 (up)

  • delete请求
  • put请求
  • head请求
  • options请求
  • trace请求

服务器响应客户端

 HTTP/1.1 200 ok                                     //状态行
Content-Type: text/html;charset=UTF-8                //响应头
Content-Length: 160
Date: Mon, 08 Nov 2021 13:19:32 GMT
Keep-Alive: timeout=20
Connection: keep-alive
                                                     //空白行
<!doctype html>                                      //响应体
<html>
    <head>
        <title>from get servlet</title>
    </head>
    <body>
        <h1>from get servlet</h1>
    </body>
</html>
  • 状态行

    三部分组成

    • 第一部分:协议版本号(HTTP/1.1)
    • 第二部分:状态码(HTTP协议中规定的响应状态号。不同的响应结果对应不同的号码。)
      • 200 表示请求响应成功,正常结束。
      • 404表示访问的资源不存在,通常是因为要么是你路径写错了,要么是路径写对了,但是服务器中对应的资源并没有启动成功。总之404错误是前端错误。
      • 405表示前端发送的请求方式与后端请求的处理方式不一致时发生:
        • 比如:前端是POST请求,后端的处理方式按照get方式进行处理时,发生405
        • 比如:前端是GET请求,后端的处理方式按照post方式进行处理时,发生405
      • 500表示服务器端的程序出现了异常。一般会认为是服务器端的错误导致的。
      • 以4开始的,一般是浏览器端的错误导致的。
      • 以5开始的,一般是服务器端的错误导致的。
    • 第三部分:状态的描述信息
      • ok 表示正常成功结束。
      • not found 表示资源找不到。
  • 响应头:
    含有很多成分

    • 响应的内容类型
    • 响应的内容长度
    • 响应的时间
  • 空白行:

    • 用来分隔“响应头”和“响应体”的。
  • 响应体:

    • 响应体就是响应的正文,这些内容是一个长的字符串,这个字符串被浏览器渲染,解释并执行,最终展示出效果。

httpservlet

httpservlet简介

这是一个非常常用的已经继承了genericalservlet接口的一个类,(它是一个便于开发的一个类,本身没有httpservlet也是需要遵守http协议的)继承这个类的servlet程序可以在页面显式http帮助显现的错误

  • httpservlet设计的模式
    • 模板方法设计方式
  • http继承的类
    • jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)
    • jakarta.servlet.ServletException Servlet异常(类)
  • http包下的类
    • jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)

    • jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象)—— 继承了 jakarta.servlet.ServletRequest Servlet请求接口(接口)

    • jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象)—— 继承了 jakarta.servlet.ServletResponse Servlet响应接口(接口)

httpservlet源码分析

public class HelloServlet extends HttpServlet {
	// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
	public HelloServlet() {
    }
    
    //override 重写 doGet方法
    //override 重写 doPost方法
}

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {
           
	// 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
	// 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
	public void init() throws ServletException {
        // NOOP by default
    }
}

// HttpServlet模板类。
public abstract class HttpServlet extends GenericServlet {
    // 用户发送第一次请求的时候这个service会执行
    // 用户发送第N次请求的时候,这个service方法还是会执行。
    // 用户只要发送一次请求,这个service方法就会执行一次。
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        // 调用重载的service方法。
        service(request, response);
    }
    
    // 这个service方法的两个参数都是带有Http的。
    // 这个service是一个模板方法。
    // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 获取请求方式
        // 这个请求方式最终可能是:""
        // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
        // GET POST PUT DELETE HEAD OPTIONS TRACE
        String method = req.getMethod();

        // 如果请求方式是GET请求,则执行doGet方法。
        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
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // 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)) {
            // 如果请求方式是POST请求,则执行doPost方法。
            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);
        }
    }
    
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
        // 报405错误
        String msg = lStrings.getString("http.method_get_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 报405错误
        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
}

/*
通过以上源代码分析:
	假设前端发送的请求是get请求,后端程序员重写的方法是doPost
	假设前端发送的请求是post请求,后端程序员重写的方法是doGet
	会发生什么呢?
		发生405这样的一个错误。
		405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
	
	通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.

怎么避免405的错误呢?
	后端重写了doGet方法,前端一定要发get请求。
	后端重写了doPost方法,前端一定要发post请求。
	这样可以避免405错误。
	
	这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。
	
有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
少写一点。
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值