2014年8月11日星期一

微信公众帐号开发 - 查歌词

想做一个歌词查询的工具有很长时间了,之前的想法一直是在web端,提供高质量的封面图,精美排版的歌词,作词作曲演唱者信息,如果可能再加入试听与购买链接,要是能再提供些背后的故事与独家采访就更牛逼了。
这样的想法需要大量的人力物力还有高超的技术才能运转的起来,起码目前我单枪匹马是无法完成的,所以一直拖着无法实现。昨天突然想起来我可以在微信公众平台上面建立一个公众号,提供查询功能,初期只接受文本消息,也只回复文本消息就好,简简单单,也有一定的实用性。
说做就做,昨天做出了第一个版本,这个版本里我只是在数据库中随便添加了几条数据,只有一个表一个字段,完全的LIKE匹配,这样的架构数据量大了之后性能一定歇菜,但是先不管了,于是想先做个爬虫到百度或者虾米上面去爬歌词,在google的时候发现虾米居然有api可以调用,这下爬起来简单多了,今天早上来到公司突然想起来虾米也有搜索功能,于是手动去搜了一下,url非常规范,返回的结果结构也很规范,于是决定修改架构,不自己保存数据了,直接去虾米上搜索后获得歌曲id,再到api去获取歌词的url路径,转而获取歌词内容,这也是第二个版本。
具体流程
  1. 用户通过微信向公众帐号发送文本消息
  2. 微信服务器向自己填写的api路径POST一个xml信息
  3. 解析获取关键词后到虾米搜索
  4. 抓取搜索结果获取歌曲id
  5. 到虾米api去获取歌曲详细信息
  6. 通过歌词url获取歌词内容
  7. 包装成xml信息返回给微信服务器
  8. 微信发送给用户结果
具体代码
1. 通过微信验证  微信只有在验证时是使用GET方式访问api,其余只有POST,"Hello, it works!"是为了测试方便加上去的
public void ProcessRequest (HttpContext context) {
    switch (context.Request.HttpMethod)
    {
        case "GET":
            context.Response.ContentType = "text/plain";
            context.Response.Write(context.Request["echostr"] == null ? "Hello, it works!" : context.Request["echostr"]);
            break;
        case "POST":
            WeiXinServlet.DoPost(context);
            break;
        default:
            break;
    }
}
2. 定义消息类  由于目前只有文本消息,所以我只定义了消息基类和文本消息类,回复的文本消息类还加了一个toXML()方法,用于返回给微信服务器
public class BaseMessage
{
    public string ToUserName { get; set; }
    public string FromUserName { get; set; }
    public string CreateTime { get; set; }
    public string MsgType { get; set; }
}

public class ContentMessage : BaseMessage
{
    public string Content { get; set; }
}

public class ReturnContentMessage : ContentMessage
{
    public virtual string toXML()
    {
        return XmlHelper.ObjectToXml(this);
    }
}
3. toXML()方法  .NET提供了直接从类序列化为xml的方法,很方便
public static string ObjectToXml(object obj)
{
    XmlSerializer serializer = new XmlSerializer(obj.GetType());
    using(MemoryStream stream = new MemoryStream())
    {
        XmlTextWriter xmlTW = new XmlTextWriter(stream, new UTF8Encoding(false));
        xmlTW.Formatting = Formatting.Indented;
        serializer.Serialize(xmlTW, obj);
        xmlTW.Flush();
        xmlTW.Close();
        UTF8Encoding uTF8 = new UTF8Encoding(false, true);

        //直接序列化底层node name会以类名命名,转换为xml
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(uTF8.GetString(stream.ToArray()));
        XmlDocument docNew = new XmlDocument();
        XmlElement newRoot = docNew.CreateElement("xml");
        docNew.AppendChild(newRoot);
        newRoot.InnerXml = doc.DocumentElement.InnerXml;
        return docNew.OuterXml;
    }
}
4. doPOST()方法  微信这点不太方便,所有访问你的api的都扔到这一个地址来,你只能自己做判断然后分发处理,同样我只做了文本消息(text)的处理。
public static void DoPost(HttpContext context)
    {
        StreamReader reader = new StreamReader(context.Request.InputStream, Encoding.UTF8);
        string postStr =  reader.ReadToEnd();
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(postStr);
        switch(doc.SelectSingleNode("/xml/MsgType").InnerText)
        {
            case "text":
                ContentMessage content = new ContentMessage
                {
                    ToUserName = doc.SelectSingleNode("/xml/ToUserName").InnerText,
                    FromUserName = doc.SelectSingleNode("/xml/FromUserName").InnerText,
                    CreateTime = doc.SelectSingleNode("/xml/CreateTime").InnerText,
                    MsgType = "text",
                    Content = doc.SelectSingleNode("/xml/Content").InnerText
                };
                string lyric = Lyrics.ChaGeCi(content.Content);

                ReturnContentMessage returnContent = new ReturnContentMessage
                {
                    ToUserName = content.FromUserName,
                    FromUserName = content.ToUserName,
                    CreateTime = DateTime.Now.ToString(),
                    MsgType = "text",
                    Content = lyric
                };
                string result = XmlHelper.ObjectToXml(returnContent);
                context.Response.ContentType = "text/xml";
                context.Response.Write(result);
                break;
        }
    }
5. 具体歌词逻辑获取  目前只取搜索结果的第一条数据,这样最简洁快速。另外引用了外部抓取库HtmlAgilityPack,顺便吐槽一下,这个库的网站上真的是什么文档都没有啊,要想弄懂得去读源码…,在筛选结果时还用到了一点正则表达式
public static string ChaGeCi(string twoChar)
    {
        string lyric = "";
        string keyword = twoChar.Replace(" ", "+");

        //1.搜索,获取第1条结果的
        HtmlWeb web1 = new HtmlWeb();
        HtmlDocument doc1 = web1.Load("http://www.xiami.com/search/song-lyric/?key=" + keyword);

        //取第1条结果
        HtmlNode node = doc1.DocumentNode.SelectSingleNode("//body//div//div//div//div//div//div//div//div//ul//li[1]//table//tbody//tr//td//a[@href]");
        if(node != null)
        {
            string sid = node.Attributes["href"].Value;
            sid = Regex.Replace(sid, @"[^\d]*", "");

            //2.获取歌曲信息,HtmlAgilityPack库无法读取网络的xml文件
            Uri uri2 = new Uri("http://www.xiami.com/song/playlist/id/" + sid);
            HttpWebRequest req2 = HttpWebRequest.Create(uri2) as HttpWebRequest;
            string songPage = "";
            using(HttpWebResponse res2 = req2.GetResponse() as HttpWebResponse)
            {
                StreamReader reader2 = new StreamReader(res2.GetResponseStream());
                songPage = reader2.ReadToEnd();
            }

            if(songPage.Length > 1)
            {
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(songPage);
                //歌名和演唱者,放在文本消息结尾方便判断结果对错
                string title = xml.GetElementsByTagName("title")[0].InnerText;
                string artist = xml.GetElementsByTagName("artist")[0].InnerText;
                string lyric_url = xml.GetElementsByTagName("lyric")[0].InnerText;

                //3.根据lyric_url下载歌词
                HttpWebRequest req3 = HttpWebRequest.Create(new Uri(lyric_url)) as HttpWebRequest;
                using(HttpWebResponse res3 = req3.GetResponse() as HttpWebResponse)
                {
                    StreamReader reader3 = new StreamReader(res3.GetResponseStream());
                    lyric = reader3.ReadToEnd();
                    lyric = Regex.Replace(lyric, @"\[.*?\]", "");
                    lyric += "\n" + title + " - " + artist;
                }
            }

        }
        return lyric == "" ? "抱歉,没有找到相关歌词~" : lyric;
    }

总结
  • 搜索可能要加上网易云音乐的接口
  • 如果有可能的话,后续还可以加上语音的搜索

2014年7月22日星期二

初试jquery


同事送我一本jquery的书,之前一直想学javascript可是看了半天完全无感,正好借着这个机会学学jquery也是不错的,周末在家啃了20多页,其实只是看了看选择器和部分包装集内容,转过天来上班之前做的页面有bug,心想正好拿来练练jquery吧,于是硬逼着自己上阵,在垂头丧气沉睡了半个小时后,突然开窍完成了页面功能,并做了改进。

页面是一个评价页面,功能包括加载数据,并提交表单,加载数据部分没什么问题,主要是交互部分,需要做到字数检查提示,按钮效果,星星打分效果与取值。处理逻辑是打开页面后,在textarea中输入文字,点击提交按钮时检查字数,如果不满足条件则提示,并将按钮变灰。

之后我做了修改,加载完页面按钮是灰色,在输入文字时keyup事件中检测字数,如果满足条件则按钮变亮,可以点击提交,叫来测试同事问这种交互方式可不可以,回答挺好。

<script type="text/javascript">

$("#CommentContent").keyup(function () {
    var commentContent = $("#CommentContent");
    if (commentContent.val().length >= 5 && commentContent.val().length <= 200) {
        $("div.Btn input").removeClass("btn3").addClass("btn9").removeAttr("disabled");
        $("span.rem-txt").text("");
    }
    else {
        $("span.rem-txt").text(commentContent.attr("data-val-length"));
    }
})

$("li>div.eva-star>span.star").click(function () {
    var nexts = $(this).nextAll().removeClass("star-y");
    var prevs = $(this).addClass("star-y").prevAll().addClass("star-y");
    var len = $(this).prevAll("span").length + 1;
    $("input#Score").val(len);
});

</script>

总结

  • jquery的选择器太强大,以至于有时候不知道用哪种方式好
  • 有点开窍了,还需要加强学习
  • this没用成功,等提高了再找原因吧
  • 今天才知道原来后端是敏感词,男人要做就做前端,出发

2014年7月17日星期四

中国式融资


家里小区的电梯里挂上了同程网的广告,说实话我是个不容易被广告吸引的人,但这个广告做的的确很吸引人眼球,硕大的1元购让人无法抗拒,之后我在手机上装了同程的客户端,操作了几下我就不想用了,真心的。

  1. app启动时的引导页卡出翔
  2. 点击1元购进入的居然是网页,而且在此卡出翔
  3. 还没看到具体1元购的内容就要求用户登录
  4. 买火车票选择站点时点击没有任何反色效果

用到这我已经对这个app没有任何兴趣了,用户体验达到了令人发指的傻逼化。此番如此大规模的铺广告,估计是看携程那边并购的正火,去哪也成功傍上了干爹,自己再不造点声势早晚得死,还是学学阿里博点眼球,没准金主顺声而来成功融资,估值翻个几番再找下家。

老董昨天说了几家不知名的app,说是几轮融资下来估值1亿、10亿美元的,看得出他又想往app方向上靠了。其实目前国内的app市场就像上世纪末的互联网,随便投点钱然后找科技网站写点长篇体验软文就可以在投的钱后面加个0直接卖给下家,paypul那么牛逼的一个团队,做出来的超10亿美元市值的公司也不过就3家,在美国已经算是个非常了不起的成就了,你在中国这么一个人人不皆知的小app就敢叫价10亿,还是美金,无论从技术还是创新还是改变人们生活方式上都排不上号,真腆着脸这么自夸。

而目前国内的所谓天使投资人,绝大部分是不靠谱的,具体内容请参见这篇《我所知道的黑暗创投圈》,最近听说黄教主伙同李冰冰任泉也搞了个什么star投资公司,专门投资高科技,大哥大姐们,你们放IT圈吧,不要把IT人也娱乐化了,你们最近没什么博眼球的就来这个,专门做倒爷卖这种你们都不知道是什么东西的app好意思吗。


2014年7月1日星期二

C#调用json.net执行json的相关操作


