如何在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"}
问题根源
- 变量未正确初始化:
name和type仅在GlideRecord循环中赋值,若没有匹配的附件,变量会保留未定义状态,ServiceNow中直接引用GlideRecord字段会返回GlideElement对象,JSON序列化后变为{} - 附件逻辑缺陷:当前代码仅保留最后一个附件,未处理多附件场景
- 附件内容错误:使用固定字符串作为
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
相关产品推荐
相关产品推荐

