本文为您介绍边缘程序ER(EdgeRoutine)在回源、操作请求及回复、子请求和异步事件等场景中的使用案例。

回源

  • 场景描述
    以下代码示例为您介绍了三种HTTP请求方式。回源如果要透传客户端请求头、客户端请求负载和客户端请求的HTTP方法,最好的方式是直接Fetch对应的Request,具体见以下代码示例中的示例2。
    说明 body是一个ReadableStream对象,是一个只能读取和使用一次的流,如果要多次使用,需要buffer到JS对象中。关于Stream API的介绍,请参见MDN官方文档Streams API
  • 代码示例
    addEventListener('fetch', (event) => {
      event.respondWith(handleRespond(event));
    });
    
    async function handleRespond(event) {
      
      // 1.子请求。该操作是一个独立的HTTP操作,客户端请求的头和方法
      //    没有透传到该请求。
         let resp1 = await fetch("http://www.example.com");
      
      // 2.子请求,使用客户端的头。
      let resp2 = await fetch("http://www.example.com", {
        body: event.request.body,
        headers: event.request.headers,
        method: event.request.method
      });
      
      // 3.直接使用客户端请求发向源站。fetch的URL是客户端请求的URL,即
      //    hostname是host header。
      let resp3 = await fetch(event.request);
      
      // 注意,所有fetch api的body对象是个流,一旦读取就无法再透传。
      // 例如:
      //    let json = await event.request.body;
      //    fetch(event.request);
      //  以上fetch会抛出JS异常,event.request.body这个流已经被读取了。
      //  您可以fetch(event.request, {body: JSON.stringify(json)});
    }

操作请求和回复

  • 定义
    • Request的定义,请参见MDN官方文档Request
    • Response的定义,请参见MDN官方文档Response
  • 代码示例
    addEventListener('fetch', (event) => {
      event.respondWith(handleResponse(event));
    });
    
    async function handleResponse(event) {
      // 1.Request对象的相关属性或者方法。
      {
        let request = event.request;
        
        // 请求方法
        let method = request.method;
        // 请求path
        let path = request.path;
        // 请求URL
        let url = request.url;
        // 请求头
        let headers = request.headers;
        // 请求负载,body不是一个string或者arrayBuffer,而是一个ReadableStream。
        let body = request.body;
      }
      
      // 2.Response对象相关方法和属性。
      {
        let response = new Response("Hello World", {
          status: 200,
          statusText: "Perfect",
          headers: [[
            "X-Special-Header1": "value1",
            "X-Where-Am-I": "edge"
          ]]
        });
        
        // 回复状态码
        let status = response.status;
        
        // 回复状态信息
        let statusText = response.statusText;
        
        // 回复头
        let header = response.headers;
        
        // 回复是否是一个3xx跟随的结构
        let is3XX = response.redirected;
        
        // 回复的body
        let body = response.body;
      }
      
      // 3.body mixin的用法和属性,Request和Response都适用。
      {
        let response = new Response("Hello World");
        
        // 将body当作字符串处理,默认为utf-8编码。
        // let string = await response.text();
        
        // 将body当作arrayBuffer,即二进制,编码未知。
        // let binary = await response.arrayBuffer();
        
        // 将body当作JSON处理,默认为utf-8编码。
        // let json = await response.json();
        
        // 将body当作表单数据处理:
        // 头必须有
        //   (1) content-type: application/x-www-urlform-encoded
        //                     或者
        //   (2) content-type: multipart/form-data
        // let formData = await response.formData();
        
        // 注意,以上方法读取将完全读完流的所有信息,流将不再可用,无法lock任何reader。
      }
      return "done";
    }

子请求

  • 定义

    子请求的核心是一个Fetch API,该Fetch API是一个异步API,会返回一个Promise。

  • 代码示例
    addEventListener('fetch', (event) => {
      event.respondWith(handleResponse(event));
    });
    
    async function handleResponse(event) {
      // 1.简单的fetch
      let resp1 = await fetch("http://www.example.com");
      
      // 2.编写特殊header
      {
        let hdr = new Headers({
          "X-Cache" : "Hit",
          "User-Agent" : "Hello from EdgeRoutine"
        });
        let resp2 = await fetch("http://www.example.com", {headers: hdr});
      }
      
      // 3.post/delete/head/put
      {
        let resp3 = await fetch("http://www.example.com", {method: "POST", body: "Hey Jude"});
      }
      
      // 4.发送请求body
      //  4.1 字符串
      //  4.2 ArrayBuffer, Uint8Array
      //  4.3 流
      {
        let resp4 = await fetch("http://www.example.com", {method: "POST", body: "data"});
        let resp5 = await fetch("http://www.example.com", {method: "POST", body: new Uint8Array(10)});
        let upstream = await fetch("http://www.taobao.com");
        let resp6 = await fetch("http://www.example.com", {method: "POST", body: upstream.body});
      }
      
      // 5.使用Request对象fetch
      {
        let req = new Request("http://www.taobao.com", {method: "POST", body: "data"});
        let resp7 = await fetch(req);
      }
      
      // 6.3xx redirect,默认情况fetch不会跟随3xx回复码。
      {
        // 自动跟随重定向,tmall强制重定向到https流。
        let resp = await fetch("http://www.tmall.com", {redirect: "follow"});
        
        // 读出所有请求的URL
        let urlList = resp.urlList;
        for (const url of urlList) {
                console.log(url);
        }
      }
      
      return "done";
    }

异步事件

  • 定义

    异步事件是指ER的主回复被发送后,请求的上下文会被清理,如果您有还未完成的异步请求将会被取消。Response定义请参见MDN官方文档ExtendableEvent.waitUntil()

  • 代码示例
    addEventListener('fetch', (event) => {
      event.respondWith(handleResponse(event));
    });
    
    async function handleResponse(event) {
        let resp = fetch("http://www.taobao.com");
      
      // 1.想要保证以上fetch完成,必须使用event.waitUntil或者使用await关键字
      //    同步异步请求。
      event.waitUntil(resp);
      
      // 2.现在直接回复给客户端请求,上面的fetch请求可以异步后台完成,即时你的请求已经
      //    回复给了客户端。
      return "done";
    }