C#调用钉钉API发送通知教程
在企业信息化建设中,实时通知是提升工作效率的重要手段。钉钉作为国内流行的企业级通讯工具,提供了丰富的API接口,允许开发者通过代码发送各类通知。本文将详细介绍如何使用C#语言调用钉钉API发送通知,帮助企业实现自动化消息推送。
一、钉钉开放平台配置
在开始编码前,我们需要在钉钉开放平台完成相关配置,获取调用API所需的凭证。
1.1 创建钉钉应用
-
登录钉钉开放平台
-
进入"应用开发" -> "企业内部开发" -> "应用管理"
-
点击"创建应用",填写应用名称、描述等信息
-
上传应用图标,选择应用类型为"H5微应用"
-
点击"确定创建"按钮完成应用创建
1.2 获取应用凭证
应用创建成功后,我们需要获取两个重要的凭证:
-
AppKey:应用的唯一标识
-
AppSecret:应用的密钥,用于生成访问令牌
这两个凭证可以在应用详情页的"凭证与基础信息"中找到。
1.3 添加接口权限
为了能够发送通知,我们需要为应用添加相应的接口权限:
-
在应用详情页中,点击"权限管理"
-
搜索并添加以下权限:
-
企业消息通知
-
获取部门基础信息
(如需按部门发送通知) -
获取用户基本信息
(如需指定用户发送通知)
-
-
点击"申请权限",等待管理员审核通过
1.4 创建自定义机器人(可选)
除了通过应用发送通知外,我们还可以通过自定义机器人发送群消息:
-
打开钉钉群聊,点击右上角"群设置"
-
选择"智能群助手" -> "添加机器人"
-
选择"自定义机器人"
-
设置机器人名称、安全设置(建议选择"加签"方式)
-
完成创建后,复制Webhook地址,保存备用
二、C#项目准备
现在,我们开始准备C#项目环境,为调用钉钉API做准备。
2.1 创建项目
-
打开Visual Studio,创建一个新的控制台应用或Web应用项目
-
选择.NET Framework或.NET Core版本(本文以.NET Core 3.1为例)
2.2 添加必要的NuGet包
我们需要添加以下NuGet包来简化开发:
# 使用Package Manager Console Install-Package Newtonsoft.Json Install-Package RestSharp
或通过NuGet包管理器界面搜索并安装这些包。
2.3 创建配置文件
在项目中添加一个配置文件appsettings.json
,用于存储我们在钉钉开放平台获取的凭证信息:
{"DingTalk": {"AppKey": "your_app_key","AppSecret": "your_app_secret","AgentId": 123456789,"Webhook": "your_webhook_url","Secret": "your_robot_secret"} }
三、实现钉钉API调用工具类
接下来,我们将创建一个工具类,封装钉钉API的调用方法。
3.1 创建访问令牌管理类
首先,我们需要创建一个类来管理访问令牌(AccessToken):
using Newtonsoft.Json; using RestSharp; using System; using System.Configuration; using System.Threading.Tasks; public class DingTalkTokenManager {private static string _accessToken = string.Empty;private static DateTime _tokenExpireTime = DateTime.MinValue;private readonly string _appKey;private readonly string _appSecret; public DingTalkTokenManager(string appKey, string appSecret){_appKey = appKey;_appSecret = appSecret;} public async Task<string> GetAccessTokenAsync(){// 检查令牌是否已过期,如未过期则直接返回if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime){return _accessToken;} // 调用钉钉API获取新的访问令牌var client = new RestClient("https://oapi.dingtalk.com");var request = new RestRequest("gettoken", Method.GET);request.AddParameter("appkey", _appKey);request.AddParameter("appsecret", _appSecret); var response = await client.ExecuteAsync(request);var result = JsonConvert.DeserializeObject<dynamic>(response.Content); if (result.errcode == 0){_accessToken = result.access_token;// 访问令牌有效期为7200秒,我们设置提前10分钟刷新_tokenExpireTime = DateTime.Now.AddSeconds(result.expires_in - 600);return _accessToken;}else{throw new Exception($"获取访问令牌失败:{result.errmsg}");}} }
3.2 创建钉钉消息发送类
接下来,我们创建一个类来处理各种类型的消息发送:
using Newtonsoft.Json; using RestSharp; using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; public class DingTalkMessageSender {private readonly DingTalkTokenManager _tokenManager;private readonly string _agentId;private readonly string _webhook;private readonly string _secret; public DingTalkMessageSender(string appKey, string appSecret, string agentId, string webhook = "", string secret = ""){_tokenManager = new DingTalkTokenManager(appKey, appSecret);_agentId = agentId;_webhook = webhook;_secret = secret;} /// <summary>/// 发送工作通知消息/// </summary>public async Task<dynamic> SendWorkNoticeAsync(string userIdList, string deptIdList, string toAllUser, string message){var accessToken = await _tokenManager.GetAccessTokenAsync();var client = new RestClient("https://oapi.dingtalk.com");var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);request.AddParameter("access_token", accessToken); var requestBody = new{agent_id = _agentId,userid_list = userIdList, // 用户ID列表,多个用户用逗号分隔dept_id_list = deptIdList, // 部门ID列表,多个部门用逗号分隔to_all_user = toAllUser, // 是否发送给全员msg = new{msgtype = "text",text = new { content = message }}}; request.AddJsonBody(requestBody);var response = await client.ExecuteAsync(request);return JsonConvert.DeserializeObject<dynamic>(response.Content);} /// <summary>/// 通过机器人发送群消息/// </summary>public async Task<dynamic> SendRobotMessageAsync(string message){if (string.IsNullOrEmpty(_webhook)){throw new Exception("Webhook地址未配置");} var client = new RestClient(_webhook);var request = new RestRequest(Method.POST);// 如果配置了加签,需要生成签名if (!string.IsNullOrEmpty(_secret)){var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString();var stringToSign = $"{timestamp}\n{_secret}";var signature = ComputeHmacSha256(stringToSign);request.AddQueryParameter("timestamp", timestamp);request.AddQueryParameter("sign", signature);} var requestBody = new{msgtype = "text",text = new { content = message }}; request.AddJsonBody(requestBody);var response = await client.ExecuteAsync(request);return JsonConvert.DeserializeObject<dynamic>(response.Content);} /// <summary>/// 计算HMAC-SHA256签名/// </summary>private string ComputeHmacSha256(string data){var encoding = new UTF8Encoding();byte[] keyBytes = encoding.GetBytes(_secret);byte[] messageBytes = encoding.GetBytes(data); using (var hmacsha256 = new HMACSHA256(keyBytes)){byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);return Convert.ToBase64String(hashmessage);}} }
四、实现发送不同类型的通知
钉钉支持多种消息类型,除了基本的文本消息外,还支持链接、Markdown、卡片等丰富的消息类型。下面我们来实现几种常见的消息发送方式。
4.1 发送文本消息
/// <summary> /// 发送文本消息(扩展方法) /// </summary> public async Task<dynamic> SendTextMessageAsync(string userIdList, string message) {return await SendWorkNoticeAsync(userIdList, "", "false", message); }
4.2 发送Markdown消息
/// <summary> /// 发送Markdown消息 /// </summary> public async Task<dynamic> SendMarkdownMessageAsync(string userIdList, string title, string markdownText) {var accessToken = await _tokenManager.GetAccessTokenAsync();var client = new RestClient("https://oapi.dingtalk.com");var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);request.AddParameter("access_token", accessToken); var requestBody = new{agent_id = _agentId,userid_list = userIdList,to_all_user = "false",msg = new{msgtype = "markdown",markdown = new{title = title,text = markdownText}}}; request.AddJsonBody(requestBody);var response = await client.ExecuteAsync(request);return JsonConvert.DeserializeObject<dynamic>(response.Content); }
4.3 发送卡片消息
/// <summary> /// 发送卡片消息 /// </summary> public async Task<dynamic> SendActionCardMessageAsync(string userIdList, string title, string markdownText, string singleTitle, string singleURL) {var accessToken = await _tokenManager.GetAccessTokenAsync();var client = new RestClient("https://oapi.dingtalk.com");var request = new RestRequest("topapi/message/corpconversation/asyncsend_v2", Method.POST);request.AddParameter("access_token", accessToken); var requestBody = new{agent_id = _agentId,userid_list = userIdList,to_all_user = "false",msg = new{msgtype = "action_card",action_card = new{title = title,text = markdownText,single_title = singleTitle,single_url = singleURL,btn_orientation = "0"}}}; request.AddJsonBody(requestBody);var response = await client.ExecuteAsync(request);return JsonConvert.DeserializeObject<dynamic>(response.Content); }
五、完整使用示例
下面是一个完整的使用示例,展示如何在实际项目中使用我们创建的工具类发送钉钉通知:
using Microsoft.Extensions.Configuration; using System; using System.IO; using System.Threading.Tasks; class Program {static async Task Main(string[] args){// 读取配置文件var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build(); // 获取配置信息var appKey = configuration["DingTalk:AppKey"];var appSecret = configuration["DingTalk:AppSecret"];var agentId = configuration["DingTalk:AgentId"];var webhook = configuration["DingTalk:Webhook"];var secret = configuration["DingTalk:Secret"]; try{// 初始化消息发送器var messageSender = new DingTalkMessageSender(appKey, appSecret, agentId, webhook, secret); // 示例1:发送工作通知文本消息Console.WriteLine("发送工作通知文本消息...");var userIdList = "user1,user2";var textResponse = await messageSender.SendTextMessageAsync(userIdList, "这是一条测试消息,由C#程序发送。");Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(textResponse)}"); // 示例2:发送Markdown消息Console.WriteLine("\n发送Markdown消息...");var markdownTitle = "项目进度通知";var markdownContent = @"# 项目进度通报\n\n> 项目A已完成70%\n> 项目B已完成45%\n\n## 本周重点任务\n- 完成项目A的模块三开发\n- 开始项目B的系统测试\n\n**请相关负责人注意时间节点!**";var markdownResponse = await messageSender.SendMarkdownMessageAsync(userIdList, markdownTitle, markdownContent);Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(markdownResponse)}"); // 示例3:发送卡片消息Console.WriteLine("\n发送卡片消息...");var cardTitle = "重要会议通知";var cardContent = @"# 产品评审会议\n\n**时间:** 2023年10月15日 14:00-16:00\n**地点:** 三楼会议室A\n**参会人员:** 产品部全体成员、开发团队负责人\n\n## 会议议程\n1. 产品需求回顾\n2. 功能设计评审\n3. 开发进度同步\n4. 问题讨论与解决";var cardResponse = await messageSender.SendActionCardMessageAsync(userIdList, cardTitle, cardContent, "查看详情", "https://example.com/meeting-details");Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(cardResponse)}"); // 示例4:通过机器人发送群消息Console.WriteLine("\n通过机器人发送群消息...");var robotResponse = await messageSender.SendRobotMessageAsync("【系统通知】服务器备份任务已完成,备份文件大小:2.5GB");Console.WriteLine($"发送结果:{JsonConvert.SerializeObject(robotResponse)}"); Console.WriteLine("\n所有消息发送完成!");}catch (Exception ex){Console.WriteLine($"发送消息时发生错误:{ex.Message}");} Console.ReadLine();} }