You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lambda函数调用API发送OTP时遇TypeError [ERR_UNESCAPED_CHARACTERS]错误

修复Lambda调用API发送OTP时的路径转义错误

问题场景

在Lambda函数中调用第三方API发送OTP,通过请求路径的查询参数传递手机号和验证码,运行时报错:
TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters

原代码如下:

const https = require('https');

function postRequest(body) {
  
  const ans = body.answer
  const phon = body.phone
  
  
  const options = {
    hostname: 'app.xxx.xx',
    path: '/api/v1/send?user_id=24xxx&api_key=3Yxxxx&sender_id=dEMO&to='+{phon}+'&message=Your%OTP%is%'+{ans}+'thanks',
    method: 'POST',
    port: 443,
    
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, res => {
      let rawData = '';

      res.on('data', chunk => {
        rawData += chunk;
      });

      res.on('end', () => {
        try {
          resolve(JSON.parse(rawData));
        } catch (err) {
          reject(new Error(err));
        }
      });
    });

    req.on('error', err => {
      reject(new Error(err));
    });

    
    req.write(JSON.stringify(body));
    req.end();
  });
}

exports.handler = async (event, context, callback) => {
  //Create a random number for otp
  const challengeAnswer = Math.random().toString(10).substr(2, 4);
  const phoneNumber = event.request.userAttributes.phone_number;

  console.log(event, context);
 
  await postRequest({
      phone: phoneNumber,
      answer: challengeAnswer,
    },
    function(err, data) {
      if (err) {
        console.log(err.stack);
        console.log(data);
        return;
      }
      console.log(`SMS sent to ${phoneNumber} and otp = ${challengeAnswer}`);
      return data;
    });

callback(null, event);
};

错误原因

  • 路径拼接语法错误:+{phon}+ 和 +{ans}+ 的写法错误,大括号会把变量转成[object Object]字符串,导致路径包含非法字符
  • 查询参数未URL编码:手机号(可能带+号)、验证码这类动态值直接拼接进路径,未进行URL编码,触发转义错误
  • 冗余的POST请求体:API参数已通过查询参数传递,不需要再写入请求体,多余的req.write可能造成不必要的问题
  • async/await使用错误:postRequest返回Promise,但调用时同时用了await和回调函数,属于混用,会导致逻辑混乱

修复方案

修正步骤

  1. 移除变量拼接时的大括号,直接用+ phon +这类正确的字符串拼接方式
  2. 用encodeURIComponent()对所有动态查询参数值进行URL编码
  3. 删除req.write(JSON.stringify(body)),因为参数已在路径中传递
  4. 用try/catch替代回调函数处理Promise的成功/失败逻辑

修正后的完整代码

const https = require('https');

function postRequest(body) {
  const ans = body.answer;
  const phon = body.phone;

  // 对动态参数进行URL编码,避免未转义字符
  const encodedPhone = encodeURIComponent(phon);
  const encodedOtp = encodeURIComponent(ans);
  
  // 用模板字符串拼接路径,修正语法错误
  const options = {
    hostname: 'app.xxx.xx',
    path: `/api/v1/send?user_id=24xxx&api_key=3Yxxxx&sender_id=dEMO&to=${encodedPhone}&message=Your%20OTP%20is%20${encodedOtp}%20thanks`,
    method: 'POST',
    port: 443,
  };

  return new Promise((resolve, reject) => {
    const req = https.request(options, res => {
      let rawData = '';

      res.on('data', chunk => {
        rawData += chunk;
      });

      res.on('end', () => {
        try {
          resolve(JSON.parse(rawData));
        } catch (err) {
          reject(new Error(err));
        }
      });
    });

    req.on('error', err => {
      reject(new Error(err));
    });

    // 移除不必要的请求体写入
    req.end();
  });
}

exports.handler = async (event, context, callback) => {
  const challengeAnswer = Math.random().toString(10).substr(2, 4);
  const phoneNumber = event.request.userAttributes.phone_number;

  console.log(event, context);

  try {
    const data = await postRequest({
      phone: phoneNumber,
      answer: challengeAnswer,
    });
    console.log(`SMS sent to ${phoneNumber} and otp = ${challengeAnswer}`);
  } catch (err) {
    console.log(err.stack);
  }

  callback(null, event);
};

额外说明

  • encodeURIComponent()会对所有非URL安全字符进行转义,比如空格转成%20、+号转成%2B,完全符合URL规范
  • 路径中的%OTP%改成了%20OTP%20,原写法会被解析为非法的URL编码,正确的空格转义是%20
  • 用模板字符串拼接路径比+号更易读,也减少拼接错误的概率

内容的提问来源于stack exchange,提问作者hewa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 11:45:34