微信的access_token每次获取有效期为7200秒,也就是两个小时,在这两个小时内,再重复获取access_token不仅浪费带宽和速度,也增加了出错的几率,而且由于微信对于每日获取次数做了限制,所以每次操作都要获取一遍肯定不是一个好的办法,于是需要我们将获取到的access_token保存在本地,有效期内使用我们本地的数据。

由于我的场景是多用户,所以将AppId、Secret保存下来定时获取的方式肯定不合适。而由于没有太频繁的调用,所以我暂时选择使用json文件将用户的id、access_token、获取时间保存在本地json文件内,正好数据上也是键值对的组合,如果调用频繁那么可以试试键值对的数据库,比如redis之类的。

流程图:

【待加】

操作json我比较习惯使用json.net的库,即Newtonsoft.Json,引用之后便可以使用了。而json.net的网站文档还是非常简单不健全的,我搜了很多中文的个人博客,总觉得很混淆,概念理不清,直到我打开了stackoverflow上这个问题的页面,才终于理清了思路,之后再看官方文档就简单多了。

A JToken is a generic representation of a JSON value of any kind. It could be a string, object, array, property, etc.
A JProperty is a single JToken value paired with a name. It can only be added to a JObject, and its value cannot be another JProperty.
A JObject is a collection of JProperties. It cannot hold any other kind of JToken directly.

好了,以下是具体代码:

  • 没有文件,创建

  1. 函数System.AppDomain.CurrentDomain.BaseDirectory曾经突然抽了,得到的路径没有反斜杠,过了个周末又莫名其妙的好了,具体原因不明;
  2. File.Create(mem_access_token)后面一定要跟Close(),否则文件没有释放,其他进程无法操作
  3. 初始化Jobject对象不能用字符串"{}"转换,而是直接JObject AllAccToken = new JObject();
string access_token = "";
JObject AllAccToken = new JObject();

string mem_access_token = System.AppDomain.CurrentDomain.BaseDirectory + @"\Resource\js\MemAccessToken.json";
if(!File.Exists(mem_access_token))
{
    File.Create(mem_access_token).Close();
    using(StreamWriter sw = new StreamWriter(mem_access_token, false))
    {
        sw.Write(AllAccToken);
    }
}
  • 读取文件

using (FileStream fs = new FileStream(mem_access_token, FileMode.Open))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        all_access_token = sr.ReadToEnd();
    }
}
  • 查询key

    如果存在,则查询对应的token是否已经过期,如果没过期则取值,否则调用微信接口获取并修改保存文件;如果不存在直接调用微信接口获取并添加保存文件
    调用的方法getAccessTokenFromWeiXin如果有access_token则返回access_token,否则返回errcode,因为errcode是5位,所以判断时长度写的6
if(AllAccToken[key] != null)
    {
        DateTime token_time = DateTime.Parse((string)AllAccToken.SelectToken(key).SelectToken("token_time"));
        //token未过期,读取
        if (token_time.AddSeconds(7200) > DateTime.Now)
        {
            return (string)AllAccToken.SelectToken(key).SelectToken("access_token");
        }
        //token已过期,重新获取并保存
        else
        {
            access_token = getAccessTokenFromWeiXin(Data);
            if ((access_token + "").Length > 6)
            {
                AllAccToken[key]["access_token"] = access_token;
                AllAccToken[key]["token_time"] = DateTime.Now;
                using (StreamWriter sw = new StreamWriter(mem_access_token, false))
                {
                    sw.Write(AllAccToken.ToString());
                }
            }
        }
    }
    //文件中没有token,获取并插入文件
