ASP.NET实现微信功能(1)(创建菜单,验证,给菜单添加事件)
LZ实在 不知道怎么起名字了,索性就取了这个名字,开始吧,说实在的,想给自己的平常的学习做一个总结,总是忘了总结。也只能给工作做一个总结了。
我打算用2篇文章来写,第一篇是关于订阅号的,就是这个号,另一篇是关于服务号的,到时候会介绍更多的东西,闲话不多,开始吧。
首先,我们需要一个能创建自定义菜单的订阅号,微信的个人认证是不可能获得订阅号的,只有企业或者政府机构认证才可以申请。
首先我还是从创建菜单说起吧,创建菜单我们不需要进行服务器配置,我们只需要appid和appsecret就行了,这个东西从微信公众平台的后台进去,然后找到开发者中心。
如下图所示。
只有申请了认证的才可以活动APPID 和APPSECRET,然后我们来创建菜单吧,下面的APPID和APPSECRET将会用XXX代替,带来的不便请谅解。
首先我们需要在前台创建一个按钮,一个创建菜单的按钮,一个删除菜单的按钮。
这次因为赶时间,我把菜单给写死了,下一篇文章写的时候,我会展示给大家一个灵活的菜单。
下面是前台,就2个按钮,一个删除,一个添加修改按钮,针对订阅号。
<div class="btn"> <div><asp:Button runat="server" ID="Add_Menu" OnClick="Add_Menu_Click" Text="创建(修改)菜单"/></div> <div><asp:Button runat="server" ID="Del_Menu" OnClick="Del_Menu_Click" Text="删除菜单" OnClientClick="return confirm('是否删除菜单?')" />
</div>
后台代码我就一个一个解释吧。第一个为APPID和APPSECRET,
公众号有调用接口,第一个为授权,第二个为创建菜单的接口,第三个为删除菜单的接口。
如果有不懂的,可以参考官方API:http://mp.weixin.qq.com/wiki/index.php?title=%E9%A6%96%E9%A1%B5
/*以下为订阅号*/
static string appId = "XXXX";//公众号的appId
static string appSecret = "XXXXX";//公众号的appSecret /*以下为公共接口调用URL*/
static string appUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
static string postUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
static string postDelUrl = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token="; //删除菜单的URL
我们要APPID和APPSECRET有什么作用呢?答案就是,我们要获得接入微信的一个凭证,而这个凭证,就是ACCESS_TOKEN.
下面是获得ACCESS_TOKEN的代码。具体的代码我也不是太懂,反正就是一个转化,有兴趣的可以自己测试一下,我就不多累赘了。
//获得ACCESS_TOKEN,通过appid和app_secect获得(订阅号)
public static string GetAccessToken()
{
WebClient webClient = new WebClient();
Byte[] bytes = webClient.DownloadData(string.Format("{0}&appid={1}&secret={2}", appUrl, appId, appSecret));
string result = Encoding.GetEncoding("utf-8").GetString(bytes); //JObject jObj = JObject.Parse(result);
//JToken token = jObj["access_token"];
//return token.ToString().Substring(1, token.ToString().Length - 2); string[] temp = result.Split(',');
string[] tp = temp[0].Split(':');
return tp[1].ToString().Replace('"', ' ').Trim().ToString(); }
下面是删除菜单的代码,主要 也是通过HTTP请求然后把请求转化成流的形式,然后把流读出来。
private void DelMenu(string url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentNullException("url");
}
Encoding encoding = Encoding.UTF8;
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream instream = response.GetResponseStream();
StreamReader sr = new StreamReader(instream, encoding);
string content = sr.ReadToEnd(); }
当然上面的删除是部分代码:
我们当然删除是通过微信给的URL去删除啦。
//删除菜单(订阅号)
protected void Del_Menu_Click(object sender, EventArgs e)
{
DelMenu(postDelUrl + GetAccessToken());
}
下面我们来说一下怎么创建菜单,微信官方已经给我们提供了JSON的示例,如下图:
我也附上我自己的代码。
//创建微信菜单JSON字符串
public string GetWXMenuStr()
{
string weixin1 = "";
weixin1 += "{\n";
weixin1 += "\"button\":[\n";
weixin1 += "{\n";
// weixin1 += "\"type\":\"click\",\n";
//第一个菜单
weixin1 += "\"name\":\"公共信息\",\n";
weixin1 += "\"sub_button\":[\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"通知公告\",\n";
weixin1 += "\"key\":\"11\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"工作动态\",\n";
weixin1 += "\"key\":\"12\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"政策法规\",\n";
weixin1 += "\"key\":\"13\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"经济视野\",\n";
weixin1 += "\"key\":\"14\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"专题报道\",\n";
weixin1 += "\"key\":\"15\"\n";
weixin1 += "}]\n";
weixin1 += "},\n";
//第二个菜单
weixin1 += "{\n";
//weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"公共服务\",\n";
weixin1 += "\"sub_button\":[\n";
weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"企业之窗\",\n";
weixin1 += "\"key\":\"21\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"金融服务\",\n";
weixin1 += "\"key\":\"22\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"创业指导\",\n";
weixin1 += "\"key\":\"23\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"管理服务\",\n";
weixin1 += "\"key\":\"24\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"click\",\n";
weixin1 += "\"name\":\"法律服务\",\n";
weixin1 += "\"key\":\"25\"\n";
weixin1 += "}]\n";
weixin1 += "},\n";
//第三个菜单(view类型的)
weixin1 += "{\n";
weixin1 += "\"name\":\"互动交流\",\n";
weixin1 += "\"sub_button\":[\n";
weixin1 += "{\n";
weixin1 += "\"type\":\"view\",\n";
weixin1 += "\"name\":\"服务信箱\",\n";
weixin1 += "\"url\":\"http://www.baidu.com\"\n";
weixin1 += "},\n"; weixin1 += "{\n";
weixin1 += "\"type\":\"view\",\n";
weixin1 += "\"name\":\"网上咨询\",\n";
weixin1 += "\"url\":\"http://www.soso.com\"\n";
weixin1 += "}]\n";
weixin1 += "}\n";
weixin1 += "}]\n"; weixin1 += "}\n";
return weixin1;
}
需要注意的是,微信一级菜单只允许取3个,二级菜单最多5个,请大家注意。
下面是创建菜单的代码,说实在的我挺失败的,也是有点不求甚解,先放上来吧反正:
/// <summary>
///
/// </summary>
/// <param name="url"></param>
/// <param name="postData"></param>
private void PostMenuData(string url, string postData)
{
Stream outstream = null;
Stream instream = null;
StreamReader sr = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
Encoding encoding = Encoding.UTF8;
byte[] data = encoding.GetBytes(postData);
// 准备请求...
try
{
// 设置参数
request = WebRequest.Create(url) as HttpWebRequest;
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
outstream = request.GetRequestStream();
outstream.Write(data, 0, data.Length);
outstream.Close();
//发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
//直到request.GetResponse()程序才开始向目标网页发送Post请求
instream = response.GetResponseStream();
sr = new StreamReader(instream, encoding);
//返回结果网页(html)代码
string content = sr.ReadToEnd();
string err = string.Empty;
// return content;
}
catch (Exception ex)
{
string err = ex.Message;
//return string.Empty;
}
}
下面点击添加按钮就执行如下方法:
/// <summary>
/// 创建自定义菜单(订阅号)
/// </summary>
private void CreateWxMenu()
{ string weixin1=GetWXMenuStr(); PostMenuData(postUrl + GetAccessToken(), weixin1);
}
好了,到此我们的菜单就创建好了,下面来介绍一下如何给菜单添加事件,如果是VIEW类型的,只要给一个URL就行了,上面有写到,
如果是CLICK类型的,就要指定KEY,KEY是唯一的一个标识符。
不过在这之前,我们先要知道一点,微信必须要先有一个转发的文件,我们的微信和我们的网站的通信,就通过这个转发URL,打开开发者中心:
我们首先要启用服务器配置,把自己网站的那个转发文件配置进去,注意:微信只支持80端口,如果有一个订阅号,另一个服务号要用到的话,怎么办呢,写2个转发文件就够了。
废话不多说,如果你有条件的话,建议你去买一个云服务器做测试,我做测试是给公司买了一个云服务器的,坑死人,连报销都不给。
我这次是用的一个ASHX文件做转发,当然你也可以用ASPX文件,反正看自己喜好,那么我们还是来分析一下ASHX文件吧。
我们首先要知道,第一次进行验证的时候,肯定是要验证这个文件的,那么就要一个TOKEN,这个TOKEN只用验证一次,验证完了就没用了。
TOKEN是我们自己设置的,但是文件里的TOKEN要和服务器配置里的一样哦,而且验证通过了要启动服务器,这是很容易疏忽的,我都被忽悠了几次,哈哈。
因为第一次验证的时候的请求是GET的,如果验证通过了是POST的,所以我们可以在ProcessRequest方法这么写:
public void ProcessRequest (HttpContext context) {
//context.Response.ContentType = "text/plain";
//context.Response.Write("Hello World");
//以下代码只要用一次就行了,通过了就不必 再用了
string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "GET")
{
string token = "123"; //填写微信服务端的TOKEN if (string.IsNullOrEmpty(token))
{
return;
} string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["signature"];
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"]; if (!string.IsNullOrEmpty(echoString))
{
HttpContext.Current.Response.Write(echoString);
HttpContext.Current.Response.End();
} } else if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
Handle(postString);
}
} }
下面我们主要是以HANDEL方法为主线来看这个文件。
/// <summary>
/// 处理信息并应答
/// </summary>
private void Handle(string postStr)
{
//messageHelp help = new messageHelp(); string responseContent = ReturnMessage(postStr); HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
HttpContext.Current.Response.Write(responseContent);
}
其他的都可以无视,我们的重点主要是在这个ReturnMessage方法上面,就是说,微信服务端给我们传了一串字符串,我们的目的,就是把这串字符串解密。
试想一下,如果我们点击了微信菜单上了某一个内容,然后他会传字符串到我们这个ASHX文件中,如下,有可能是EVENT,事件,点击事件。
//返回消息
public string ReturnMessage(string postStr)
{
string responseContent = "";
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(new System.IO.MemoryStream(System.Text.Encoding.GetEncoding("GB2312").GetBytes(postStr)));
XmlNode MsgType = xmldoc.SelectSingleNode("/xml/MsgType");
if (MsgType != null)
{
switch (MsgType.InnerText)
{
case "event":
responseContent = EventHandle(xmldoc);//事件处理
break;
case "text":
responseContent = TextHandle(xmldoc);//接受文本消息处理
break;
default:
break;
}
}
return responseContent;
}
如果是事件的话,那么我们就需要返回这个事件处理之后的字符串,我们这次重点来看这个EvenHandler.关键是看前面的,我后面写了一些业务代码,
大家可以无视,都是关于ID的。大家想一下,我们创建菜单的时候,是不是指定过KEY,这里就可以用到哦,其中下面的ResponseContent也是返回字符串。
public string EventHandle(XmlDocument xmldoc)
{
string responseContent = "";
XmlNode Event = xmldoc.SelectSingleNode("/xml/Event");
XmlNode EventKey = xmldoc.SelectSingleNode("/xml/EventKey");
XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName");
XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName");
if (Event!=null)
{
//菜单单击事件
if (Event.InnerText.Equals("CLICK"))
{
if (EventKey.InnerText.Equals("11"))//click_one
{
string typeid = "d19cace294e745fa8e38a8a1593703"; //类型ID:通知公告 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("12"))//click_two
{
string typeid="dd2321c73d3946709c49f3fcb16d78"; //类型ID:工作动态 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("13"))//click_three
{
string typeid = "3fbac2fbc0d44367a5358c80529e05"; //类型ID:政策法规 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
} else if (EventKey.InnerText.Equals("14"))//click_three
{
string typeid = "9c1fbf93601a4285a77614e9b1261f"; //类型ID:经济视野 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
} else if (EventKey.InnerText.Equals("15"))//click_three
{
string typeid = "14f0d6b7c4204035b82e3e83e81e59"; //类型ID:专题报道 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
} else if (EventKey.InnerText.Equals("21"))//click_three
{
string typeid = "ae6fc69cabfa4d059d5ba17bde1faf"; //类型ID:企业之窗 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("22"))//click_three
{
string typeid = "fb86dbe3d7ed4df4850f7f24ce128b"; //类型ID:金融服务 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("23"))//click_three
{
string typeid = "f961aa8accba454f840355f67cb492"; //类型ID:创业指导 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("24"))//click_three
{
string typeid = "0e5033c616214fe484cdff377741b3"; //类型ID:管理服务 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
}
else if (EventKey.InnerText.Equals("25"))//click_three
{
string typeid = "6f22bdf17708471e840a653931d4ec"; //类型ID:法律服务 responseContent = GetStrJsonDynamiclly(xmldoc, typeid);
} }
}
return responseContent;
}
上面的是GetStrJsonDynamiclly方法的掠影,主要是业务逻辑,大家可以参考下。
//获得要上传到服务器的字符串,其中的typeid为动态值
public string GetStrJsonDynamiclly(XmlDocument xmldoc,string typeid)
{ string responseContent = "";
XmlNode Event = xmldoc.SelectSingleNode("/xml/Event");
XmlNode EventKey = xmldoc.SelectSingleNode("/xml/EventKey");
XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName");
XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName");
string picUrl = ""; //图片地址;
string basicPicUrl = "http://XXX/XXX/pic/";
//创业指导 if (typeid == "f961aa8accba454f840355f67cb492")
{
picUrl = basicPicUrl + "cyzd.jpg";
}
//法律服务
else if (typeid == "6f22bdf17708471e840a653931d4ec")
{
picUrl = basicPicUrl + "flfw.jpg";
} string strJson = string.Format(ReplyType.Message_News_Main,
FromUserName.InnerText,
ToUserName.InnerText,
DateTime.Now.Ticks,
"6",
string.Format(ReplyType.Message_News_Item, GetDataByIndex(1, typeid), "",
picUrl,
basicIP + GetArticleParams(1, typeid)[0]) +
string.Format(ReplyType.Message_News_Item, GetDataByIndex(2, typeid), "",
"",
basicIP + GetArticleParams(2, typeid)[0])
+
string.Format(ReplyType.Message_News_Item, GetDataByIndex(3, typeid), "",
"",
basicIP + GetArticleParams(3, typeid)[0]) +
string.Format(ReplyType.Message_News_Item, GetDataByIndex(4, typeid), "",
"",
basicIP + GetArticleParams(4, typeid)[0]) +
string.Format(ReplyType.Message_News_Item, GetDataByIndex(5, typeid), "",
"",
basicIP + GetArticleParams(5, typeid)[0]) +
string.Format(ReplyType.Message_News_Item, GetDataByIndex(6, typeid), "",
"",
basicIP + GetArticleParams(6, typeid)[0]));
return strJson; }
总之,上面的代码只有一个目的,就是返回字符串,比如点击微信的菜单,可以返回6篇文章,其中可以给文章指定背景图片。其中的方法是获取URL和文章标题的,具体就不累赘了,根据自己的业务具体来办。
另外还给大家提供3个工具方法:
大家根据需求自由使用,具体的完全的代码,我就不给哦。
//写入日志
public void WriteLog(string text)
{
StreamWriter sw = new StreamWriter(HttpContext.Current.Server.MapPath(".") + "\\log.txt", true);
sw.WriteLine(text);
sw.Close();//写入
}
} //回复类型
public class ReplyType
{
/// <summary>
/// 普通文本消息
/// </summary>
public static string Message_Text
{
get
{
return @"<xml>
<ToUserName><![CDATA[{0}]]></ToUserName>
<FromUserName><![CDATA[{1}]]></FromUserName>
<CreateTime>{2}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{3}]]></Content>
</xml>";
}
}
/// <summary>
/// 图文消息主体
/// </summary>
public static string Message_News_Main
{
get
{
return @"<xml>
<ToUserName><![CDATA[{0}]]></ToUserName>
<FromUserName><![CDATA[{1}]]></FromUserName>
<CreateTime>{2}</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>{3}</ArticleCount>
<Articles>
{4}
</Articles>
</xml> ";
}
}
/// <summary>
/// 图文消息项
/// </summary>
public static string Message_News_Item
{
get
{
return @"<item>
<Title><![CDATA[{0}]]></Title>
<Description><![CDATA[{1}]]></Description>
<PicUrl><![CDATA[{2}]]></PicUrl>
<Url><![CDATA[{3}]]></Url>
</item>";
}
}
好了,这一篇因为时间和技术原因就介绍到这里了,过1,2个星期我将会把灵活在后台配置菜单以及服务号C#后台推送图文消息跟大家讲清楚。
ASP.NET实现微信功能(1)(创建菜单,验证,给菜单添加事件)的更多相关文章
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇 第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是 ...
- ASP.NET MVC 微信公共平台开发之验证消息的真实性
ASP.NET MVC 微信公共平台开发 验证消息的真实性 在MVC Controller所在项目中添加过滤器,在过滤器中重写 public override void OnActionExecuti ...
- ASP.NET实现微信功能(2)(服务号高级群发)
前面写了一篇文章,关于微信的:http://www.cnblogs.com/kmsfan/p/4047097.html 今天打算来写本系列的第二批文章,服务号后台群发. 在写本篇文章之前,我们先来看看 ...
- [c#]asp.net开发微信公众平台(8)微信9大高级接口,自定义菜单
前7篇把最基础的消息接收和回复全做完了, 也把高级接口的入口和分拆处理写好了空方法, 此篇接着介绍微信的9大高级接口, 并着重讲解其中的自定义菜单. 微信9大接口为: 1.语音识别接口 2.客服接 ...
- 微信程序开发系列教程(四)使用微信API创建公众号自定义菜单
大家可能经常看到一些微信公众号具有功能强大的自定义菜单,点击之后可以访问很多有用的功能. 这篇教程就教大家如何动手做一做. 这个教程最后实现的效果是:创建一个一级菜单"UI5", ...
- C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)
C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...
- asp.net 输出微信自定义菜单json
这里使用LitJson.dll作json解析. 微信规定的自定义菜单json样式如下: { "button":[ { "type":"click&qu ...
- asp.net 仿微信端菜单设置
第一步:添加引用文件 <link rel="stylesheet" href="~/assets/css/bootstrap.min.css"> & ...
随机推荐
- iOS 琐碎点------切某个或某几个角的圆角
不说废话----------> 1.如果是切四个角的圆角,代码示例: self.picImage.layer.cornerRadius = 8; self.picImage.layer.mask ...
- radio相关
radio 按钮组, name=”sex”. <input type="radio" name="sex" value="Male"& ...
- C#_基础:委托速讲
1定义:委托=函数指针 C# public delegate void Test(string str); 等价C++ public void (*Test)(string str): 委托赋值(初始 ...
- opecv获取图像轮廓
获取轮廓 #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/ ...
- 【Java并发编程实战】-----“J.U.C”:锁,lock
在java中有两种方法实现锁机制,一种是在前一篇博客中([java7并发编程实战]-----线程同步机制:synchronized)介绍的synchronized,而另一种是比synchronized ...
- [ASP.NET MVC 小牛之路]07 - URL Routing
我们知道在ASP.NET Web Forms中,一个URL请求往往对应一个aspx页面,一个aspx页面就是一个物理文件,它包含对请求的处理. 而在ASP.NET MVC中,一个URL请求是由对应的一 ...
- css双飞翼布局
双飞翼布局是一种比较灵活的布局,始于淘宝UED,玉伯提出的,他着重介绍的是双飞翼栅格布局. 三列布局为"双飞燕"布局,可以把三栏比作一只鸟,main部分相当是于鸟的身体,而lef ...
- Linux线程体传递参数的方法详解
传递参数的两种方法 线程函数只有一个参数的情况:直接定义一个变量通过应用传给线程函数. 例子 #include #include using namespace std; pthread_t thre ...
- jQuery源码分析系列(31) : Ajax deferred实现
AJAX的底层实现都是浏览器提供的,所以任何基于api上面的框架或者库,都只是说对于功能的灵活与兼容维护性做出最优的扩展 ajax请求的流程: 1.通过 new XMLHttpRequest 或其它的 ...
- NodeJS POST Request Over JSON-RPC
1.npm install art-template2.npm install request3.在app.js中加入以下代码转html: var template = require('art-t ...