请求处理程序(Handler)

更新时间:

您可以使用Node.js请求处理程序响应接收到的事件并执行相应的业务逻辑。本文介绍Node.js请求处理程序的相关概念、结构特点和示例。

什么是请求处理程序

FC函数的请求处理程序,是函数代码中处理请求的方法。当您的FC函数被调用时,函数计算会运行您提供的Handler方法处理请求。您可以通过函数计算控制台请求处理程序参数配置Handler。

对Node.js语言的FC函数而言,您的请求处理程序格式为文件名.方法名。例如,您的文件名为index.jsindex.mjs,方法名为handler,则请求处理程序为index.handler

关于FC函数的具体定义和相关操作,请参见创建事件函数

请求处理程序的具体配置均需符合函数计算平台的配置规范。配置规范因请求处理程序类型而异。

Handler签名

说明

函数计算从Node.js 18运行时开始支持ECMAScript(ES)模块。在此之前(Node.js 16及以前的版本),函数计算仅支持使用CommonJS 模块。详情请参见将请求处理程序指定为ES模块

一个简单的Event Handler签名定义如下。

Node.js 18或Node.js 20

ES模块

说明

当前示例代码支持一键部署,您可以直接在函数计算FC中一键部署本代码。start-fc3-nodejs-es

// index.mjs
export const handler = async (event, context) => {
  console.log("receive event: \n" + event);
  return "Hello World!";
};

CommonJS模块

// index.js
exports.handler = async function(event, context) {
  console.log("receive event: \n" + event);
  return "Hello World!";
};

Node.js 16及以下版本

// index.js
exports.handler = async function(event, context, callback) {
  console.log("event: \n" + event);
  callback(null, 'hello world');
};

以上示例代码解析如下:

handler是方法名称,与函数计算控制台配置的请求处理程序相对应。例如,创建函数时指定的请求处理程序index.handler,那么函数计算会去加载index.js中定义的handler函数,并从这里开始执行。

函数计算运行时会将请求参数传递到请求处理程序中,第一个参数是event对象,包含请求的有效负载信息,event对象是Buffer类型,您可以将其转换成所需要的对象类型。第二个参数是 context对象,提供在调用时的运行上下文信息。更多信息,请参见上下文

    说明
    • 使用Node.js 18及以上版本的运行时环境时,建议您使用Async/Await功能,而不是使用回调函数callback

    • 函数计算会根据返回值的类型对返回结果做相应转换。

      • Buffer类型:不转换,原样返回。

      • Object类型:转换为JSON格式后返回。

      • 其他类型:转换为字符串后返回。

Async/Await

使用Node.js 18及以上版本的运行时环境时,建议您使用Async/Await方式来实现。Async/Await是一种简洁、易读的Node.js异步代码编写方式,无需嵌套回调或链式调用。

重要

如果您使用的Node.js 16及以下版本的运行时环境,则必须显式使用回调方法 callback发送响应,否则会出现请求超时错误。

Async/Await方式与回调函数callback相比,有以下优点:

  • 可读性更好:Async/Await方式的代码更加线性和同步,更易于理解和维护。它避免了回调函数嵌套过深的情况,使代码结构更清晰。

  • 调试和错误处理更简单:可以使用try-catch块更方便地捕获和处理异步操作中的错误,错误堆栈更加清晰,可以更准确地追踪错误发生的位置。

  • 效率更高:回调函数通常需要在代码的不同部分之间切换。Async/Await方式可以减少上下文切换的数量,从而提高代码效率。

示例一:解析JSON格式参数

示例代码

当您传入JSON格式参数时,函数计算会透传参数内容,需要您在代码中自行解析。下面是解析JSON格式事件的代码示例。

ES模块

说明

此示例仅支持运行在Node.js 18及以上版本的运行时环境。

export const handler = async (event, context) => {
  var eventObj = JSON.parse(event.toString());
  return eventObj['key'];
};

CommonJS模块

说明

当前示例代码支持一键部署,您可以直接在函数计算FC中一键部署本代码。start-fc3-nodejs-json

