一个简单的小 POST 转发还要编译一套程序然后部署并且运维?太复杂了!能不能只写一个文件就实现这样的功能呢?只要有 Serverless 的函数计算功能即可。这里使用阿里云函数计算功能(小量免费哦),其他平台同理。
0. 准备工作
你需要有 C#编程基础才能愉快的阅读本文章。如果你只使用过 WinForm、WPF、ASP.NET 等传统.NET 技术,请不要错过.NET Core 这个全新的跨平台产品~(疯狂安利)
请准备好.NET Core SDK 以及一个文本编辑器,推荐使用 VS Code。
请阅读腾讯 Bugly 和钉钉机器人相关文档。
推荐也阅读一下阿里云官方文档函数计算 > 构建函数 > 编程语言 > C# > C# 函数入口,本文根据需求使用的是 HTTP 触发器,但是这文档写的确实很乱,我看了半天才看懂……
1. 创建项目,添加依赖
mkdir BuglyToDingtalkServerless
cd BuglyToDingtalkServerless
dotnet new console -f netcoreapp2.1
官方示例使用的是 webapi 模板,为什么这里使用 console 呢?因为 webapi 会创建一堆在函数计算中使用不到的东西,为了避免还要删除一堆。由于在撰写本文时阿里云还不支持最新的 2.2 版本,所以我们还需要指定.NET Core 的版本。继续添加引用
dotnet add package Aliyun.Serverless.Core
dotnet add package Aliyun.Serverless.Core.Http
dotnet add package Newtonsoft.Json
最后得到的 BuglyToDingtalkServerless.csproj
文件如下
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType> <!--这行可以删除-->
<TargetFramework>netcoreapp2.1</TargetFramework> <!--注意这里的版本-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aliyun.Serverless.Core" Version="1.0.1" />
<PackageReference Include="Aliyun.Serverless.Core.Http" Version="1.0.2" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.11" /> <!--注意这里的版本-->
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>
</Project>
2. 编写业务代码
创建一个文件,名称为 FcRemoteEntrypoint.cs
,当然你想塞 Program.cs
里也没问题。结合腾讯 Bugly 文档中的 example(注意那里面有的参数名竟然是错的,考验英文水平的时候到了),进行开发,下面直接贴代码了,请看注释
(提示:点击代码段左上角的三个小按钮可以将代码全屏)
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Aliyun.Serverless.Core;
using Aliyun.Serverless.Core.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
namespace BuglyToDingtalkServerless
{
public class SingleHttpHandler : FcHttpEntrypoint
{
// 钉钉机器人 webhook 地址
const string webhook = "https://oapi.dingtalk.com/robot/send?access_token=your_token";
protected override void Init(IWebHostBuilder builder)
{
}
// 这里是真正的入口
public override async Task<HttpResponse> HandleRequest(HttpRequest request, HttpResponse response, IFcContext fcContext)
{
string method = request.Method;
string relativePath = request.Path.Value;
fcContext.Logger.LogInformation("method = {0}; requestPath = {1}", method, relativePath);
// 读取请求内容
StreamReader sr = new StreamReader(request.Body);
string requestBody = sr.ReadToEnd();
fcContext.Logger.LogInformation("requestBody = {}", requestBody);
// 没有见过 dynamic 的话千万不要慌,这是动态类型,我只是为了下面写起来像 js 才这么玩的
// 用常规的 JObject 都可以的,就是下面引用值就变成了 value["param"]
dynamic value = JsonConvert.DeserializeObject(requestBody);
string msgTitle = $"每日运营统计 - {value.eventContent.date}";
string msgText = "";
if (value.eventType == "bugly_crash_trend")
{
msgText += $"### {msgTitle}\n\n\n";
msgText += "版本: 用户访问量/崩溃次数/受影响用户\n\n";
foreach (var s in value.eventContent.datas)
{
msgText += $"[{s.version}: {s.accessUser} / {s.crashCount} / {s.crashUser}]({s.url})\n\n";
}
}
var msg = new { msgtype = "markdown", markdown = new { title = msgTitle, text = msgText } };
fcContext.Logger.LogInformation("ToDingtalkBody = {}", msg);
// POST 拼好的 json 到钉钉 webhook
await new HttpClient().PostAsJsonAsync(webhook, msg);
response.StatusCode = 200;
return response;
}
}
}
3. 编译与部署
发布程序
dotnet publish -c Release
将文件夹下 bin/Release/netcoreapp2.1/publish
下的文件(一堆 dll)打包成 zip(不要将 publish 文件夹打包进去)
下面我们将程序部署到阿里云函数计算中,打开阿里云函数计算后台,新建函数,选择空白函数
触发器配置,选择 HTTP 触发器,认证方式为匿名(因为腾讯 Bugly 的 Webhook 不支持认证),请求方式为 POST
基础信息请按自己的喜好设置,运行环境请选 dotnetcore,代码包上传选择之前打包好的 zip
函数入口如果是按照上文中的代码来的话,设置为:BuglyToDingtalkServerless::BuglyToDingtalkServerless.SingleHttpHandler::HandleRequest
剩下的安全配置请根据实际情况配置。
创建好后我们来到函数的触发器中,就可以得到供腾讯 Bugly 使用的 Webhook 地址了!将这个地址填入 Bugly 配置中,静候第二天早上 9 点的推送吧!(前提是应用有人用,产生了统计数据)
在此之前我们可以用假数据先测试一下,在代码执行里,请求方式 POST,路径空,Body 设置为 raw,JSON 格式,下面给到一份测试样例
{
"eventType": "bugly_crash_trend",
"timestamp": 1462780713515,
"isEncrypt": 0,
"eventContent": {
"datas": [
{
"accessUser": 12972,//联网用户数
"crashCount": 21,//crash 次数
"crashUser": 20,//crash 影响用户数
"version": "1.2.3",//app 版本号
"url": "http://bugly.qq.com/realtime?app=1104512706&pid=1&ptag=1005-10003&vers=0.0.0.12.12&time=last_7_day&tab=crash"
},
{
"accessUser": 15019,
"crashCount": 66,
"crashUser": 64,
"version": "1.2.4",
"url": "http://bugly.qq.com/realtime?app=1104512706&pid=1&ptag=1005-10003&vers=0.0.0.12.12&time=last_7_day&tab=crash"
},
{
"accessUser": 15120,
"crashCount": 1430,
"crashUser": 1423,
"version": "1.2.4",
"url": "http://bugly.qq.com/realtime?app=1104512706&pid=1&ptag=1005-10003&vers=0.0.0.12.12&time=last_7_day&tab=crash"
}
],
"appId": "1104512706", //appId
"platformId": 1, //平台
"appName": "AF", //app 名称
"date": "20160508",
"appUrl":"http://bugly.qq.com/issueIndex?app=1104512706&pid=1&ptag=1005-10000"
},
"signature": "ACE346A4AE13A23A52A0D0D19350B466AF51728A"
}
Enjoy~
Comments | 2 条评论
博主 rsong
hi, 您好, 请问下, “但是这文档写的确实很乱,我看了半天才看懂……”, 这个文档结构您作为读者觉得应该怎么排版才好呢?
博主 SkiTiSu
@rsong 感谢大佬光临~我认为比较好的方式是从创建新项目开始一步一步手把手介绍,当碰到新的概念时先简单讲一下,把 demo 跑通,快速跑通我觉得还是挺重要的,怎么也运行不起来很令人抓狂,可以展示一下项目的文件目录结构是怎么样的,或者同时提供完整 demo 工程下载,另外,与 demo 项目匹配的在阿里云 Web 上的操作最好也能提供步骤介绍。最后才是完整的 API 文档。