前端开发微信小程序,前端开发微信小程序框架

时间:2022-08-23 阅读:49 评论:0 作者:老李

Senparc.Weixin SDK介绍

Senparc.Weixin SDk是目前.net平台上使用率最高前端开发微信小程序的微信SDK前端开发微信小程序,除硬件平台暂未发布以外覆盖了所有微信平台模块,自2013年免费开源起已经持续更新了4年,是GitHub上目前Star和Fork数最多的中国C#开源项目。

目前大多数模块都支持了.net 4.0 / .net 4.5 / .net core 三个版本,Senparc官方计划在2017年将新增.net 4.6.2版本,并着力强化.net core版本,在7月左右会停止对.net 4.0版本的更新。

其中,小程序的模块命名为Senparc.Weixin.WxOpen,Nuget地址:

v1.0版本已于2017年1月9日凌晨同步上线!

Senparc.Wexin SDK系列库:

过去的4年时间,Senparc团队总共迭代发布了100多个稳定版本,目前总体框架已经比较完善,可以应对超高并发在内的各种系统环境。

以下是官方提供的一些资源:

官网:

源代码(包含Demo):

在线Demo(包含Nuget项目入口):

Demo公众号:盛派网络小助手

公众号开发系列教程:

下载类库chm帮助文档:

在线类库帮助文档:

微信开发资源汇总项目:

Senparc.Weixin SDK交流QQ群:342319110

小程序开发交流QQ群:108830388

微信平台生态关系

加上小程序之后,整个公众号的平台生态又多了一环,根据开发代码体量和包含关系(尤其是核心重叠部分),目前大概是下面这样的场景:

整个2016年,开发者们被微信小程序吊足了胃口,每每微信官方有小程序的动态发布,都会被刷屏,大众对微信小程序的关注程度可见一斑。

对于开发者来说,无论微信小程序的实际商业应用广度或是被接受度如何,这都将是一块熟悉又陌生的广阔的处女地。

由于小程序的多数规则和微信公众号基本一致,因此我们的小程序模块(Senparc.Weixin.WxOpen.dll)多数代码是从公众号模块(Senparc.Weixin.MP.dll)移植过来的。

微信官方为小程序准备了比较细致的文档和教程(这一点和当初公众号相比简直不在一个级别上),以下是一些重要的线上资源:

小程序开发文:https://mp.weixin.qq.com/debug/wxadoc/dev/

小程序设计指南:https://mp.weixin.qq.com/debug/wxadoc/design/

开发工具下载:

https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.HTML

下文将整理目前为止对小程序后端开发最为重要的知识点、注意点进行介绍,或给出对应资源的地址,并就和微信公众号相关的技能给出相关的索引。

对于一个在高并发场景下保持高可用性的小程序来说,这几个方面的处理细节是不可忽视的:

消息处理

上下文

消息加密

AccessToken令牌处理

高级接口

异步开发

分布式缓存

同步锁

webSocket

跟踪日志及异常处理

消息处理

微信小程序在进入客服会话状态之后,即可与客服进行对话,可以发送文字或图片信息,和公众号不同的是,目前小程序的消息系统是不能进行直接回复的,必须使用异步的方式调用“客服接口”推送消息(下文会讲到),为此我们优化了Senparc.Weixin SDK中的消息处理流程:MessageHandler。

MessageHandler是一个信消息的处理模块,也是整个微信开发过程中不可缺少的一部分。在MessageHandler中,开发者可以非常轻松地处理所有类型的微信消息。小程序的MessageHandler类叫做WxOpenMessageHandler。

在ASP.NET MVC我们只需要如下几步就可实现对消息的处理(和微信公众号一致)。

第一步:创建上上下文类,CustomWxOpenMessageContext.cs:

有关WxOpenMessageContext(上下文)的内容下文会讲到。

CustomWxOpenMessageContext.cs 代码如下:

using Senparc.Weixin.Context;

using Senparc.Weixin.WxOpen.Entities;

namespace Senparc.Weixin.MP.Sample.CommonService.WxOpenMessageHandler