exports.handler = function(event, context, callback) {
  var eventObj = JSON.parse(event.toString());
  callback(null, eventObj['key']);
};

前提条件

已创建Node.js函数,具体操作,请参见创建事件函数。如果您需要将代码指定为ES模块,创建函数时,运行环境需选择Node.js 18或Node.js 20。

操作步骤

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数配置页面,选择代码页签,在代码编辑器中输入上述示例代码,然后单击部署代码

    说明

    上述示例代码中函数的请求处理程序是index.js中的handler方法。如果您的函数的请求处理程序配置不同,请获取对应的文件和方法进行更新。

  4. 代码页签,单击测试函数右侧的image.png图标,从下拉列表中选择配置测试参数,输入以下示例测试参数,然后单击确定

    {
      "key": "value"
    }
  5. 单击测试函数

    函数执行成功后,查看返回结果,您可以看到返回结果为value

示例二:通过临时密钥安全读写OSS的资源

示例代码

您可以使用函数计算为您提供的临时密钥访问对象存储OSS,代码示例如下所示。

ES模块

说明

此示例仅支持运行在Node.js 18及以上版本的运行时环境。

// index.mjs
import OSSClient from 'ali-oss';

export const handler = async (event, context) => {
    console.log(event.toString());

    var ossClient = new OSSClient({
        accessKeyId: context.credentials.accessKeyId,
        accessKeySecret: context.credentials.accessKeySecret,
        stsToken: context.credentials.securityToken,
        region: 'oss-cn-shenzhen',
        bucket: 'my-bucket',
    });

    try {
      const uploadResult = await ossClient.put('myObj', Buffer.from('hello, fc', "utf-8"));
      console.log('upload success, ', uploadResult);
      return "put object"
    } catch (error) {
      throw error
    }
};

代码示例解析:

  • context.credentials:从 context 参数中获取临时密钥,避免在代码中硬编码密码等敏感信息。

  • myObj:OSS对象名。

  • Buffer.from('hello, fc', "utf-8"): 上传的对象内容。

  • return "put object":上传成功正常返回 "put object"。

  • throw err:上传失败时抛出异常。

CommonJS模块

说明

当前示例代码支持一键部署,您可以直接在函数计算FC中一键部署本代码。start-fc3-nodejs-oss

var OSSClient = require('ali-oss');

exports.handler = function (event, context, callback) {
    console.log(event.toString());

    var ossClient = new OSSClient({
        accessKeyId: context.credentials.accessKeyId,
        accessKeySecret: context.credentials.accessKeySecret,
        stsToken: context.credentials.securityToken,
        region: `oss-${context.region}`,
    		bucket: process.env.BUCKET_NAME,
    });

    ossClient.put('myObj', Buffer.from('hello, fc', "utf-8")).then(function (res) {
        callback(null, 'put object');
    }).catch(function (err) {
        callback(err);
    });  
};

示例代码解析如下:

  • context.credentials:表示从context参数中获取临时密钥,避免在代码中硬编码密码等敏感信息。

  • myObj:OSS对象名称。

  • Buffer.from('hello, fc', "utf-8"):上传的对象的内容。

  • callback(null, 'put object'):上传成功正常返回put object

  • callback(err):上传失败时返回err

前提条件

  • 为服务配置具有访问对象存储OSS的权限的角色。具体操作,请参见授予函数计算访问其他云服务的权限

  • 创建运行环境为Node.js的函数。具体操作,请参见创建事件函数。如果您需要将代码指定为ES模块,创建函数时,运行环境需选择Node.js 18或Node.js 20。

