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

如何在Microsoft Graph API请求体中动态发送附件?附报错排查

问题:ServiceNow业务规则调用Microsoft Graph API发送附件失败

问题现象

  • 调用Graph API时返回RequestBodyRead错误:
    {"error":{"code":"RequestBodyRead","message":"An unexpected 'StartObject' node was found for property named 'name' when reading from the JSON reader. A 'PrimitiveValue' node was expected."}}
    
  • 请求体中附件部分的name和contentType字段为空对象:
    "attachments":[{"@odata.type":"#microsoft.graph.fileAttachment","name":{},"contentType":{},"contentBytes":"SGVsbG8gV29ybGQh"}],"saveToSentItems":"true"}
    

问题根源

  1. 变量未正确初始化:name和type仅在GlideRecord循环中赋值,若没有匹配的附件,变量会保留未定义状态,ServiceNow中直接引用GlideRecord字段会返回GlideElement对象,JSON序列化后变为{}
  2. 附件逻辑缺陷:当前代码仅保留最后一个附件,未处理多附件场景
  3. 附件内容错误:使用固定字符串作为contentBytes,未实际读取附件的二进制内容并Base64编码

修正方案

解决步骤

  • 初始化变量为空字符串,避免未赋值时出现对象
  • 循环构建附件数组,支持多个附件
  • 使用GlideSysAttachment.getContentBase64()获取真实的附件Base64内容
  • 添加无附件时的判断,避免发送空附件节点

修正后的完整代码

var body = current.body_text;
var subject = current.subject;

// 初始化附件数组,避免无附件时出现异常
var attachments = [];
var att = new GlideRecord('sys_attachment');
att.addQuery('table_name', 'sys_email');
att.addQuery('table_sys_id', current.sys_id);
att.query();

// 循环处理所有匹配的附件
while(att.next()) {
    gs.info('Processing attachment: ' + att.file_name);
    // 显式转为字符串,避免返回GlideElement对象
    var fileName = att.file_name.toString();
    var contentType = att.content_type.toString();
    // 获取附件的Base64编码内容
    var contentBase64 = GlideSysAttachment.getContentBase64(att);

    attachments.push({
        "@odata.type": "#microsoft.graph.fileAttachment",
        "name": fileName,
        "contentType": contentType,
        "contentBytes": contentBase64
    });
}

var restMessage = new sn_ws.RESTMessageV2('Email PoC', 'POST Email User');
restMessage.setStringParameter('id', current.u_emailbox_id);

// 处理CC收件人逻辑
var ccAddresses = current.getValue('copied');
var ccRecipients = [];
if (ccAddresses) {
    var ccList = ccAddresses.replace(/\s*,\s*/g, ',').split(',');
    for (var i = 0; i < ccList.length; i++) {
        ccRecipients.push({
            "emailAddress": {
                "address": ccList[i].trim()
            }
        });
    }
}

// 构建请求体基础结构
var requestBody = {
    "message": {
        "subject": current.getValue('subject'),
        "body": {
            "contentType": "html",
            "content": current.getValue('body')
        },
        "toRecipients": [{
            "emailAddress": {
                "address": current.getValue('direct')
            }
        }],
        "internetMessageHeaders": [{
            "name": "X-MSIP-APP-classification",
            "value": "Restricted"
        }]
    },
    "saveToSentItems": "true"
};

// 仅当存在附件时,添加attachments节点
if (attachments.length > 0) {
    requestBody.message.attachments = attachments;
}

// 发送请求并记录日志
gs.info('Final Request Body: ' + JSON.stringify(requestBody));
restMessage.setRequestBody(JSON.stringify(requestBody));
var response = restMessage.execute();

var responseBody = response.getBody();
var httpStatus = response.getStatusCode();

gs.info('API Response Body: ' + responseBody);
gs.info('HTTP Status Code: ' + httpStatus);

关键说明

  • 所有GlideRecord字段调用.toString()确保获取字符串值,彻底避免返回GlideElement对象
  • 使用GlideSysAttachment.getContentBase64()直接获取附件的Base64编码内容,无需手动处理二进制数据
  • 仅当存在附件时才添加attachments节点,避免发送空数组或无效对象
  • 优化CC收件人处理逻辑,增加trim()清除邮箱地址前后空格

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:47:07