{

public class CustomWxOpenMessageContext : MessageContext<IRequestMessageBase,IResponseMessageBase>

{

public CustomWxOpenMessageContext()

{

base.MessageContextRemoved += CustomMessageContext_MessageContextRemoved;

}

/// <summary>

/// 当上下文过期,被移除时触发的时间

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

void CustomMessageContext_MessageContextRemoved(object sender, Senparc.Weixin.Context.WeixinContextRemovedEventArgs<IRequestMessageBase,IResponseMessageBase> e)

{

/* 注意,这个事件不是实时触发的(当然你也可以专门写一个线程监控)

* 为了提高效率,根据WeixinContext中的算法,这里的过期消息会在过期后下一条请求执行之前被清除

*/

var messageContext = e.MessageContext as CustomWxOpenMessageContext;

if (messageContext == null)

{

return;//如果是正常的调用,messageContext不会为null

}

//TODO:这里根据需要执行消息过期时候的逻辑,下面的代码仅供参考

//Log.InfoFormat("{0}的消息上下文已过期",e.OpenId);

//API.SendMessage(e.OpenId, "由于长时间未搭理客服,您的客服状态已退出!");

}

}

}

源码:

第二步:创建自定义 CustomWxOpenMessageHandler.cs:

CustomWxOpenMessageHandler.cs 代码如下:

using System.IO;

using System.Web.Configuration;

using Senparc.Weixin.MP.Entities;

using Senparc.Weixin.MP.Entities.Request;

using Senparc.Weixin.MP.MessageHandlers;

using IRequestMessageBase = Senparc.Weixin.MP.Entities.IRequestMessageBase;

using IResponseMessageBase = Senparc.Weixin.MP.Entities.IResponseMessageBase;

namespace Senparc.Weixin.MP.Sample.CommonService.WxOpenMessageHandler

{

/// <summary>

/// 自定义MessageHandler

/// 把MessageHandler作为基类,重写对应请求的处理方法

/// </summary>

public partial class CustomWxOpenMessageHandler : MessageHandler<CustomWxOpenMessageContext>

{

private string appId = WebConfigurationManager.AppSettings["WxOpenAppId"];

private string appSecret = WebConfigurationManager.AppSettings["WxOpenAppSecret"];

public CustomWxOpenMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0)

: base(inputStream, postModel, maxRecordCount)

{

//这里设置仅用于测试,实际开发可以在外部更全局的地方设置,

//比如MessageHandler<MessageContext>.GlobalWeixinContext.ExpireMinutes = 3。

WeixinContext.ExpireMinutes = 3;

if (!string.IsNullOrEmpty(postModel.AppId))

{

appId = postModel.AppId;//通过第三方开放平台发送过来的请求

}

//在指定条件下,不使用消息去重

base.OmitRepeatedMessageFunc = requestMessage =>

{

var textRequestMessage = requestMessage as RequestMessageText;

if (textRequestMessage != null && textRequestMessage.Content == "容错")

{

return false;

}

return true;

};

}

public override void OnExecuting()

{

//测试MessageContext.StorageData

if (CurrentMessageContext.StorageData == null)

{

CurrentMessageContext.StorageData = 0;

}

base.OnExecuting();

}

public override void OnExecuted()

{

base.OnExecuted();

CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1;

}

/// <summary>

/// 处理文字请求

/// </summary>

/// <returns></returns>

public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)

{

//TODO:这里的逻辑可以交给Service处理具体信息,参考OnLocationRequest方法或/Service/LocationSercice.cs

//这里可以进行数据库记录或处理

return new SuccessResponseMessage();

}

public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)

{

//发来图片,进行处理

return DefaultResponseMessage(requestMessage);

}

public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)

{

//所有没有被处理的消息会默认返回这里的结果

return new SuccessResponseMessage();

//return new SuccessResponseMessage();等效于:

//base.TextResponseMessage = "success";

//return null;

}

}

}

源码:

微信小程序的MessageHandler和微信公众号的MessageHandler的一些区别见下表,这也是微信小程序和微信公众号在消息处理上的一些差别,务必须要注意!