操作步骤

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. (可选)在目标函数的函数详情页签,选择代码页签,然后在下方的WebIDE界面,依次选择Terminal > New Terminal打开终端,执行以下命令安装ali-oss依赖。

    npm install ali-oss --save

    安装完成后,在WebIDE界面左侧代码目录可以看到生成了node_modules文件夹,文件夹内包含ali-oss目录以及其他依赖库。

  4. 在目标函数的函数详情页签,选择代码页签,在下方的代码编辑器中输入上述示例代码,保存并单击部署代码

    说明
    • 上述示例代码中函数的请求处理程序是index.jsindex.mjs文件中的handler方法。如果您的函数的请求处理程序配置不同,请获取对应的文件和方法进行更新。

    • 上述示例代码中regionbucket需按照实际情况填写。

  5. 单击测试函数

    函数执行成功后,查看返回结果,您可以看到返回结果为put object

示例三:调用外部命令

您的Node.js程序也可以创建fork进程,调用外部命令。例如,您可以使用child_process模块调用Linux的ls -l命令,输出当前目录下的文件列表。代码示例如下所示。

ES模块

说明

此示例仅支持运行在Node.js 18及以上版本的运行时环境。

'use strict';

import { exec } from 'child_process';
import { promisify } from 'util';

const execPromisify = promisify(exec);
export const handler = async (event, context) => {
  try {
    const { stdout, stderr } = await execPromisify("ls -l");
    console.log(`stdout: ${stdout}`);
    if (stderr !== "") {
      console.error(`stderr: ${stderr}`);
    }
    return stdout;
  } catch (error) {
    console.error(`exec error: ${error}`);
    return error;
  }
}

CommonJS模块

说明

当前示例代码支持一键部署,您可以直接在函数计算FC中一键部署本代码。start-fc3-nodejs-exec

'use strict';

var exec = require('child_process').exec;
exports.handler = (event, context, callback) => {
  console.log('start to execute a command');
  exec("ls -l", function(error, stdout, stderr){
    callback(null, stdout);
});
}

示例四:使用HTTP触发器调用函数

示例代码

使用HTTP触发器提供的URL调用函数,函数代码示例如下。

  • 如果HTTP触发器的认证方式无需认证,您可以直接使用Postman或Curl工具来调用函数。具体操作,请参见操作步骤

  • 如果HTTP触发器的认证方式签名认证JWT认证,请使用签名方式或JWT认证方式来调用函数。具体操作,请参见认证鉴权

关于HTTP触发调用的请求负载格式和响应负载格式,请参见HTTP触发器调用函数

ES模块

说明

此示例仅支持运行在Node.js 18及以上版本的运行时环境。

'use strict';

export const handler = async (event, context) => {
  const eventObj = JSON.parse(event);
  console.log(`receive event: ${JSON.stringify(eventObj)}`);

  let body = 'Hello World!';
  // get http request body
  if ("body" in eventObj) {
    body = eventObj.body;
    if (eventObj.isBase64Encoded) {
      body = Buffer.from(body, 'base64').toString('utf-8');
    }
  }
  console.log(`receive http body: ${body}`);

  return {
    'statusCode': 200,
    'body': body
  };
}

CommonJS模块

说明

当前示例代码支持一键部署,您可以直接在函数计算FC中一键部署本代码。start-fc3-nodejs-http

'use strict';
exports.handler = (event, context, callback) => {
  const eventObj = JSON.parse(event);
  console.log(`receive event: ${JSON.stringify(eventObj)}`);

  let body = 'Hello World!';
  // get http request body
  if ("body" in eventObj) {
    body = eventObj.body;
    if (eventObj.isBase64Encoded) {
      body = Buffer.from(body, 'base64').toString('utf-8');
    }
  }
  console.log(`receive http body: ${body}`);

  callback(null, {
    'statusCode': 200,
    'body': body
  });
}

前提条件

已使用上述示例创建运行环境为Node.js的函数并创建HTTP触发器。具体操作,请参见创建事件函数配置HTTP触发器并使用HTTP触发。如果您需要将代码指定为ES模块,创建函数时,运行环境需选择Node.js 18或Node.js 20。