else
    {
        access_token = getAccessTokenFromWeiXin(Data);
        if((access_token + "").Length > 6)
        {
            string new_access_token = @"{""access_token"":""" + access_token + @""",""token_time"":""" + DateTime.Now.ToString() + @"""}";
            AllAccToken.Add(memberPKId.ToString(), JToken.Parse(new_access_token));
            using (StreamWriter sw = new StreamWriter(mem_access_token, false))
            {
                sw.Write(AllAccToken.ToString());
            }
        }
    }

另外两个方法代码

调用微信接口获取access_token

public static string getAccessTokenFromWeiXin(ShpWeiXinShopAppData Data)
{
    Uri uri = new Uri(String.Format(@"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", Data.WxAppId_NULL, Data.WxSecret_NULL));
    HttpWebRequest wxReq = HttpWebRequest.Create(uri) as HttpWebRequest;
    string wxJson = "";
    using (HttpWebResponse wxTokenRes = wxReq.GetResponse() as HttpWebResponse)
    {
        StreamReader reader = new StreamReader(wxTokenRes.GetResponseStream());
        wxJson = reader.ReadToEnd();
    }
    JObject jo = JObject.Parse(wxJson);
    string access_token = jo["access_token"] == null ? (string)jo["errcode"] : (string)jo["access_token"];
    return access_token;
}

根据微信错误号获取微信错误信息

微信的所有返回码我也保存成了json文件,方便用户提示,而且这个文件可以做成只读的。

public static string WeiXinError(string key)
{
    using (FileStream fs = new FileStream(System.AppDomain.CurrentDomain.BaseDirectory + @"\Resource\XML\WeiXinErrcode.json", FileMode.Open))
    {
        using(StreamReader sr = new StreamReader(fs))
        {
            string error = sr.ReadToEnd();
            JObject wxErr = JObject.Parse(error);
            return wxErr[key].ToString();
        }
    }
}

小结

经过这次改进,程序优化不少,而且明确了改进方向;
json操作方法很多,找到自己习惯的就好,而这种键值对的方式其实还是很有用的,如果操作频繁的话可以考虑读入内存中操作、数据库中操作等方式。

%23%20C%23%u8C03%u7528json.net%u6267%u884Cjson%u7684%u76F8%u5173%u64CD%u4F5C%0A%0A@%28My%20Blogger%29%5BC%23%7Cjson%7Cjson.net%7C%u5FAE%u4FE1%7C%u6587%u4EF6%u64CD%u4F5C%5D%0A%0A%20%20%20%20%u5FAE%u4FE1%u7684access_token%u6BCF%u6B21%u83B7%u53D6%u6709%u6548%u671F%u4E3A7200%u79D2%uFF0C%u4E5F%u5C31%u662F%u4E24%u4E2A%u5C0F%u65F6%uFF0C%u5728%u8FD9%u4E24%u4E2A%u5C0F%u65F6%u5185%uFF0C%u518D%u91CD%u590D%u83B7%u53D6access_token%u4E0D%u4EC5%u6D6A%u8D39%u5E26%u5BBD%u548C%u901F%u5EA6%uFF0C%u4E5F%u589E%u52A0%u4E86%u51FA%u9519%u7684%u51E0%u7387%uFF0C%u800C%u4E14%u7531%u4E8E%u5FAE%u4FE1%u5BF9%u4E8E%u6BCF%u65E5%u83B7%u53D6%u6B21%u6570%u505A%u4E86%u9650%u5236%uFF0C%u6240%u4EE5%u6BCF%u6B21%u64CD%u4F5C%u90FD%u8981%u83B7%u53D6%u4E00%u904D%u80AF%u5B9A%u4E0D%u662F%u4E00%u4E2A%u597D%u7684%u529E%u6CD5%uFF0C%u4E8E%u662F%u9700%u8981%u6211%u4EEC%u5C06%u83B7%u53D6%u5230%u7684access_token%u4FDD%u5B58%u5728%u672C%u5730%uFF0C%u6709%u6548%u671F%u5185%u4F7F%u7528%u6211%u4EEC%u672C%u5730%u7684%u6570%u636E%u3002%0A---------------------%0A%0A%u7531%u4E8E%u6211%u7684%u573A%u666F%u662F%u591A%u7528%u6237%uFF0C%u6240%u4EE5%u5C06AppId%u3001Secret%u4FDD%u5B58%u4E0B%u6765%u5B9A%u65F6%u83B7%u53D6%u7684%u65B9%u5F0F%u80AF%u5B9A%u4E0D%u5408%u9002%u3002%u800C%u7531%u4E8E%u6CA1%u6709%u592A%u9891%u7E41%u7684%u8C03%u7528%uFF0C%u6240%u4EE5%u6211%u6682%u65F6%u9009%u62E9%u4F7F%u7528json%u6587%u4EF6%u5C06%u7528%u6237%u7684id%u3001access_token%u3001%u83B7%u53D6%u65F6%u95F4%u4FDD%u5B58%u5728%u672C%u5730json%u6587%u4EF6%u5185%uFF0C%u6B63%u597D%u6570%u636E%u4E0A%u4E5F%u662F%u952E%u503C%u5BF9%u7684%u7EC4%u5408%uFF0C%u5982%u679C%u8C03%u7528%u9891%u7E41%u90A3%u4E48%u53EF%u4EE5%u8BD5%u8BD5%u952E%u503C%u5BF9%u7684%u6570%u636E%u5E93%uFF0C%u6BD4%u5982redis%u4E4B%u7C7B%u7684%u3002%0A%23%23%23%23%u6D41%u7A0B%u56FE%uFF1A%0A%u3010%u5F85%u52A0%u3011%0A%0A%u64CD%u4F5Cjson%u6211%u6BD4%u8F83%u4E60%u60EF%u4F7F%u7528json.net%u7684%u5E93%uFF0C%u5373Newtonsoft.Json%uFF0C%u5F15%u7528%u4E4B%u540E%u4FBF%u53EF%u4EE5%u4F7F%u7528%u4E86%u3002%u800Cjson.net%u7684%u7F51%u7AD9%u6587%u6863%u8FD8%u662F%u975E%u5E38%u7B80%u5355%u4E0D%u5065%u5168%u7684%uFF0C%u6211%u641C%u4E86%u5F88%u591A%u4E2D%u6587%u7684%u4E2A%u4EBA%u535A%u5BA2%uFF0C%u603B%u89C9%u5F97%u5F88%u6DF7%u6DC6%uFF0C%u6982%u5FF5%u7406%u4E0D%u6E05%uFF0C%u76F4%u5230%u6211%u6253%u5F00%u4E86stackoverflow%u4E0A%5B%u8FD9%u4E2A%u95EE%u9898%u7684%u9875%u9762%5D%28http%3A//stackoverflow.com/questions/15413825/how-do-you-add-a-jtoken-to-an-jobject%29%uFF0C%u624D%u7EC8%u4E8E%u7406%u6E05%u4E86%u601D%u8DEF%uFF0C%u4E4B%u540E%u518D%u770B%u5B98%u65B9%u6587%u6863%u5C31%u7B80%u5355%u591A%u4E86%u3002%0A%0A%3EA%20JToken%20is%20a%20generic%20representation%20of%20a%20JSON%20value%20of%20any%20kind.%20It%20could%20be%20a%20string%2C%20object%2C%20array%2C%20property%2C%20etc.%0AA%20JProperty%20is%20a%20single%20JToken%20value%20paired%20with%20a%20name.%20It%20can%20only%20be%20added%20to%20a%20JObject%2C%20and%20its%20value%20cannot%20be%20another%20JProperty.%0AA%20JObject%20is%20a%20collection%20of%20JProperties.%20It%20cannot%20hold%20any%20other%20kind%20of%20JToken%20directly.%0A%0A%u597D%u4E86%uFF0C%u4EE5%u4E0B%u662F%u5177%u4F53%u4EE3%u7801%uFF1A%0A-%20%23%23%23%23%u6CA1%u6709%u6587%u4EF6%uFF0C%u521B%u5EFA%0A1.%20%u51FD%u6570%60System.AppDomain.CurrentDomain.BaseDirectory%60%u66FE%u7ECF%u7A81%u7136%u62BD%u4E86%uFF0C%u5F97%u5230%u7684%u8DEF%u5F84%u6CA1%u6709%u53CD%u659C%u6760%uFF0C%u8FC7%u4E86%u4E2A%u5468%u672B%u53C8%u83AB%u540D%u5176%u5999%u7684%u597D%u4E86%uFF0C%u5177%u4F53%u539F%u56E0%u4E0D%u660E%uFF1B%0A2.%20%60File.Create%28mem_access_token%29%60%u540E%u9762%u4E00%u5B9A%u8981%u8DDF%60Close%28%29%60%2C%u5426%u5219%u6587%u4EF6%u6CA1%u6709%u91CA%u653E%uFF0C%u5176%u4ED6%u8FDB%u7A0B%u65E0%u6CD5%u64CD%u4F5C%0A3.%20%u521D%u59CB%u5316Jobject%u5BF9%u8C61%u4E0D%u80FD%u7528%u5B57%u7B26%u4E32%22%7B%7D%22%u8F6C%u6362%uFF0C%u800C%u662F%u76F4%u63A5%60JObject%20AllAccToken%20%3D%20new%20JObject%28%29%3B%60%0A%0A%60%60%60%20C%23%0Astring%20access_token%20%3D%20%22%22%3B%0AJObject%20AllAccToken%20%3D%20new%20JObject%28%29%3B%0A%0Astring%20mem_access_token%20%3D%20System.AppDomain.CurrentDomain.BaseDirectory%20+%20@%22%5CResource%5Cjs%5CMemAccessToken.json%22%3B%0Aif%28%21File.Exists%28mem_access_token%29%29%0A%7B%0A%20%20%20%20File.Create%28mem_access_token%29.Close%28%29%3B%0A%20%20%20%20using%28StreamWriter%20sw%20%3D%20new%20StreamWriter%28mem_access_token%2C%20false%29%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20sw.Write%28AllAccToken%29%3B%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0A-%20%23%23%23%23%u8BFB%u53D6%u6587%u4EF6%0A%0A%60%60%60%20C%23%0Ausing%20%28FileStream%20fs%20%3D%20new%20FileStream%28mem_access_token%2C%20FileMode.Open%29%29%0A%7B%0A%20%20%20%20using%20%28StreamReader%20sr%20%3D%20new%20StreamReader%28fs%29%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20all_access_token%20%3D%20sr.ReadToEnd%28%29%3B%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0A-%20%23%23%23%23%u67E5%u8BE2key%0A%u5982%u679C%u5B58%u5728%uFF0C%u5219%u67E5%u8BE2%u5BF9%u5E94%u7684token%u662F%u5426%u5DF2%u7ECF%u8FC7%u671F%uFF0C%u5982%u679C%u6CA1%u8FC7%u671F%u5219%u53D6%u503C%uFF0C%u5426%u5219%u8C03%u7528%u5FAE%u4FE1%u63A5%u53E3%u83B7%u53D6%u5E76%u4FEE%u6539%u4FDD%u5B58%u6587%u4EF6%uFF1B%u5982%u679C%u4E0D%u5B58%u5728%u76F4%u63A5%u8C03%u7528%u5FAE%u4FE1%u63A5%u53E3%u83B7%u53D6%u5E76%u6DFB%u52A0%u4FDD%u5B58%u6587%u4EF6%0A%u8C03%u7528%u7684%u65B9%u6CD5%60getAccessTokenFromWeiXin%60%u5982%u679C%u6709access_token%u5219%u8FD4%u56DEaccess_token%uFF0C%u5426%u5219%u8FD4%u56DEerrcode%uFF0C%u56E0%u4E3Aerrcode%u662F5%u4F4D%uFF0C%u6240%u4EE5%u5224%u65AD%u65F6%u957F%u5EA6%u5199%u76846%0A%0A%60%60%60%20C%23%0Aif%28AllAccToken%5Bkey%5D%20%21%3D%20null%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20DateTime%20token_time%20%3D%20DateTime.Parse%28%28string%29AllAccToken.SelectToken%28key%29.SelectToken%28%22token_time%22%29%29%3B%0A%20%20%20%20%20%20%20%20//token%u672A%u8FC7%u671F%uFF0C%u8BFB%u53D6%0A%20%20%20%20%20%20%20%20if%20%28token_time.AddSeconds%287200%29%20%3E%20DateTime.Now%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%28string%29AllAccToken.SelectToken%28key%29.SelectToken%28%22access_token%22%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20//token%u5DF2%u8FC7%u671F%uFF0C%u91CD%u65B0%u83B7%u53D6%u5E76%u4FDD%u5B58%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20access_token%20%3D%20getAccessTokenFromWeiXin%28Data%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%28access_token%20+%20%22%22%29.Length%20%3E%206%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AllAccToken%5Bkey%5D%5B%22access_token%22%5D%20%3D%20access_token%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AllAccToken%5Bkey%5D%5B%22token_time%22%5D%20%3D%20DateTime.Now%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20using%20%28StreamWriter%20sw%20%3D%20new%20StreamWriter%28mem_access_token%2C%20false%29%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sw.Write%28AllAccToken.ToString%28%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20//%u6587%u4EF6%u4E2D%u6CA1%u6709token%uFF0C%u83B7%u53D6%u5E76%u63D2%u5165%u6587%u4EF6%0Aelse%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20access_token%20%3D%20getAccessTokenFromWeiXin%28Data%29%3B%0A%20%20%20%20%20%20%20%20if%28%28access_token%20+%20%22%22%29.Length%20%3E%206%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20string%20new_access_token%20%3D%20@%22%7B%22%22access_token%22%22%3A%22%22%22%20+%20access_token%20+%20@%22%22%22%2C%22%22token_time%22%22%3A%22%22%22%20+%20DateTime.Now.ToString%28%29%20+%20@%22%22%22%7D%22%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20AllAccToken.Add%28memberPKId.ToString%28%29%2C%20JToken.Parse%28new_access_token%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20using%20%28StreamWriter%20sw%20%3D%20new%20StreamWriter%28mem_access_token%2C%20false%29%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sw.Write%28AllAccToken.ToString%28%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%60%60%60%0A%0A%u53E6%u5916%u4E24%u4E2A%u65B9%u6CD5%u4EE3%u7801%0A%23%23%23%23%u8C03%u7528%u5FAE%u4FE1%u63A5%u53E3%u83B7%u53D6access_token%0A%60%60%60%20C%23%0Apublic%20static%20string%20getAccessTokenFromWeiXin%28ShpWeiXinShopAppData%20Data%29%0A%7B%0A%20%20%20%20Uri%20uri%20%3D%20new%20Uri%28String.Format%28@%22https%3A//api.weixin.qq.com/cgi-bin/token%3Fgrant_type%3Dclient_credential%26appid%3D%7B0%7D%26secret%3D%7B1%7D%22%2C%20Data.WxAppId_NULL%2C%20Data.WxSecret_NULL%29%29%3B%0A%20%20%20%20HttpWebRequest%20wxReq%20%3D%20HttpWebRequest.Create%28uri%29%20as%20HttpWebRequest%3B%0A%20%20%20%20string%20wxJson%20%3D%20%22%22%3B%0A%20%20%20%20using%20%28HttpWebResponse%20wxTokenRes%20%3D%20wxReq.GetResponse%28%29%20as%20HttpWebResponse%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20StreamReader%20reader%20%3D%20new%20StreamReader%28wxTokenRes.GetResponseStream%28%29%29%3B%0A%20%20%20%20%20%20%20%20wxJson%20%3D%20reader.ReadToEnd%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20JObject%20jo%20%3D%20JObject.Parse%28wxJson%29%3B%0A%20%20%20%20string%20access_token%20%3D%20jo%5B%22access_token%22%5D%20%3D%3D%20null%20%3F%20%28string%29jo%5B%22errcode%22%5D%20%3A%20%28string%29jo%5B%22access_token%22%5D%3B%0A%20%20%20%20return%20access_token%3B%0A%7D%0A%60%60%60%0A%0A%23%23%23%23%u6839%u636E%u5FAE%u4FE1%u9519%u8BEF%u53F7%u83B7%u53D6%u5FAE%u4FE1%u9519%u8BEF%u4FE1%u606F%0A%u5FAE%u4FE1%u7684%u6240%u6709%u8FD4%u56DE%u7801%u6211%u4E5F%u4FDD%u5B58%u6210%u4E86json%u6587%u4EF6%uFF0C%u65B9%u4FBF%u7528%u6237%u63D0%u793A%uFF0C%u800C%u4E14%u8FD9%u4E2A%u6587%u4EF6%u53EF%u4EE5%u505A%u6210%u53EA%u8BFB%u7684%u3002%0A%60%60%60%20C%23%0Apublic%20static%20string%20WeiXinError%28string%20key%29%0A%7B%0A%20%20%20%20using%20%28FileStream%20fs%20%3D%20new%20FileStream%28System.AppDomain.CurrentDomain.BaseDirectory%20+%20@%22%5CResource%5CXML%5CWeiXinErrcode.json%22%2C%20FileMode.Open%29%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20using%28StreamReader%20sr%20%3D%20new%20StreamReader%28fs%29%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20string%20error%20%3D%20sr.ReadToEnd%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20JObject%20wxErr%20%3D%20JObject.Parse%28error%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20wxErr%5Bkey%5D.ToString%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0A--------------------------%0A%23%23%23%23%u5C0F%u7ED3%0A%u7ECF%u8FC7%u8FD9%u6B21%u6539%u8FDB%uFF0C%u7A0B%u5E8F%u4F18%u5316%u4E0D%u5C11%uFF0C%u800C%u4E14%u660E%u786E%u4E86%u6539%u8FDB%u65B9%u5411%uFF1B%0Ajson%u64CD%u4F5C%u65B9%u6CD5%u5F88%u591A%uFF0C%u627E%u5230%u81EA%u5DF1%u4E60%u60EF%u7684%u5C31%u597D%uFF0C%u800C%u8FD9%u79CD%u952E%u503C%u5BF9%u7684%u65B9%u5F0F%u5176%u5B9E%u8FD8%u662F%u5F88%u6709%u7528%u7684%uFF0C%u5982%u679C%u64CD%u4F5C%u9891%u7E41%u7684%u8BDD%u53EF%u4EE5%u8003%u8651%u8BFB%u5165%u5185%u5B58%u4E2D%u64CD%u4F5C%u3001%u6570%u636E%u5E93%u4E2D%u64CD%u4F5C%u7B49%u65B9%u5F0F%u3002

2014年6月24日星期二

C#微信开发之菜单管理


想进行微信公众平台开发已经有一年半了,之前也想过很多种可行性案例,可迟迟没有行动起来,昨天也看到了小区物业开启了微信服务,也是我思考的案例之一,也是我觉得最有用的一个案例,可苦于没有服务器资源没有进行,最近正好有一次机会开始了微信开发之旅,虽说有点晚,但总算也是涨点姿势。

今天进行的是微信自定义菜单,菜单功能包括以下4点:

进行微信开发需要认证的订阅号服务号,并开启开发模式,具体方法不废话,直接说自定义菜单:

查询

很简单,就不说了。

修改

微信的接口中没有修改,所以要进行删除创建两步操作。

  1. 获取access_token
    微信的每一步操作都是依靠这个token进行的,所以每步操作前都需要获取,有效期默认是7200秒,如果在这段时间内可以保存就不用再次获取
public static JObject access_token(int memberPKId)
{
    ShpWeiXinShopAppData wxData = new ShpWeiXinShopAppSystem().GetData(memberPKId);
    Uri uri = new Uri(String.Format(@"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", wxData.WxAppId_NULL, wxData.WxSecret_NULL));
    HttpWebRequest wxReq = HttpWebRequest.Create(uri) as HttpWebRequest;
    string wxJson = "";
    using (HttpWebResponse wxTokenRes = wxReq.GetResponse() as HttpWebResponse)
    {
        StreamReader reader = new StreamReader(wxTokenRes.GetResponseStream());
        wxJson = reader.ReadToEnd();
    }
    return JObject.Parse(wxJson);
}
  1. 删除菜单
JObject json = WeiXin.access_token(Int32.Parse(memberPKId));
if (json["access_token"] != null)
{
    string access_token = json["access_token"].ToString();
    Uri uri = new Uri(String.Format(@"https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", access_token));
    HttpWebRequest wxMenuReq = HttpWebRequest.Create(uri) as HttpWebRequest;
    using (HttpWebResponse wxDelMenuRes = wxMenuReq.GetResponse() as HttpWebResponse)
    {
        StreamReader reader = new StreamReader(wxDelMenuRes.GetResponseStream());
        menuDel = reader.ReadToEnd();
    }
    json = JObject.Parse(menuDel);
  1. 创建菜单
    创建时不仅需要传送参数,还需要传送一个json对象过去,先拼接成字符串再post,顺便吐槽一下,C#的post方法还没有jquery的post方便,怪不得那么多前端牛逼了
if(json["errcode"].ToString() == "0")
{
    DataTable dt = new ShpWeixinShopMenuSystem().GetShpWeixinShopMenuListByMemberPKId(Int32.Parse(memberPKId));
    DataRow[] firstMenu = dt.Select("MenuLevel = 1", "DisplayOrder");
    string menu = "{\"button\":[";

    foreach(DataRow dr1 in firstMenu)
    {
        menu += "{\"name\":\"" + dr1["MenuCaption"].ToString() + "\"";
        string expression = String.Format("MenuLevel = 2 and ParentMenu = {0}", dr1["MenuItemPKId"].ToString());
        DataRow[] secondMenu = dt.Select(expression, "DisplayOrder");
        if(secondMenu.Length > 0)
        {
            menu += ",\"sub_button\":[";
            foreach(DataRow dr2 in secondMenu)
            {
                menu += "{\"type\":\"view\",\"name\":\"" + dr2["MenuCaption"].ToString() + "\",\"url\":\"" + dr2["RelUrl"].ToString() + "\"},";
            }
            menu = menu.Remove(menu.Length-1, 1);
            menu += "]},";
        }
    }
    menu += "]}";

    HttpWebRequest wxMenuRequest = (HttpWebRequest)WebRequest.Create("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + access_token);
    wxMenuRequest.ContentType = "text/json";
    wxMenuRequest.Method = "POST";

    using(StreamWriter sw = new StreamWriter(wxMenuRequest.GetRequestStream()))
    {
        sw.Write(menu);
        sw.Flush();
        sw.Close();
        HttpWebResponse wxMenuResponse = (HttpWebResponse)wxMenuRequest.GetResponse();
        using(StreamReader sr = new StreamReader(wxMenuResponse.GetResponseStream()))
        {
            menuCre = sr.ReadToEnd();
        }
    }
}

菜单事件推送

就是点击菜单按钮事件的通知。


写在最后

开发微信公众平台貌似不难,RESTapi调试起来也异常的方便,而且微信中没有太多的界面、用户体验之类的东西,所以整体来说开发速度比较快,难度不高,而真正需要的是想法与运营。

%23%20C%23%u5FAE%u4FE1%u5F00%u53D1%u4E4B%u83DC%u5355%u7BA1%u7406%0A%0A@%28My%20Blogger%29%5BC%23%7C.NET%7C%u5F00%u53D1%7C%u5FAE%u4FE1%7C%u5FAE%u4FE1%u516C%u4F17%u5E73%u53F0%5D%0A%0A%u60F3%u8FDB%u884C%u5FAE%u4FE1%u516C%u4F17%u5E73%u53F0%u5F00%u53D1%u5DF2%u7ECF%u6709%u4E00%u5E74%u534A%u4E86%uFF0C%u4E4B%u524D%u4E5F%u60F3%u8FC7%u5F88%u591A%u79CD%u53EF%u884C%u6027%u6848%u4F8B%uFF0C%u53EF%u8FDF%u8FDF%u6CA1%u6709%u884C%u52A8%u8D77%u6765%uFF0C%u6628%u5929%u4E5F%u770B%u5230%u4E86%u5C0F%u533A%u7269%u4E1A%u5F00%u542F%u4E86%u5FAE%u4FE1%u670D%u52A1%uFF0C%u4E5F%u662F%u6211%u601D%u8003%u7684%u6848%u4F8B%u4E4B%u4E00%uFF0C%u4E5F%u662F%u6211%u89C9%u5F97%u6700%u6709%u7528%u7684%u4E00%u4E2A%u6848%u4F8B%uFF0C%u53EF%u82E6%u4E8E%u6CA1%u6709%u670D%u52A1%u5668%u8D44%u6E90%u6CA1%u6709%u8FDB%u884C%uFF0C%u6700%u8FD1%u6B63%u597D%u6709%u4E00%u6B21%u673A%u4F1A%u5F00%u59CB%u4E86%u5FAE%u4FE1%u5F00%u53D1%u4E4B%u65C5%uFF0C%u867D%u8BF4%u6709%u70B9%u665A%uFF0C%u4F46%u603B%u7B97%u4E5F%u662F%u6DA8%u70B9%u59FF%u52BF%u3002%0A%0A%u4ECA%u5929%u8FDB%u884C%u7684%u662F%u5FAE%u4FE1%u81EA%u5B9A%u4E49%u83DC%u5355%uFF0C%u83DC%u5355%u529F%u80FD%u5305%u62EC%u4EE5%u4E0B4%u70B9%uFF1A%0A-%20**%5B%u521B%u5EFA%u83DC%u5355%5D%28http%3A//mp.weixin.qq.com/wiki/index.php%3Ftitle%3D%25E8%2587%25AA%25E5%25AE%259A%25E4%25B9%2589%25E8%258F%259C%25E5%258D%2595%25E5%2588%259B%25E5%25BB%25BA%25E6%258E%25A5%25E5%258F%25A3%29**%0A-%20**%5B%u67E5%u8BE2%u83DC%u5355%5D%28http%3A//mp.weixin.qq.com/wiki/index.php%3Ftitle%3D%25E8%2587%25AA%25E5%25AE%259A%25E4%25B9%2589%25E8%258F%259C%25E5%258D%2595%25E6%259F%25A5%25E8%25AF%25A2%25E6%258E%25A5%25E5%258F%25A3%29**%0A-%20**%5B%u5220%u9664%u83DC%u5355%5D%28http%3A//mp.weixin.qq.com/wiki/index.php%3Ftitle%3D%25E8%2587%25AA%25E5%25AE%259A%25E4%25B9%2589%25E8%258F%259C%25E5%258D%2595%25E5%2588%25A0%25E9%2599%25A4%25E6%258E%25A5%25E5%258F%25A3%29**%0A-%20**%5B%u83DC%u5355%u4E8B%u4EF6%u63A8%u9001%5D%28http%3A//mp.weixin.qq.com/wiki/index.php%3Ftitle%3D%25E8%2587%25AA%25E5%25AE%259A%25E4%25B9%2589%25E8%258F%259C%25E5%258D%2595%25E4%25BA%258B%25E4%25BB%25B6%25E6%258E%25A8%25E9%2580%2581%29**%0A%0A%u8FDB%u884C%u5FAE%u4FE1%u5F00%u53D1%u9700%u8981**%u8BA4%u8BC1%u7684%u8BA2%u9605%u53F7**%u6216**%u670D%u52A1%u53F7**%uFF0C%u5E76%u5F00%u542F%u5F00%u53D1%u6A21%u5F0F%uFF0C%u5177%u4F53%u65B9%u6CD5%u4E0D%u5E9F%u8BDD%uFF0C%u76F4%u63A5%u8BF4%u81EA%u5B9A%u4E49%u83DC%u5355%uFF1A%0A%0A%23%23%23%23%u67E5%u8BE2%0A%u5F88%u7B80%u5355%uFF0C%u5C31%u4E0D%u8BF4%u4E86%u3002%0A%0A%23%23%23%23%u4FEE%u6539%0A%u5FAE%u4FE1%u7684%u63A5%u53E3%u4E2D%u6CA1%u6709%u4FEE%u6539%uFF0C%u6240%u4EE5%u8981%u8FDB%u884C**%u5220%u9664**%u4E0E**%u521B%u5EFA**%u4E24%u6B65%u64CD%u4F5C%u3002%0A1.%20%u83B7%u53D6access_token%0A%u5FAE%u4FE1%u7684%u6BCF%u4E00%u6B65%u64CD%u4F5C%u90FD%u662F%u4F9D%u9760%u8FD9%u4E2Atoken%u8FDB%u884C%u7684%uFF0C%u6240%u4EE5%u6BCF%u6B65%u64CD%u4F5C%u524D%u90FD%u9700%u8981%u83B7%u53D6%uFF0C%u6709%u6548%u671F%u9ED8%u8BA4%u662F7200%u79D2%uFF0C%u5982%u679C%u5728%u8FD9%u6BB5%u65F6%u95F4%u5185%u53EF%u4EE5%u4FDD%u5B58%u5C31%u4E0D%u7528%u518D%u6B21%u83B7%u53D6%0A%0A%60%60%60%20C%23%0Apublic%20static%20JObject%20access_token%28int%20memberPKId%29%0A%7B%0A%20%20%20%20ShpWeiXinShopAppData%20wxData%20%3D%20new%20ShpWeiXinShopAppSystem%28%29.GetData%28memberPKId%29%3B%0A%20%20%20%20Uri%20uri%20%3D%20new%20Uri%28String.Format%28@%22https%3A//api.weixin.qq.com/cgi-bin/token%3Fgrant_type%3Dclient_credential%26appid%3D%7B0%7D%26secret%3D%7B1%7D%22%2C%20wxData.WxAppId_NULL%2C%20wxData.WxSecret_NULL%29%29%3B%0A%20%20%20%20HttpWebRequest%20wxReq%20%3D%20HttpWebRequest.Create%28uri%29%20as%20HttpWebRequest%3B%0A%20%20%20%20string%20wxJson%20%3D%20%22%22%3B%0A%20%20%20%20using%20%28HttpWebResponse%20wxTokenRes%20%3D%20wxReq.GetResponse%28%29%20as%20HttpWebResponse%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20StreamReader%20reader%20%3D%20new%20StreamReader%28wxTokenRes.GetResponseStream%28%29%29%3B%0A%20%20%20%20%20%20%20%20wxJson%20%3D%20reader.ReadToEnd%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20JObject.Parse%28wxJson%29%3B%0A%7D%0A%60%60%60%0A2.%20%u5220%u9664%u83DC%u5355%0A%0A%60%60%60%20C%23%0AJObject%20json%20%3D%20WeiXin.access_token%28Int32.Parse%28memberPKId%29%29%3B%0Aif%20%28json%5B%22access_token%22%5D%20%21%3D%20null%29%0A%7B%0A%20%20%20%20string%20access_token%20%3D%20json%5B%22access_token%22%5D.ToString%28%29%3B%0A%20%20%20%20Uri%20uri%20%3D%20new%20Uri%28String.Format%28@%22https%3A//api.weixin.qq.com/cgi-bin/menu/delete%3Faccess_token%3D%7B0%7D%22%2C%20access_token%29%29%3B%0A%20%20%20%20HttpWebRequest%20wxMenuReq%20%3D%20HttpWebRequest.Create%28uri%29%20as%20HttpWebRequest%3B%0A%20%20%20%20using%20%28HttpWebResponse%20wxDelMenuRes%20%3D%20wxMenuReq.GetResponse%28%29%20as%20HttpWebResponse%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20StreamReader%20reader%20%3D%20new%20StreamReader%28wxDelMenuRes.GetResponseStream%28%29%29%3B%0A%20%20%20%20%20%20%20%20menuDel%20%3D%20reader.ReadToEnd%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20json%20%3D%20JObject.Parse%28menuDel%29%3B%0A%60%60%60%0A3.%20%u521B%u5EFA%u83DC%u5355%0A%u521B%u5EFA%u65F6%u4E0D%u4EC5%u9700%u8981%u4F20%u9001%u53C2%u6570%uFF0C%u8FD8%u9700%u8981%u4F20%u9001%u4E00%u4E2Ajson%u5BF9%u8C61%u8FC7%u53BB%uFF0C%u5148%u62FC%u63A5%u6210%u5B57%u7B26%u4E32%u518Dpost%uFF0C%u987A%u4FBF%u5410%u69FD%u4E00%u4E0B%uFF0CC%23%u7684post%u65B9%u6CD5%u8FD8%u6CA1%u6709jquery%u7684post%u65B9%u4FBF%uFF0C%u602A%u4E0D%u5F97%u90A3%u4E48%u591A%u524D%u7AEF%u725B%u903C%u4E86%0A%0A%60%60%60%20C%23%0Aif%28json%5B%22errcode%22%5D.ToString%28%29%20%3D%3D%20%220%22%29%0A%7B%0A%20%20%20%20DataTable%20dt%20%3D%20new%20ShpWeixinShopMenuSystem%28%29.GetShpWeixinShopMenuListByMemberPKId%28Int32.Parse%28memberPKId%29%29%3B%0A%20%20%20%20DataRow%5B%5D%20firstMenu%20%3D%20dt.Select%28%22MenuLevel%20%3D%201%22%2C%20%22DisplayOrder%22%29%3B%0A%20%20%20%20string%20menu%20%3D%20%22%7B%5C%22button%5C%22%3A%5B%22%3B%0A%20%20%20%20%0A%20%20%20%20foreach%28DataRow%20dr1%20in%20firstMenu%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20menu%20+%3D%20%22%7B%5C%22name%5C%22%3A%5C%22%22%20+%20dr1%5B%22MenuCaption%22%5D.ToString%28%29%20+%20%22%5C%22%22%3B%0A%20%20%20%20%20%20%20%20string%20expression%20%3D%20String.Format%28%22MenuLevel%20%3D%202%20and%20ParentMenu%20%3D%20%7B0%7D%22%2C%20dr1%5B%22MenuItemPKId%22%5D.ToString%28%29%29%3B%0A%20%20%20%20%20%20%20%20DataRow%5B%5D%20secondMenu%20%3D%20dt.Select%28expression%2C%20%22DisplayOrder%22%29%3B%0A%20%20%20%20%20%20%20%20if%28secondMenu.Length%20%3E%200%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20menu%20+%3D%20%22%2C%5C%22sub_button%5C%22%3A%5B%22%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20foreach%28DataRow%20dr2%20in%20secondMenu%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20menu%20+%3D%20%22%7B%5C%22type%5C%22%3A%5C%22view%5C%22%2C%5C%22name%5C%22%3A%5C%22%22%20+%20dr2%5B%22MenuCaption%22%5D.ToString%28%29%20+%20%22%5C%22%2C%5C%22url%5C%22%3A%5C%22%22%20+%20dr2%5B%22RelUrl%22%5D.ToString%28%29%20+%20%22%5C%22%7D%2C%22%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20menu%20%3D%20menu.Remove%28menu.Length-1%2C%201%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20menu%20+%3D%20%22%5D%7D%2C%22%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20menu%20+%3D%20%22%5D%7D%22%3B%0A%0A%20%20%20%20HttpWebRequest%20wxMenuRequest%20%3D%20%28HttpWebRequest%29WebRequest.Create%28%22https%3A//api.weixin.qq.com/cgi-bin/menu/create%3Faccess_token%3D%22%20+%20access_token%29%3B%0A%20%20%20%20wxMenuRequest.ContentType%20%3D%20%22text/json%22%3B%0A%20%20%20%20wxMenuRequest.Method%20%3D%20%22POST%22%3B%0A%20%20%20%20%0A%20%20%20%20using%28StreamWriter%20sw%20%3D%20new%20StreamWriter%28wxMenuRequest.GetRequestStream%28%29%29%29%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20sw.Write%28menu%29%3B%0A%20%20%20%20%20%20%20%20sw.Flush%28%29%3B%0A%20%20%20%20%20%20%20%20sw.Close%28%29%3B%0A%20%20%20%20%20%20%20%20HttpWebResponse%20wxMenuResponse%20%3D%20%28HttpWebResponse%29wxMenuRequest.GetResponse%28%29%3B%0A%20%20%20%20%20%20%20%20using%28StreamReader%20sr%20%3D%20new%20StreamReader%28wxMenuResponse.GetResponseStream%28%29%29%29%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20menuCre%20%3D%20sr.ReadToEnd%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%0A%60%60%60%0A%0A%23%23%23%23%20%u83DC%u5355%u4E8B%u4EF6%u63A8%u9001%0A%u8FD9%u4E2A%u63A5%u53E3%u662F%u65B0%u52A0%u7684%uFF0C%u4E4B%u524D%u6CA1%u6709%u770B%u5230%uFF0C%u6682%u65F6%u6CA1%u6709%u7528%u5230%uFF0C%u5DF2%u52A0%u5165%u5F00%u53D1%u8BA1%u5212%uFF0C%u4EE5%u540E%u5199%u597D%u4E86%u518D%u6765%u66F4%u65B0%u3002%0A%0A----------------------------%0A%0A%23%23%23%23%u5199%u5728%u6700%u540E%0A%u5F00%u53D1%u5FAE%u4FE1%u516C%u4F17%u5E73%u53F0%u8C8C%u4F3C%u4E0D%u96BE%uFF0CRESTapi%u8C03%u8BD5%u8D77%u6765%u4E5F%u5F02%u5E38%u7684%u65B9%u4FBF%uFF0C%u800C%u4E14%u5FAE%u4FE1%u4E2D%u6CA1%u6709%u592A%u591A%u7684%u754C%u9762%u3001%u7528%u6237%u4F53%u9A8C%u4E4B%u7C7B%u7684%u4E1C%u897F%uFF0C%u6240%u4EE5%u6574%u4F53%u6765%u8BF4%u5F00%u53D1%u901F%u5EA6%u6BD4%u8F83%u5FEB%uFF0C%u96BE%u5EA6%u4E0D%u9AD8%uFF0C%u800C%u771F%u6B63%u9700%u8981%u7684%u662F%u60F3%u6CD5%u4E0E%u8FD0%u8425%u3002%0A%0A

2014年6月19日星期四

我为什么喜欢亚马逊


曾经有位做淘宝的朋友对我说:你为什么总是推崇亚马逊,我觉得在亚马逊上买东西比较low。我当时被气笑了,这也说明一个事实,任何国际高科技公司进入中国都有很强的壁垒,能做到被众人接受那更是难上加难。很多人都觉得亚马逊只是像京东一样的一家B2C的网站,却不知道其又是京东的抄袭对象,而距离,那只能呵呵了。
初次知道亚马逊还是网上看到有人说这是一家卖百货的网站,后来看到优秀logo设计案例中有亚马逊logo的详细解读,A-Z、笑脸、字体等都包含其中,再后来在同学家里看到了kindle,被精美的外表吸引,再后来看开发者论坛,知道了AWS,再后来亚马逊进入中国,日常购物+海淘没少在上面买东西,直到今天,亚马逊推出了第一款手机。

亚马逊的优势:

  • 贝佐斯:可以说没有贝佐斯就没有今天的亚马逊;
  • AWS:亚马逊云的技术含量甩出IT企业微软和苹果几条街;
  • 贴近生活:亚马逊销售百货,每个人需要;
  • 卓越的仓储物流系统:请注意,我使用的是卓越;
  • 强大的售后系统:如果你打过京东的售后就会由衷的赞叹亚马逊的专业;
  • 丰富的电子书资源:唯苹果可以匹敌;

kindle:

kindle已经经历了很多代产品,而且产品线也足够丰富,目前据说还准备开发一款超薄型kindle,由于对电子书比较喜欢,所以我也在paperwhite入华的第一天买了一部,还是挺喜欢的,只是即使有背光也无法在晚上躺在床上慢慢看,既伤眼睛也影响旁边人休息。
可是kindle已经构建出一个成熟的生态圈,拿现在时髦的话来说就是红海,资源丰富,硬件优秀,款式繁多,携带方便,总有一款适合你,不买也来看一看。网上很多人还是对n年前的kindle3、4情有独钟,足以见得亚马逊在设计上的独到。而电子书不仅大大的节省了空间,也不会丢失,只要使用账号就可以找到买过的所有书籍。

Fire Phone:

今天亚马逊推出了蓄谋已久的手机,名曰Fire Phone。看网上视频貌似做工还不错,不会像三棒子那样全身塑料。亮点功能有几点:
  • 3D技术:前面5个摄像头不是白装的,这也会带来前所未有的用户体验;
  • 无限的云:拍照可以直接存储至AWS,空间无限。在使用iphone时我的感慨就是,常用应用的空间其实16G基本够用,拍照和视频占用的空间特别大,亚马逊这样会逼的其他厂商降低云价格,好事;
  • 拍照扫货Firefly:对于一个购物网站来说,这种功能是必备无疑的了,重要的是信息量和识别准确率,希望国内也能足够好;
  • 与Fire TV的整合:我敢打赌国内那么多出盒子的没考虑到这个问题;
  • 操作系统:看视频有点黑莓的味道,不可否认黑莓真的很酷;
此外亚马逊海量的商品与电子书、电影资源也是其优势,缺点也有,就是裸机价格太高,有多少人愿意在同样价格的iphone6和安卓手机上选择后者呢。

关于抄袭

亚马逊在国内有不少的拥趸,不光是消费者,还有一大票厂商,只能说他们比较识货,现在AWS已经被抄的满天飞了,kindle也已经有太多的山寨货,都已经玩死一轮了,手机嘛,估计阿里、京东会迅速跟上,希望亚马逊且行且珍惜,早日入华早日调价。

亚马逊已经不仅仅是一个网站,而是一家IT界高科技公司,他的技术、用户体验都是业界的佼佼者,综合能力出其右者寥寥无几,而且贝佐斯曾经说过:不会为了董事会盈利而损害用户体验。我表示非常赞同,有这种做事的精神,叫人怎么能不喜欢这家公司。
%23%20%u6211%u4E3A%u4EC0%u4E48%u559C%u6B22%u4E9A%u9A6C%u900A%0A@%28My%20Blogger%29%5B%u4E9A%u9A6C%u900A%7Camazon%5D%0A%0A%u66FE%u7ECF%u6709%u4F4D%u505A%u6DD8%u5B9D%u7684%u670B%u53CB%u5BF9%u6211%u8BF4%uFF1A%u4F60%u4E3A%u4EC0%u4E48%u603B%u662F%u63A8%u5D07%u4E9A%u9A6C%u900A%uFF0C%u6211%u89C9%u5F97%u5728%u4E9A%u9A6C%u900A%u4E0A%u4E70%u4E1C%u897F%u6BD4%u8F83low%u3002%u6211%u5F53%u65F6%u88AB%u6C14%u7B11%u4E86%uFF0C%u8FD9%u4E5F%u8BF4%u660E%u4E00%u4E2A%u4E8B%u5B9E%uFF0C%u4EFB%u4F55%u56FD%u9645%u9AD8%u79D1%u6280%u516C%u53F8%u8FDB%u5165%u4E2D%u56FD%u90FD%u6709%u5F88%u5F3A%u7684%u58C1%u5792%uFF0C%u80FD%u505A%u5230%u88AB%u4F17%u4EBA%u63A5%u53D7%u90A3%u66F4%u662F%u96BE%u4E0A%u52A0%u96BE%u3002%u5F88%u591A%u4EBA%u90FD%u89C9%u5F97%u4E9A%u9A6C%u900A%u53EA%u662F%u50CF%u4EAC%u4E1C%u4E00%u6837%u7684%u4E00%u5BB6B2C%u7684%u7F51%u7AD9%uFF0C%u5374%u4E0D%u77E5%u9053%u5176%u53C8%u662F%u4EAC%u4E1C%u7684%u6284%u88AD%u5BF9%u8C61%uFF0C%u800C%u8DDD%u79BB%uFF0C%u90A3%u53EA%u80FD%u5475%u5475%u4E86%u3002%0A%0A%u521D%u6B21%u77E5%u9053%u4E9A%u9A6C%u900A%u8FD8%u662F%u7F51%u4E0A%u770B%u5230%u6709%u4EBA%u8BF4%u8FD9%u662F%u4E00%u5BB6%u5356%u767E%u8D27%u7684%u7F51%u7AD9%uFF0C%u540E%u6765%u770B%u5230%u4F18%u79C0logo%u8BBE%u8BA1%u6848%u4F8B%u4E2D%u6709%u4E9A%u9A6C%u900Alogo%u7684%u8BE6%u7EC6%u89E3%u8BFB%uFF0CA-Z%u3001%u7B11%u8138%u3001%u5B57%u4F53%u7B49%u90FD%u5305%u542B%u5176%u4E2D%uFF0C%u518D%u540E%u6765%u5728%u540C%u5B66%u5BB6%u91CC%u770B%u5230%u4E86kindle%uFF0C%u88AB%u7CBE%u7F8E%u7684%u5916%u8868%u5438%u5F15%uFF0C%u518D%u540E%u6765%u770B%u5F00%u53D1%u8005%u8BBA%u575B%uFF0C%u77E5%u9053%u4E86AWS%uFF0C%u518D%u540E%u6765%u4E9A%u9A6C%u900A%u8FDB%u5165%u4E2D%u56FD%uFF0C%u65E5%u5E38%u8D2D%u7269+%u6D77%u6DD8%u6CA1%u5C11%u5728%u4E0A%u9762%u4E70%u4E1C%u897F%uFF0C%u76F4%u5230%u4ECA%u5929%uFF0C%u4E9A%u9A6C%u900A%u63A8%u51FA%u4E86%u7B2C%u4E00%u6B3E%u624B%u673A%u3002%0A%0A%23%23%23%23%u4E9A%u9A6C%u900A%u7684%u4F18%u52BF%uFF1A%0A-%20**%u8D1D%u4F50%u65AF**%uFF1A%u53EF%u4EE5%u8BF4%u6CA1%u6709%u8D1D%u4F50%u65AF%u5C31%u6CA1%u6709%u4ECA%u5929%u7684%u4E9A%u9A6C%u900A%uFF1B%0A-%20**AWS**%uFF1A%u4E9A%u9A6C%u900A%u4E91%u7684%u6280%u672F%u542B%u91CF%u7529%u51FAIT%u4F01%u4E1A%u5FAE%u8F6F%u548C%u82F9%u679C%u51E0%u6761%u8857%uFF1B%0A-%20**%u8D34%u8FD1%u751F%u6D3B**%uFF1A%u4E9A%u9A6C%u900A%u9500%u552E%u767E%u8D27%uFF0C%u6BCF%u4E2A%u4EBA%u9700%u8981%uFF1B%0A-%20**%u5353%u8D8A%u7684%u4ED3%u50A8%u7269%u6D41%u7CFB%u7EDF**%uFF1A%u8BF7%u6CE8%u610F%uFF0C%u6211%u4F7F%u7528%u7684%u662F%u5353%u8D8A%uFF1B%0A-%20**%u5F3A%u5927%u7684%u552E%u540E%u7CFB%u7EDF**%uFF1A%u5982%u679C%u4F60%u6253%u8FC7%u4EAC%u4E1C%u7684%u552E%u540E%u5C31%u4F1A%u7531%u8877%u7684%u8D5E%u53F9%u4E9A%u9A6C%u900A%u7684%u4E13%u4E1A%uFF1B%0A-%20**%u4E30%u5BCC%u7684%u7535%u5B50%u4E66%u8D44%u6E90**%uFF1A%u552F%u82F9%u679C%u53EF%u4EE5%u5339%u654C%uFF1B%0A%0A%23%23%23%23kindle%uFF1A%0Akindle%u5DF2%u7ECF%u7ECF%u5386%u4E86%u5F88%u591A%u4EE3%u4EA7%u54C1%uFF0C%u800C%u4E14%u4EA7%u54C1%u7EBF%u4E5F%u8DB3%u591F%u4E30%u5BCC%uFF0C%u76EE%u524D%u636E%u8BF4%u8FD8%u51C6%u5907%u5F00%u53D1%u4E00%u6B3E%u8D85%u8584%u578Bkindle%uFF0C%u7531%u4E8E%u5BF9%u7535%u5B50%u4E66%u6BD4%u8F83%u559C%u6B22%uFF0C%u6240%u4EE5%u6211%u4E5F%u5728paperwhite%u5165%u534E%u7684%u7B2C%u4E00%u5929%u4E70%u4E86%u4E00%u90E8%uFF0C%u8FD8%u662F%u633A%u559C%u6B22%u7684%uFF0C%u53EA%u662F%u5373%u4F7F%u6709%u80CC%u5149%u4E5F%u65E0%u6CD5%u5728%u665A%u4E0A%u8EBA%u5728%u5E8A%u4E0A%u6162%u6162%u770B%uFF0C%u65E2%u4F24%u773C%u775B%u4E5F%u5F71%u54CD%u65C1%u8FB9%u4EBA%u4F11%u606F%u3002%0A%0A%u53EF%u662Fkindle%u5DF2%u7ECF%u6784%u5EFA%u51FA%u4E00%u4E2A%u6210%u719F%u7684%u751F%u6001%u5708%uFF0C%u62FF%u73B0%u5728%u65F6%u9AE6%u7684%u8BDD%u6765%u8BF4%u5C31%u662F%u7EA2%u6D77%uFF0C%u8D44%u6E90%u4E30%u5BCC%uFF0C%u786C%u4EF6%u4F18%u79C0%uFF0C%u6B3E%u5F0F%u7E41%u591A%uFF0C%u643A%u5E26%u65B9%u4FBF%uFF0C%u603B%u6709%u4E00%u6B3E%u9002%u5408%u4F60%uFF0C%u4E0D%u4E70%u4E5F%u6765%u770B%u4E00%u770B%u3002%u7F51%u4E0A%u5F88%u591A%u4EBA%u8FD8%u662F%u5BF9n%u5E74%u524D%u7684kindle3%u30014%u60C5%u6709%u72EC%u949F%uFF0C%u8DB3%u4EE5%u89C1%u5F97%u4E9A%u9A6C%u900A%u5728%u8BBE%u8BA1%u4E0A%u7684%u72EC%u5230%u3002%u800C%u7535%u5B50%u4E66%u4E0D%u4EC5%u5927%u5927%u7684%u8282%u7701%u4E86%u7A7A%u95F4%uFF0C%u4E5F%u4E0D%u4F1A%u4E22%u5931%uFF0C%u53EA%u8981%u4F7F%u7528%u8D26%u53F7%u5C31%u53EF%u4EE5%u627E%u5230%u4E70%u8FC7%u7684%u6240%u6709%u4E66%u7C4D%u3002%0A%0A%23%23%23%23Fire%20Phone%uFF1A%0A%u4ECA%u5929%u4E9A%u9A6C%u900A%u63A8%u51FA%u4E86%u84C4%u8C0B%u5DF2%u4E45%u7684%u624B%u673A%uFF0C%u540D%u66F0Fire%20Phone%u3002%u770B%u7F51%u4E0A%u89C6%u9891%u8C8C%u4F3C%u505A%u5DE5%u8FD8%u4E0D%u9519%uFF0C%u4E0D%u4F1A%u50CF%u4E09%u68D2%u5B50%u90A3%u6837%u5168%u8EAB%u5851%u6599%u3002%u4EAE%u70B9%u529F%u80FD%u6709%u51E0%u70B9%uFF1A%0A-%20**3D%u6280%u672F**%uFF1A%u524D%u97625%u4E2A%u6444%u50CF%u5934%u4E0D%u662F%u767D%u88C5%u7684%uFF0C%u8FD9%u4E5F%u4F1A%u5E26%u6765%u524D%u6240%u672A%u6709%u7684%u7528%u6237%u4F53%u9A8C%uFF1B%0A-%20**%u65E0%u9650%u7684%u4E91**%uFF1A%u62CD%u7167%u53EF%u4EE5%u76F4%u63A5%u5B58%u50A8%u81F3AWS%uFF0C%u7A7A%u95F4%u65E0%u9650%u3002%u5728%u4F7F%u7528iphone%u65F6%u6211%u7684%u611F%u6168%u5C31%u662F%uFF0C%u5E38%u7528%u5E94%u7528%u7684%u7A7A%u95F4%u5176%u5B9E16G%u57FA%u672C%u591F%u7528%uFF0C%u62CD%u7167%u548C%u89C6%u9891%u5360%u7528%u7684%u7A7A%u95F4%u7279%u522B%u5927%uFF0C%u4E9A%u9A6C%u900A%u8FD9%u6837%u4F1A%u903C%u7684%u5176%u4ED6%u5382%u5546%u964D%u4F4E%u4E91%u4EF7%u683C%uFF0C%u597D%u4E8B%uFF1B%0A-%20**%u62CD%u7167%u626B%u8D27Firefly**%uFF1A%u5BF9%u4E8E%u4E00%u4E2A%u8D2D%u7269%u7F51%u7AD9%u6765%u8BF4%uFF0C%u8FD9%u79CD%u529F%u80FD%u662F%u5FC5%u5907%u65E0%u7591%u7684%u4E86%uFF0C%u91CD%u8981%u7684%u662F%u4FE1%u606F%u91CF%u548C%u8BC6%u522B%u51C6%u786E%u7387%uFF0C%u5E0C%u671B%u56FD%u5185%u4E5F%u80FD%u8DB3%u591F%u597D%uFF1B%0A%0A%u7F3A%u70B9%u4E5F%u662F%u6709%u7684%uFF0C%u5C31%u662F%u88F8%u673A%u4EF7%u683C%u592A%u9AD8%uFF0C%u6709%u591A%u5C11%u4EBA%u613F%u610F%u5728%u540C%u6837%u4EF7%u683C%u7684iphone6%u548C%u5B89%u5353%u624B%u673A%u4E0A%u9009%u62E9%u540E%u8005%u5462%u3002%0A%0A%23%23%23%23%u5173%u4E8E%u6284%u88AD%0A%u4E9A%u9A6C%u900A%u5728%u56FD%u5185%u6709%u4E0D%u5C11%u7684%u62E5%u8DB8%uFF0C%u4E0D%u5149%u662F%u6D88%u8D39%u8005%uFF0C%u8FD8%u6709%u4E00%u5927%u7968%u5382%u5546%uFF0C%u53EA%u80FD%u8BF4%u4ED6%u4EEC%u6BD4%u8F83%u8BC6%u8D27%uFF0C%u73B0%u5728AWS%u5DF2%u7ECF%u88AB%u6284%u7684%u6EE1%u5929%u98DE%u4E86%uFF0Ckindle%u4E5F%u5DF2%u7ECF%u6709%u592A%u591A%u7684%u5C71%u5BE8%u8D27%uFF0C%u90FD%u5DF2%u7ECF%u73A9%u6B7B%u4E00%u8F6E%u4E86%uFF0C%u624B%u673A%u561B%uFF0C%u4F30%u8BA1%u963F%u91CC%u3001%u4EAC%u4E1C%u4F1A%u8FC5%u901F%u8DDF%u4E0A%uFF0C%u5E0C%u671B%u4E9A%u9A6C%u900A%u4E14%u884C%u4E14%u73CD%u60DC%uFF0C%u65E9%u65E5%u5165%u534E%u65E9%u65E5%u8C03%u4EF7%u3002%0A%0A--------------------%0A%0A%u4E9A%u9A6C%u900A%u5DF2%u7ECF%u4E0D%u4EC5%u4EC5%u662F%u4E00%u4E2A%u7F51%u7AD9%uFF0C%u800C%u662F%u4E00%u5BB6IT%u754C%u9AD8%u79D1%u6280%u516C%u53F8%uFF0C%u4ED6%u7684%u6280%u672F%u3001%u7528%u6237%u4F53%u9A8C%u90FD%u662F%u4E1A%u754C%u7684%u4F7C%u4F7C%u8005%uFF0C%u7EFC%u5408%u80FD%u529B%u51FA%u5176%u53F3%u8005%u5BE5%u5BE5%u65E0%u51E0%uFF0C%u800C%u4E14%u8D1D%u4F50%u65AF%u66FE%u7ECF%u8BF4%u8FC7%uFF1A%u4E0D%u4F1A%u4E3A%u4E86%u8463%u4E8B%u4F1A%u76C8%u5229%u800C%u635F%u5BB3%u7528%u6237%u4F53%u9A8C%u3002%u6211%u8868%u793A%u975E%u5E38%u8D5E%u540C%uFF0C%u6709%u8FD9%u79CD%u505A%u4E8B%u7684%u7CBE%u795E%uFF0C%u53EB%u4EBA%u600E%u4E48%u80FD%u4E0D%u559C%u6B22%u8FD9%u5BB6%u516C%u53F8%u3002

2014年6月17日星期二

关于文档格式Markdown的建议


关于文档格式Markdown的建议

关于Markdown

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— 维基百科

目前程序员网站都已经开始支持甚至强制使用Markdown语法编写文档,比如evernote、github、stackoverflow等。在试用几次后,我强烈建议我们使用这种类型文档,因为:

  • 语法简洁:常用的就几种特殊字符,编辑熟练后无需使用鼠标
  • 排版优美:看到的是html的排版,再也不用看word里大量的回车符
  • 编辑器轻:基本都是在线编辑
  • 功能丰富:支持高亮代码段、公式、表格等等

Markdown文档编辑工具

名称 地址 优势
马克飞象 http://maxiang.info 可通过chrome下载到本地使用,同步evernote笔记
stackedit https://stackedit.io 功能强大,导出格式多,支持多个网盘及一键发布,内置图标
作业部落 https://www.zybuluo.com/mdeditor 多种编辑器模式,支持作业部落帐号,内置很多国内网站图标

另外github、stackoverflow都可以在线编辑,只是没有实时预览,且不是纯粹的文档编辑功能,如果单纯些文档不推荐使用;本地也可以使用一些文本编辑器,如sublime text,安装插件编辑Markdown,由于配置麻烦,也不推荐。

Markdown语法

由于Markdown实现的语法可以不同,所以每个编辑器的语法或多或少都有些差异,所以建议使用哪个编辑器可以查看对应的帮助文档(那里的文档都很漂亮)。


写在最后

希望这个推荐能够帮助大家提高工作效率,以后写文档、看文档时不再痛苦,技术发展的目的就是简化过程提供生产力的,我们不能让它白发展了。

%23%20%u5173%u4E8E%u6587%u6863%u683C%u5F0FMarkdown%u7684%u5EFA%u8BAE%0A%0A@%28%u5404%u79CD%u5B9E%u7528%u5DE5%u5177%29%5BMarkdown%7C%u6587%u6863%7C%u5DE5%u5177%5D%0A%0A%23%23%23%u5173%u4E8EMarkdown%0A%0A%3E%20Markdown%20%u662F%u4E00%u79CD%u8F7B%u91CF%u7EA7%u6807%u8BB0%u8BED%u8A00%uFF0C%u5B83%u5141%u8BB8%u4EBA%u4EEC%u4F7F%u7528%u6613%u8BFB%u6613%u5199%u7684%u7EAF%u6587%u672C%u683C%u5F0F%u7F16%u5199%u6587%u6863%uFF0C%u7136%u540E%u8F6C%u6362%u6210%u683C%u5F0F%u4E30%u5BCC%u7684HTML%u9875%u9762%u3002%20%20%20%20%u2014%u2014%20%5B%u7EF4%u57FA%u767E%u79D1%5D%28https%3A//zh.wikipedia.org/wiki/Markdown%29%0A%0A%u76EE%u524D%u7A0B%u5E8F%u5458%u7F51%u7AD9%u90FD%u5DF2%u7ECF%u5F00%u59CB%u652F%u6301%u751A%u81F3%u5F3A%u5236%u4F7F%u7528Markdown%u8BED%u6CD5%u7F16%u5199%u6587%u6863%uFF0C%u6BD4%u5982evernote%u3001github%u3001stackoverflow%u7B49%u3002%u5728%u8BD5%u7528%u51E0%u6B21%u540E%uFF0C%u6211%u5F3A%u70C8%u5EFA%u8BAE%u6211%u4EEC%u4F7F%u7528%u8FD9%u79CD%u7C7B%u578B%u6587%u6863%uFF0C%u56E0%u4E3A%uFF1A%0A-%20**%u8BED%u6CD5%u7B80%u6D01**%uFF1A%u5E38%u7528%u7684%u5C31%u51E0%u79CD%u7279%u6B8A%u5B57%u7B26%uFF0C%u7F16%u8F91%u719F%u7EC3%u540E%u65E0%u9700%u4F7F%u7528%u9F20%u6807%0A-%20**%u6392%u7248%u4F18%u7F8E**%uFF1A%u770B%u5230%u7684%u662Fhtml%u7684%u6392%u7248%uFF0C%u518D%u4E5F%u4E0D%u7528%u770Bword%u91CC%u5927%u91CF%u7684%u56DE%u8F66%u7B26%0A-%20**%u7F16%u8F91%u5668%u8F7B**%uFF1A%u57FA%u672C%u90FD%u662F%u5728%u7EBF%u7F16%u8F91%0A-%20**%u529F%u80FD%u4E30%u5BCC**%uFF1A%u652F%u6301%u9AD8%u4EAE%u4EE3%u7801%u6BB5%u3001%u516C%u5F0F%u3001%u8868%u683C%u7B49%u7B49%0A%0A%23%23%23Markdown%u6587%u6863%u7F16%u8F91%u5DE5%u5177%0A%7C%20%u540D%u79F0%20%20%20%20%20%20%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%u5730%u5740%20%7C%20%20%u4F18%u52BF%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%0A%7C%20%3A——–%20%7C%20——————%3A%7C%3A——————————————–%20%7C%0A%7C%60%u9A6C%u514B%u98DE%u8C61%60%20%7C%20http%3A//maxiang.info%20%7C%u53EF%u901A%u8FC7chrome%u4E0B%u8F7D%u5230%u672C%u5730%u4F7F%u7528%uFF0C%u540C%u6B65evernote%u7B14%u8BB0%20%7C%0A%7C%60stackedit%60%7C%20https%3A//stackedit.io%20%7C%u529F%u80FD%u5F3A%u5927%uFF0C%u5BFC%u51FA%u683C%u5F0F%u591A%uFF0C%u652F%u6301%u591A%u4E2A%u7F51%u76D8%u53CA%u4E00%u952E%u53D1%u5E03%uFF0C%u5185%u7F6E%u56FE%u6807%7C%0A%7C%60%u4F5C%u4E1A%u90E8%u843D%60%20%7C%20https%3A//www.zybuluo.com/mdeditor%20%7C%u591A%u79CD%u7F16%u8F91%u5668%u6A21%u5F0F%uFF0C%u652F%u6301%u4F5C%u4E1A%u90E8%u843D%u5E10%u53F7%uFF0C%u5185%u7F6E%u5F88%u591A%u56FD%u5185%u7F51%u7AD9%u56FE%u6807%7C%0A%0A%u53E6%u5916github%u3001stackoverflow%u90FD%u53EF%u4EE5%u5728%u7EBF%u7F16%u8F91%uFF0C%u53EA%u662F%u6CA1%u6709%u5B9E%u65F6%u9884%u89C8%uFF0C%u4E14%u4E0D%u662F%u7EAF%u7CB9%u7684%u6587%u6863%u7F16%u8F91%u529F%u80FD%uFF0C%u5982%u679C%u5355%u7EAF%u4E9B%u6587%u6863%u4E0D%u63A8%u8350%u4F7F%u7528%uFF1B%u672C%u5730%u4E5F%u53EF%u4EE5%u4F7F%u7528%u4E00%u4E9B%u6587%u672C%u7F16%u8F91%u5668%uFF0C%u5982sublime%20text%uFF0C%u5B89%u88C5%u63D2%u4EF6%u7F16%u8F91Markdown%uFF0C%u7531%u4E8E%u914D%u7F6E%u9EBB%u70E6%uFF0C%u4E5F%u4E0D%u63A8%u8350%u3002%0A%0A%23%23%23Markdown%u8BED%u6CD5%0A%u7531%u4E8EMarkdown%u5B9E%u73B0%u7684%u8BED%u6CD5%u53EF%u4EE5%u4E0D%u540C%uFF0C%u6240%u4EE5%u6BCF%u4E2A%u7F16%u8F91%u5668%u7684%u8BED%u6CD5%u6216%u591A%u6216%u5C11%u90FD%u6709%u4E9B%u5DEE%u5F02%uFF0C%u6240%u4EE5%u5EFA%u8BAE%u4F7F%u7528%u54EA%u4E2A%u7F16%u8F91%u5668%u53EF%u4EE5%u67E5%u770B%u5BF9%u5E94%u7684%u5E2E%u52A9%u6587%u6863%uFF08%u90A3%u91CC%u7684%u6587%u6863%u90FD%u5F88%u6F02%u4EAE%uFF09%u3002%0A%0A—————————%0A%23%23%23%23%u5199%u5728%u6700%u540E%0A%u5E0C%u671B%u8FD9%u4E2A%u63A8%u8350%u80FD%u591F%u5E2E%u52A9%u5927%u5BB6%u63D0%u9AD8%u5DE5%u4F5C%u6548%u7387%uFF0C%u4EE5%u540E%u5199%u6587%u6863%u3001%u770B%u6587%u6863%u65F6%u4E0D%u518D%u75DB%u82E6%uFF0C%u6280%u672F%u53D1%u5C55%u7684%u76EE%u7684%u5C31%u662F%u7B80%u5316%u8FC7%u7A0B%u63D0%u4F9B%u751F%u4EA7%u529B%u7684%uFF0C%u6211%u4EEC%u4E0D%u80FD%u8BA9%u5B83%u767D%u53D1%u5C55%u4E86%u3002

2014年6月13日星期五

使用C#对REST api的PUT调用

使用C#对REST api的PUT调用


最近需要添加一个REST的api接口,因为之前url都是丑陋的传参数格式,所以这次想试试目前比较网络比较流行的规范,就是同样的url利用访问方式的不同来执行查询、添加、修改、删除操作。

  • GET: 查询数据
  • POST:创建数据
  • PUT: 修改数据
  • DELETE:删除数据

首先是ashx文件部分:

switch (context.Request.RequestType)
{
    case "POST":
        result = CreateWeiXinPordService(context);
        break;
    case "PUT":
        result = ModifyWeiXinPordService(context);
        break;
    default:
        dt = GetWeiXinPordService(context);
        break;
}

由于是修改功能,底层接口都已经实现,于是通过判断请求类型来分别调用不同的接口。

其次是调用:

POST:

using(var client = new WebClient())
{
    var values = new NameValueCollection
    {
        { "memberPKId", "123"},
        { "beginDate", DateTime.Today.ToString()},
        { "endDate", DateTime.Today.ToString()},
        { "shopState", "1"},
        { "memoInfo", ""},
    };
    byte[] result = client.UploadValues("http://api.android.xxx.w/wx/ProdService.ashx", "POST", values);
    tb.Text = Encoding.ASCII.GetString(result);
}

PUT:

using (var client = new WebClient())
{
    var values = new NameValueCollection
    {
        { "servicePKId", "5"},
        { "endDate", this.Request["date"].ToString()},
        { "memoInfo", "修改啦"},
    };
    byte[] result = client.UploadValues("http://api.android.xxx.w/wx/ProdService.ashx", "PUT", values);
    tb2.Text = Encoding.ASCII.GetString(result);
}

在测试PUT方式时,页面报405错误,不允许使用该方法。原来IIS默认是不允许PUT动词的,求助了一下网络,这里再次说一下,stackoverflow是个好地方,唯一的缺点是无法注册,发现解决办法如下。

令IIS支持PUT访问方式的方法

  • 修改站点的web.config文件,添加以下内容
<system.webServer>
    <handlers>
      <remove name="WebDAV" />
      <remove name="SimpleHandlerFactory-Integrated-4.0" />
      <add name="SimpleHandlerFactory-Integrated-4.0" path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" resourceType="Unspecified" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <modules>
      <remove name="WebDAVModule" />
    </modules>
</system.webServer>

其中path可以指定文件,verb也可以指定几个访问方式,我这里是全部放开。

  • 修改访问文件的系统权限,添加iis_iuser用户组的修改权限

总结

到这里这个PUT的接口就可以顺利运行了,由于最近使用word发现排版非常不方便且不美观,于是自己试着用马克飞象写了这篇markdown格式的日志,感觉非常方便,就是代码块对C#的支持貌似不够好,但是也非常不错了,希望以后能引入工作中。

C#的路由程序还没研究明白,要不可以将url写的更优雅一点,留到下一次吧。

%23%u4F7F%u7528C%23%u5BF9REST%20api%u7684PUT%u8C03%u7528%0A@%28.net%29%5BC%23%7CREST%7CIIS%7CPUT%5D%0A%u6700%u8FD1%u9700%u8981%u6DFB%u52A0%u4E00%u4E2AREST%u7684api%u63A5%u53E3%uFF0C%u56E0%u4E3A%u4E4B%u524Durl%u90FD%u662F%u4E11%u964B%u7684%u4F20%u53C2%u6570%u683C%u5F0F%uFF0C%u6240%u4EE5%u8FD9%u6B21%u60F3%u8BD5%u8BD5%u76EE%u524D%u6BD4%u8F83%u7F51%u7EDC%u6BD4%u8F83%u6D41%u884C%u7684%u89C4%u8303%uFF0C%u5C31%u662F%u540C%u6837%u7684url%u5229%u7528%u8BBF%u95EE%u65B9%u5F0F%u7684%u4E0D%u540C%u6765%u6267%u884C%u67E5%u8BE2%u3001%u6DFB%u52A0%u3001%u4FEE%u6539%u3001%u5220%u9664%u64CD%u4F5C%u3002%0A-%20**GET**%uFF1A%20%u67E5%u8BE2%u6570%u636E%0A-%20**POST**%uFF1A%u521B%u5EFA%u6570%u636E%0A-%20**PUT**%uFF1A%20%u4FEE%u6539%u6570%u636E%0A-%20**DELETE**%uFF1A%u5220%u9664%u6570%u636E%0A%0A%23%23%23%23%u9996%u5148%u662Fashx%u6587%u4EF6%u90E8%u5206%uFF1A%0A%60%60%60%0Aswitch%20%28context.Request.RequestType%29%0A%7B%0A%20%20%20%20case%20%22POST%22%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20CreateWeiXinPordService%28context%29%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20case%20%22PUT%22%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20ModifyWeiXinPordService%28context%29%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20dt%20%3D%20GetWeiXinPordService%28context%29%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%7D%0A%60%60%60%0A%u7531%u4E8E%u662F%u4FEE%u6539%u529F%u80FD%uFF0C%u5E95%u5C42%u63A5%u53E3%u90FD%u5DF2%u7ECF%u5B9E%u73B0%uFF0C%u4E8E%u662F%u901A%u8FC7%u5224%u65AD%u8BF7%u6C42%u7C7B%u578B%u6765%u5206%u522B%u8C03%u7528%u4E0D%u540C%u7684%u63A5%u53E3%u3002%0A%0A%23%23%23%23%u5176%u6B21%u662F%u8C03%u7528%uFF1A%0A**POST%uFF1A**%0A%60%60%60C%23%0Ausing%28var%20client%20%3D%20new%20WebClient%28%29%29%0A%7B%0A%20%20%20%20var%20values%20%3D%20new%20NameValueCollection%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7B%20%22memberPKId%22%2C%20%22123%22%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22beginDate%22%2C%20DateTime.Today.ToString%28%29%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22endDate%22%2C%20DateTime.Today.ToString%28%29%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22shopState%22%2C%20%221%22%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22memoInfo%22%2C%20%22%22%7D%2C%0A%20%20%20%20%7D%3B%0A%20%20%20%20byte%5B%5D%20result%20%3D%20client.UploadValues%28%22http%3A//api.android.xxx.w/wx/ProdService.ashx%22%2C%20%22POST%22%2C%20values%29%3B%0A%20%20%20%20tb.Text%20%3D%20Encoding.ASCII.GetString%28result%29%3B%0A%7D%0A%60%60%60%0A%0A**PUT%uFF1A**%0A%60%60%60C%23%0Ausing%20%28var%20client%20%3D%20new%20WebClient%28%29%29%0A%7B%0A%20%20%20%20var%20values%20%3D%20new%20NameValueCollection%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7B%20%22servicePKId%22%2C%20%225%22%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22endDate%22%2C%20this.Request%5B%22date%22%5D.ToString%28%29%7D%2C%0A%20%20%20%20%20%20%20%20%7B%20%22memoInfo%22%2C%20%22%u4FEE%u6539%u5566%22%7D%2C%0A%20%20%20%20%7D%3B%0A%20%20%20%20byte%5B%5D%20result%20%3D%20client.UploadValues%28%22http%3A//api.android.xxx.w/wx/ProdService.ashx%22%2C%20%22PUT%22%2C%20values%29%3B%0A%20%20%20%20tb2.Text%20%3D%20Encoding.ASCII.GetString%28result%29%3B%0A%7D%0A%60%60%60%0A%0A%u5728%u6D4B%u8BD5PUT%u65B9%u5F0F%u65F6%uFF0C%u9875%u9762%u62A5405%u9519%u8BEF%uFF0C%u4E0D%u5141%u8BB8%u4F7F%u7528%u8BE5%u65B9%u6CD5%u3002%u539F%u6765IIS%u9ED8%u8BA4%u662F%u4E0D%u5141%u8BB8PUT%u52A8%u8BCD%u7684%uFF0C%u6C42%u52A9%u4E86%u4E00%u4E0B%u7F51%u7EDC%uFF0C%u8FD9%u91CC%u518D%u6B21%u8BF4%u4E00%u4E0B%uFF0Cstackoverflow%u662F%u4E2A%u597D%u5730%u65B9%uFF0C%u552F%u4E00%u7684%u7F3A%u70B9%u662F%u65E0%u6CD5%u6CE8%u518C%uFF0C%u53D1%u73B0%u89E3%u51B3%u529E%u6CD5%u5982%u4E0B%u3002%0A%23%23%23%23%u4EE4IIS%u652F%u6301PUT%u8BBF%u95EE%u65B9%u5F0F%u7684%u65B9%u6CD5%0A-%20%u4FEE%u6539%u7AD9%u70B9%u7684web.config%u6587%u4EF6%uFF0C%u6DFB%u52A0%u4EE5%u4E0B%u5185%u5BB9%0A%0A%60%60%60XML%0A%3Csystem.webServer%3E%0A%20%20%20%20%3Chandlers%3E%0A%20%20%20%20%20%20%3Cremove%20name%3D%22WebDAV%22%20/%3E%0A%20%20%20%20%20%20%3Cremove%20name%3D%22SimpleHandlerFactory-Integrated-4.0%22%20/%3E%0A%20%20%20%20%20%20%3Cadd%20name%3D%22SimpleHandlerFactory-Integrated-4.0%22%20path%3D%22*.ashx%22%20verb%3D%22*%22%20type%3D%22System.Web.UI.SimpleHandlerFactory%22%20resourceType%3D%22Unspecified%22%20preCondition%3D%22integratedMode%2CruntimeVersionv4.0%22%20/%3E%0A%20%20%20%20%3C/handlers%3E%0A%20%20%20%20%3Cmodules%3E%0A%20%20%20%20%20%20%3Cremove%20name%3D%22WebDAVModule%22%20/%3E%0A%20%20%20%20%3C/modules%3E%0A%3C/system.webServer%3E%0A%60%60%60%0A%u5176%u4E2Dpath%u53EF%u4EE5%u6307%u5B9A%u6587%u4EF6%uFF0Cverb%u4E5F%u53EF%u4EE5%u6307%u5B9A%u51E0%u4E2A%u8BBF%u95EE%u65B9%u5F0F%uFF0C%u6211%u8FD9%u91CC%u662F%u5168%u90E8%u653E%u5F00%u3002%0A-%20%u4FEE%u6539%u8BBF%u95EE%u6587%u4EF6%u7684%u7CFB%u7EDF%u6743%u9650%uFF0C%u6DFB%u52A0iis_iuser%u7528%u6237%u7EC4%u7684%u4FEE%u6539%u6743%u9650%0A%0A-------------------------------%0A%23%23%23%23%u603B%u7ED3%0A%u5230%u8FD9%u91CC%u8FD9%u4E2APUT%u7684%u63A5%u53E3%u5C31%u53EF%u4EE5%u987A%u5229%u8FD0%u884C%u4E86%uFF0C%u7531%u4E8E%u6700%u8FD1%u4F7F%u7528word%u53D1%u73B0%u6392%u7248%u975E%u5E38%u4E0D%u65B9%u4FBF%u4E14%u4E0D%u7F8E%u89C2%uFF0C%u4E8E%u662F%u81EA%u5DF1%u8BD5%u7740%u7528%u9A6C%u514B%u98DE%u8C61%u5199%u4E86%u8FD9%u7BC7markdown%u683C%u5F0F%u7684%u65E5%u5FD7%uFF0C%u611F%u89C9%u975E%u5E38%u65B9%u4FBF%uFF0C%u5C31%u662F%u4EE3%u7801%u5757%u5BF9C%23%u7684%u652F%u6301%u8C8C%u4F3C%u4E0D%u591F%u597D%uFF0C%u4F46%u662F%u4E5F%u975E%u5E38%u4E0D%u9519%u4E86%uFF0C%u5E0C%u671B%u4EE5%u540E%u80FD%u5F15%u5165%u5DE5%u4F5C%u4E2D%u3002%0A%0AC%23%u7684%u8DEF%u7531%u7A0B%u5E8F%u8FD8%u6CA1%u7814%u7A76%u660E%u767D%uFF0C%u8981%u4E0D%u53EF%u4EE5%u5C06url%u5199%u7684%u66F4%u4F18%u96C5%u4E00%u70B9%uFF0C%u7559%u5230%u4E0B%u4E00%u6B21%u5427%u3002