微信公众号

微信小程序

请求消息类型

众多

1、 文本消息

2、 图片消息

请求事件类型

众多

进入会话(客服)事件

返回类型

1、基于

Senparc.Weixin.MP.

ResponseMessageBase

的子类

2、返回空字符串或"success"

(也可以使用SuccessResponseMessage)

只返回空字符串或"success"

(也可以使用SuccessResponseMessage)

注意:微信小程序的文本及图片类型请求消息,是在“客服对话”的状态下转发到开发者服务器的。

第三步:创建Conntroller,为使用MessageHandler做准备:

using System;

using System.IO;

using System.Web.Configuration;

using System.Web.Mvc;

using Senparc.Weixin.MP.Entities.Request;

using Senparc.Weixin.MP.MvcExtension;

using Senparc.Weixin.MP.Sample.CommonService. WxOpenMessageHandler;

namespace Senparc.Weixin.MP.Sample.Controllers.WxOpen

{

/// <summary>

/// 微信小程序Controller

/// </summary>

public partial class WxOpenController : Controller

{

public static readonly string Token = WebConfigurationManager.AppSettings["WxOpenToken"];//与微信公众账号后台的Token设置保持一致,区分大小写。

public static readonly string EncodingAESKey = WebConfigurationManager.AppSettings["WxOpenEncodingAESKey"];//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。

public static readonly string AppId = WebConfigurationManager.AppSettings["WxOpenAppId"];//与微信公众账号后台的AppId设置保持一致,区分大小写。

}

}

源码:

第四步:添加Action,使用MessageHandler

微信小程序的Url验证逻辑和微信公众号是一致的,为此我们需要添加2个Action来分别处理验证(GET)的请求和微信转发的消息请求(POST)。

GET请求:

/// <summary>

/// GET请求用于处理微信小程序后台的URL验证

/// </summary>

/// <returns></returns>

[HttpGet]

[ActionName("Index")]

public ActionResult Get(PostModel postModel, string echostr)

{

if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))

{

return Content(echostr); //返回随机字符串则表示验证通过

}

else

{

return Content("failed:" + postModel.Signature + "," + MP.CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" +

"如果你在浏览器中看到这句话,说明此地址可以被作为微信小程序后台的Url,请注意保持Token一致。");

}

}

GET请求部署完毕之后,我们直接使用浏览器打开,可以看到一条提示,表明此Action已经可以被访问:

POST请求:

/// <summary>

/// 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML。

/// </summary>

[HttpPost]

[ActionName("Index")]

public ActionResult Post(PostModel postModel)

{

if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))

{

return Content("参数错误!");

}

postModel.Token = Token;//根据自己后台的设置保持一致

postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致

postModel.AppId = AppId;//根据自己后台的设置保持一致

var maxRecordCount = 50;

//第一步:MessageHandler,对微信请求的详细判断操作都在这里面。

var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, maxRecordCount);

//第二步:执行微信处理过程

messageHandler.Execute();

//第三步:返回结果

return new FixWeixinBugWeixinResult(messageHandler);

}

核心的“三部曲”和微信公众号保持了完全的一致。

现在,只需要在Web.config中配置正确的AppId等信息,就可以在小程序后台完成“消息推送”接口的配置:

随后,我们就能在微信小程序的预览状态或正式发布之后,测试消息处理能力,下文中的“高级接口”部分我们会丰富消息处理过程,并进行发布。

上下文

在CustomWxOpenMessageHandler的基类设置的时候,我们看到使用了一个叫MessageContext的泛型(MessageHandler<MessageContext>),这个MessageContext是Senparc.Weixin SDK提供的一个默认的消息上下文处理类。

MessageContext主要用于消息由微信服务器统一转发的情况下,无法针对单个用户对话使用Session的情况,同时也具有消息去重等方法。这个默认的类已经能够处理最基础的情况,如果您的应用不是很复杂,那么直接用这个类就行了。如果项目比较复杂,您也可以根据自己的需要创建一个自己的类(实现IMessageContext接口),或继承这个类之后,再扩展更多的属性(例如工作流和比较特殊的分布式缓存,等等)。