操作步骤

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数详情页面,单击配置页签,然后再左侧导航栏,单击触发器,在触发器页面获取HTTP触发器的公网访问地址。

  4. 在Curl工具执行以下命令,调用函数。

    curl -i "https://test-nodejs-dlgxxr****.cn-shanghai.fcapp.run" -d 'Hello World!'

    以上命令中,https://test-nodejs-dlgxxr****.cn-shanghai.fcapp.run为获取到的HTTP触发器公网访问地址。

    响应结果如下。

    HTTP/1.1 200 OK
    Access-Control-Expose-Headers: Date,x-fc-request-id
    Content-Disposition: attachment
    Content-Length: 12
    Content-Type: text/html; charset=utf-8
    Etag: W/"c-Lve95gjOVATpfV8EL5X4nxwjKHE"
    X-Fc-Request-Id: 1-65d866a8-15d8796a-cb9b4feb69ca
    X-Powered-By: Express
    Date: Fri, 23 Feb 2024 09:34:34 GMT
    
    Hello World!curl: (3) URL using bad/illegal format or missing URL

示例五:使用HTTP触发器下载文件

示例代码

如果您想通过代码返回一个图片、压缩包或二进制文件,可以使用HTTP触发器实现在函数代码中返回,函数代码示例如下。

ES模块

说明

此示例仅支持运行在Node.js 18及以上版本的运行时环境。

// index.mjs
'use strict';

import mime from 'mime';
import fs from 'fs/promises';
import path from 'path'; 

export const handler = async (event, context) => {
    const fileContent = 'This is a sample text file created in the code.';
    const fileName = 'sample.txt';
    const filePath = path.join('/tmp', fileName);
    
    try {
        await fs.writeFile(filePath, fileContent);
        
        const mimeType = mime.getType(filePath);
        if (!mimeType) {
            throw new Error('Unable to determine MIME type');
        }
        
        const fileData = await fs.readFile(filePath);
        const fileBase64 = Buffer.from(fileData).toString('base64');
        
        const fcResponse = {
            'statusCode': 200,
            'headers': {
                'Content-Type': mimeType,
                'Content-Disposition': `attachment; filename="${fileName}"`,
            },
            'body': fileBase64,
            'isBase64Encoded': true
        };
        
        console.log('File generated and fetched successfully.');
        return fcResponse;
    } catch (err) {
        console.error(err);
        return {
            'statusCode': 500,
            'body': err.message
        };
    }
};

CommonJS模块

// index.js
'use strict';
const mime = require('mime');
const fs = require('fs');
const path = require('path');

exports.handler = async (event, context, callback) => {

    const fileContent = 'This is a sample text file created in the code.';
    const fileName = 'sample.txt';
    const filePath = path.join('/tmp', fileName);
    try {
        fs.writeFileSync(filePath, fileContent);

        const mimeType = mime.getType(filePath);
        if (!mimeType) {
            throw new Error('Unable to determine MIME type');
        }

        const fileData = fs.readFileSync(filePath);
        const fileBase64 = Buffer.from(fileData).toString('base64');

        const fcResponse = {
            'statusCode': 200,
            'headers': {
                'Content-Type': mimeType,
                'Content-Disposition': `attachment; filename="${fileName}"`,
            },
            'body': fileBase64,
            'isBase64Encoded': true
        };

        console.log('File generated and fetched successfully.');
        callback(null, fcResponse);
    } catch (err) {
        console.error(err);
        callback(null, {
            'statusCode': 500,
            'body': err.message
        });
    }
};

前提条件

已使用上述示例创建运行环境为Node.js的函数并创建HTTP触发器。具体操作,请参见创建事件函数配置HTTP触发器并使用HTTP触发。如果您需要将代码指定为ES模块,创建函数时,运行环境需选择Node.js 18或Node.js 20。

操作步骤

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数详情页面,选择代码页签,然后选择Terminal > New Terminal打开终端窗口,执行命令npm install mime@2安装mime库。安装完成后,单击部署代码

    image

  4. 在函数配置页面的触发器页签,获取HTTP触发器的公网访问地址,复制URL到浏览器并回车。

    image

    回车后触发器函数执行,执行成功后下载文件到本地。