上下文除了弥补Session的作用存储个人数据以外,还提供了完整对话记录的保存,对于机器人回复等场景都具有非常重要的作用。

Senparc.Weixin SDk已经对上下文功能进行了比较充分的解耦,使之成为一个独立的模块,可以进行自由的重写和扩展。开发者可以自由设置上下文保存的时间及条数。

微信服务器在没有迅速收到应用程序服务器回应的情况下,在5秒有效等待时间内,内会连续发送共计3条相同的消息,这会给程序带来一些困扰,因此,消息去重在MessageHandler里面是一个非常重要的“标配”。

消息去重已经在Senparc.Weixin.MP.MessageHandlers.MessageHandler.cs的OnExecuting()方法中实现,默认为开启,如果在某些特殊需求下需要关闭,可以将OmitRepeatedMessage参数设置为false。

由于去重方法是在OnExecuting()方法中优先执行,因此OmitRepeatedMessage的设置必须早于OnExecuting(),可以在MessageHandler的构造函数中,也可以在MessageHandler实例化之后进行设置,例如:

var messageHandler = new CustomMessageHandler(Request.InputStream, postModel);//接收消息(第一步)

messageHandler. OmitRepeatedMessage = false;

messageHandler.Execute();//执行微信处理过程

消息加密

出于消息安全考虑,微信提供了消息加密的方法,并且推荐使用。微信公众号的后台会要求开发者填写EncodingAESKey,或自动生成,如下图:

消息加密的状态在MessageHandler内部已经进行了自动判断及相关解码、编码的处理,因此开发者无因为消息加密的类型编写任何代码,只需要在微信后台按需设置即可。

在数据格式方面,微信小程序比微信公众号多提供了一个可选的JSON数据格式, Senparc.Weixin.WxOpen.dll将在v1.2版本提供对JSON格式的支持,目前请使用和微信公众号相同的XML格式。

AccessToken令牌处理

AccessToken是用于提供高级接口请求身份验证的令牌,由AppId及Secret通过api获得。AppId及Secret可以在微信小程序后台的【设置】>【开发设置】中找到或生成。

微信小程序的AccessToken令牌规则及使用方式与公众号完全兼容,因此我们在小程序模块中,共享了公众号的AccessToken处理模块:AccessTokenContainer。

AccessTokenContainer的作用是自动处理AccessToken的过期、储存(包括分布式缓存)等问题,让开发者可以专注到逻辑开发中,从而彻底忽略由于AccessToken延伸出来的一堆麻烦事。

AccessTokenContainer的使用要求开发者在Global或App_Start中对AppId及Secret进行注册(当然也可以在需要用到AccessToken之前的任意程序位置进行),例如:

protected void Application_Start()

{

/* 微信配置开始

*

* 建议按照以下顺序进行注册,尤其须将缓存放在第一位!

*/

RegisterWeixinCache(); //注册分布式缓存(按需,如果需要,必须放在第一个)

RegisterWeixinThreads(); //激活微信缓存及队列线程(必须)

RegisterSenparcWeixin(); //注册Demo所用微信公众号的账号信息(按需)

RegisterSenparcQyWeixin(); //注册Demo所用微信企业号的账号信息(按需)

RegisterWeixinPay(); //注册微信支付(按需)

RegisterWeixinThirdParty(); //注册微信第三方平台(按需)

ConfigWeixinTraceLog(); //配置微信跟踪日志(按需)

/* 微信配置结束 */

}

/// <summary>

/// 注册Demo所用微信公众号的账号信息

/// </summary>

private void RegisterSenparcWeixin()

{

//注册公众号

AccessTokenContainer.Register(

前端开发微信小程序,前端开发微信小程序框架

System.Configuration.ConfigurationManager.AppSettings["WeixinAppId"],

System.Configuration.ConfigurationManager.AppSettings["WeixinAppSecret"],

"【盛派网络小助手】公众号");

//注册小程序(完美兼容)

AccessTokenContainer.Register(

System.Configuration.ConfigurationManager.AppSettings["WxOpenAppId"],

System.Configuration.ConfigurationManager.AppSettings["WxOpenAppSecret"],

"【盛派互动】小程序");

}

上述红色的代码是和注册AccessTokenContainer有关的,实际上只需要添加3行代码,即可完全忘掉AccessToken这回事。

高级接口

小程序目前提供了包括二维码、Code换取、模板消息等少量的高级接口,并且兼容了微信公众号的文本及图片类型的客服接口(48小时内主动推送)。

Senparc.Weixin SDK为高级接口提供了非常高效、便捷的处理方式,用户如果已经注册了AppId及Secret,那么在调用高级接口的时候无需再提供AccessToken,只需提供AppId即可,AccessTokenContainer会自动识别传入的字符串属于AppId或是Secret,并对令牌过期等问题进行自动处理,确保在最大的可能性下面,开发者可以一次性得到期望的响应结果。

例如上文介绍的消息处理过程中,我们可以使用高级接口来即时回复用户:

/// <summary>

/// 处理文字请求

/// </summary>

/// <returns></returns>

public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)

{

//TODO:这里的逻辑可以交给Service处理具体信息,参考OnLocationRequest方法或/Service/LocationSercice.cs

//这里可以进行数据库记录或处理

//发送一条客服消息回复用户

var result = new StringBuilder();

result.AppendFormat("您刚才发送了文字信息:{0}rnrn", requestMessage.Content);

if (CurrentMessageContext.RequestMessages.Count > 1)

{

result.AppendFormat("您刚才还发送了如下消息({0}/{1}):rn", CurrentMessageContext.RequestMessages.Count,

CurrentMessageContext.StorageData);

for (int i = CurrentMessageContext.RequestMessages.Count - 2; i >= 0; i--)

{

var historyMessage = CurrentMessageContext.RequestMessages[i];

string content = null;

if (historyMessage is RequestMessageText)

{

content = (historyMessage as RequestMessageText).Content;

}

else if (historyMessage is RequestMessageEvent_UserEnterTempSession)

{

content = "[进入客服]";

}

else

{

content = string.Format("[非文字信息:{0}]", historyMessage.GetType().Name);

}

result.AppendFormat("{0} 【{1}】{2}rn",

historyMessage.CreateTime.ToShortTimeString(),

historyMessage.MsgType.ToString(),

content

);

}

result.AppendLine("rn");

}

//处理微信换行符识别问题

var msg = result.ToString().Replace("rn", "n");

//使用微信公众号的接口,完美兼容

Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);

return new SuccessResponseMessage();

//和公众号一样回复XML是无效的:

// return new SuccessResponseMessage()

// {

// ReturnText = string.Format(@"<?xml version=""1.0"" encoding=""utf-8""?>

//<xml>

// <ToUserName><![CDATA[{0}]]></ToUserName>

// <FromUserName><![CDATA[{1}]]></FromUserName>

// <CreateTime>1357986928</CreateTime>

// <MsgType><![CDATA[text]]></MsgType>

// <Content><![CDATA[TNT2]]></Content>

//</xml>",requestMessage.FromUserName,requestMessage.ToUserName)

// };

}

其中高级接口调用代码只需要一行:

Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);

进入客服的状态也用同样的方式处理:

public override IResponseMessageBase OnEvent_UserEnterTempSessionRequest(RequestMessageEvent_UserEnterTempSession requestMessage)

{

//进入客服

var msg = "欢迎您!这条消息来自Senparc.Weixin进入客服事件。";

Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);

return DefaultResponseMessage(requestMessage);

}

我们再来改写图片处理方式(注意,这次我们会使用异步模式):

public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)

{

//发来图片,进行处理

Task.Factory.StartNew(async () =>

{

await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(appId, WeixinOpenId, "刚才您发送了这张图片:");

await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendImageAsync(appId, WeixinOpenId, requestMessage.MediaId);

});

return DefaultResponseMessage(requestMessage);

}

上述代码中,使用了和微信公众号兼容的图片高级接口。

在微信小程序的预览或发布状态下,用户可以收到如下回复:

进入客服状态及发送文字及回复

发送图片及回复

异步开发

在高并发的场景下,异步编程可以说是入门级的一个必备。

在Senparc.Weixin SDK中,我们总共提供了400多个异步方法,除可能的个别遗漏以外,已经覆盖了所有的高级接口,方法命名规则为同步方法名后加Async。我们的目标是覆盖所有并发场景下会成为瓶颈的接口,其中MessageHandler的异步版本也已经计划在Senparc.Weixin.dll v4.11中上线。

异步方法的使用在.NET 4.5之后已经变得非常简单,例如下面的是文字消息的同步方法调用:

AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, "这是一条文字信息");

使用异步方法:

Task.Factory.StartNew(async () =>

{

await AdvancedAPIs.CustomApi.SendTextAsync(appId, WeixinOpenId, "这是一条文字信息");

});

分布式缓存

在需要考虑缓存生命周期、并发量等诸多因素的情况下,我们通常都会采用负载均衡来环节服务器压力,这种情况下几乎都会用到分布式缓存。

尤其对于微信的公众号的AccessToken,同一时间内真正有效的AccessToken只有一个(当然也存在一段时间的有效缓存,企业号是没有这个问题的),如果多台服务器各自管理自己内存中的AccessToken数据,必然会导致一场不会停止的“令牌争夺战”:各自判断AccessToken失效之后,刷新私有的AccessToken,直到当天的AccessToken获取次数被用完,系统无法提供高级接口服务。

这种情况下就需要一个统一的“数据中心”来储存AccessToken等独一份的信息,实现的方法有很多:分布式缓存、数据库、文件、数据交换模块等等,通常我们会首选分布式缓存。

Senparc.Weixin SDK为分布式缓存做了比较细致的架构,可以适应许多复杂情况下微信对于缓存的需求,并且提供了Memcached(Senparc.Weixin.Cache.Memcached.dll)和Redis(Senparc.Weixin.Cache.Redis.dll)两种缓存的实现。相比之下我们更推荐Redis,也在Redis模块上花了更多的精力进行优化。

Senparc.Weixin SDK已经实现了缓存策略的热切换,开发者可以在任意时候切换缓存状态(本地缓存、Reids、Memcached或自己扩展的任何缓存策略),并且不会影响到AccessTokenContainner等模块的稳定性。

Senparc.Weixin SDK默认缓存采用单机的本地内存,缓存生命周期和应用程序紧密联系,如果要突破这样的限制,或是在分布式环境中使用,就需要引入外部的缓存,以Redis缓存为例,我们只需要3行代码即可完成切换:

//定义Redis连接字符串

var redisConfiguration = System.Configuration.ConfigurationManager.AppSettings["Cache_Redis_Configuration"];

//设置Redis连接字符串

RedisManager.ConfigurationOption = redisConfiguration;

//指定缓存策略实例

CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);

只要配置正确,接下来的存取、序列化、储存格式、key管理等问题开发者都不必关心,无论使用什么缓存从策略,逻辑代码都无需做任何修改,Senparc.Weixin SDK的缓存策略接口已经对行为进行了严格规范,实现类会针对不同的缓存进行差别化的处理。

同步锁、WebSocket、跟踪日志及异常处理

请期待下一篇。

前端开发微信小程序,前端开发微信小程序框架

本文的Demo已经发布到GitHub的开源项目中,位于:https://github.com/JeffreySu/WeiXinMPSDK/tree/master/src/Senparc.Weixin.MP.Sample

以上部分内容节选自正在编写的《微信公众平台快速开发(拟)》图书,编写工作已经进入尾声,预计于年后进入出版流程,欢迎参与众筹:

.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文链接: https://www.liaier.com/2585.html 转载请注明出处!

相关文章 是不是在找它?!
评论区 交流一下吧!

共有0条评论来说两句吧...

欢迎 发表评论: