[转载]微信会员注册开发【带源码】:网页授权,得到code后在当前页面获取openid,js+php实现跨域请求 - 天际app开发工作室 - 博客园

mikel阅读(1037)

[转载]微信会员注册开发【带源码】:网页授权,得到code后在当前页面获取openid,js+php实现跨域请求 – 天际app开发工作室 – 博客园.

作者主页:天际app工作室 http://home.zhubajie.com/7145093/

需要引导微信公众平台用户点击链接进入注册页面,在注册页面需要获取用户微信的openid。技术核心是需要借助网页授权,并且在得到授权code时通过js立刻获取openid。

网上关于网页授权后一步步获取openid的文章大多是理论步骤的解说,落实到代码上具体怎么尽可能快的拿到openid的内容很少。笔者十分愤怒,决定写下代码和大家分享

 

这个过程需要一个前端页面代码和一个后端辅助程序,我这里前端是html+js,后端是php。

直接上代码,代码里注释解释的比较清楚:

前端:index.html


    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd";>  
    <!-- 天际app工作室 http://home.zhubajie.com/7145093/ -->  
    <html>  
    <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />  
    <meta name="viewport" content="width=device-width,height=device-height,inital-scale=1.0,maximum-scale=1.0,user-scalable=no;">  
    <meta name="apple-mobile-web-app-capable" content="yes">  
    <meta name="apple-mobile-web-app-status-bar-style" content="black">  
    <meta name="format-detection" content="telephone=no">  
      
    <title>会员注册</title>  
    <script type="text/javascript" src="jquery.js"></script>  
    <script type="text/javascript">  
      
     function callback(result) {    
            alert('cucess');    
            alert(result);  //输出openid  
        }    
          
    function getQueryString(name) {  
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");  
        var r = window.location.search.substr(1).match(reg);  
        if (r != null) return unescape(r[2]); return null;  
        }  
      
      
    var code = getQueryString("code");  
      
    $.ajax({   
          async: false,   
          url: "http://arvon2012.sinaapp.com/oauth2.php", //这是我的服务端处理文件php的  
          type: "GET",   
          //下面几行是jsoup,如果去掉下面几行的注释,后端对应的返回结果也要去掉注释  
          // dataType: 'jsonp',   
          // jsonp: 'callback', //jsonp的值自定义,如果使用jsoncallback,那么服务器端,要返回一个jsoncallback的值对应的对象.   
          // jsonpCallback:'callback',  
          data: {code:code}, //传递本页面获取的code到后台,以便后台获取openid  
          timeout: 5000,   
          success: function (result) {   
            callback(result);  
          },   
          error: function (jqXHR, textStatus, errorThrown) {   
              alert(textStatus);   
          }   
      });  
      
      
    </script>  
    </head>  
    <body>  
    </body>  

下面是服务端对应的代码,oauth2.php

<?php  
//天际app工作室 http://home.zhubajie.com/7145093/  
$code = $_GET['code'];//前端传来的code值  
  
$appid = "xxxxxxxxxxxxxxxx";  
$appsecret = "xxxxxxxxxxxxxxxxxxxxxx";  
  
//获取openid  
$url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$appsecret&code=$code&grant_type=authorization_code";  
  
$result = https_request($url);  
  
$jsoninfo = json_decode($result, true);  
$openid = $jsoninfo["openid"];//从返回json结果中读出openid  
  
$callback=$_GET['callback'];    
// echo $callback."({result:'".$openid."'})";   
echo $openid; //把openid 送回前端  
  
function https_request($url,$data = null){  
    $curl = curl_init();  
    curl_setopt($curl, CURLOPT_URL, $url);  
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);  
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);  
    if (!empty($data)){  
        curl_setopt($curl, CURLOPT_POST, 1);  
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);  
    }  
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);  
    $output = curl_exec($curl);  
    curl_close($curl);  
    return $output;  
}  
  
?>  

[转载]微信公共服务平台开发(.Net 的实现)12-------网页授权(上 :更加深入理解OAuth2.0 ) - conan_lin - 博客园

mikel阅读(959)

[转载]微信公共服务平台开发(.Net 的实现)12——-网页授权(上 :更加深入理解OAuth2.0 ) – conan_lin – 博客园.

我们首先来认识一下OAuth协议吧,这个东西很早就听说过,总觉得离我很远(我的项目用不到这些),但是最近不得不学习一下了。我在网上找了一些解释,认为解释的最好的是这样说的(出处:http://hi.baidu.com/powerthinks/item/f1cb9b3c7a88251c9dc65efa

如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间。是否有好办法可以避免这个问题呢?有的,听说有一些豪车的 车主就不担心这个问题。豪车一般配备两种钥匙:主钥匙和泊车钥匙。当你到酒店后,只需要将泊车钥匙交给服务生,停车的事情就由服务生去处理。与主钥匙相 比,这种泊车钥匙的使用功能是受限制的:它只能启动发动机并让车行驶一段有限的距离,可以锁车,但无法打开后备箱,无法使用车内其他设备。这里就体现了一 种简单的“开放授权”思想:通过一把泊车钥匙,车主便能将汽车的部分使用功能(如启动发动机、行驶一段有限的距离)授权给服务生。

授权是一个古老的概念,它是一个多用户系统必须支持的功能特性。比如,Alice和Bob都是Google的用户,那么Alice应该可以将自己的照片授 权给Bob访问。但请注意到,这种授权是一种封闭授权,它只支持系统内部用户之间的相互授权,而不能支持与其他外部系统或用户之间的授权。比如 说,Alice想使用“网易印像服务”将她的部分照片冲印出来,她怎么能做到呢?

肯定有人会说,Alice可以将自己的Google用户名和密码告诉网易印像服务,事情不就解决了吗?是的,但只有毫不关注安全和隐私的同学才会出此“绝招”。那么我们就来想一想,这一“绝招”存在哪些问题?

(1) 网易印像服务可能会缓存Alice的用户名和密码,而且可能没有加密保护。它一旦遭到攻击,Alice就会躺着中枪。

(2) 网易印像服务可以访问Alice在Google上的所有资源,Alice无法对他们进行最小的权限控制,比如只允许访问某一张照片,1小时内访问有效。

(3) Alice无法撤消她的单个授权,除非Alice更新密码。

在以Web服务为核心的云计算时代,像用户Alice的这种授权需求变得日益迫切与兴盛,“开放授权(Open Authorization)”也正因此而生,意在帮助Alice将她的资源授权给第三方应用,支持细粒度的权限控制,并且不会泄漏Alice的密码或其 它认证凭据。

 

上面的例子写的通俗易懂,各位一看就明白了,后面作者还附了一个这样图,以及注释:

 

从引言部分的描述我们可以看出,OAuth的参与实体至少有如下三个:

· RO (resourceowner): 资源所有者,对资源具有授权能力的人。如上文中的用户Alice。

· RS (resourceserver): 资源服务器,它存储资源,并处理对资源的访问请求。如Google资源服务器,它所保管的资源就是用户Alice的照片。

· Client: 第三方应用,它获得RO的授权后便可以去访问RO的资源。如网易印像服务。

此外,为了支持开放授权功能以及更好地描述开放授权协议,OAuth引入了第四个参与实体:

· AS(authorization server): 授权服务器,它认证RO的身份,为RO提供授权审批流程,并最终颁发授权令牌(Access Token)。读者请注意,为了便于协议的描述,这里只是在逻辑上把AS与RS区分开来;在物理上,AS与RS的功能可以由同一个服务器来提供服务。

如图1所示,协议的基本流程如下:

(1) Client请求RO的授权,请求中一般包含:要访问的资源路径,操作类型,Client的身份等信息。

(2) RO批准授权,并将“授权证据”发送给Client。至于RO如何批准,这个是协议之外的事情。典型的做法是,AS提供授权审批界面,让RO显式批准。这个可以参考下一节实例化分析中的描述。

(3) Client向AS请求“访问令牌(AccessToken)”。此时,Client需向AS提供RO的“授权证据”,以及Client自己身份的凭证。

(4) AS验证通过后,向Client返回“访问令牌”。访问令牌也有多种类型,若为bearer类型,那么谁持有访问令牌,谁就能访问资源。

(5) Client携带“访问令牌”访问RS上的资源。在令牌的有效期内,Client可以多次携带令牌去访问资源。

(6) RS验证令牌的有效性,比如是否伪造、是否越权、是否过期,验证通过后,才能提供服务。

 

虽然上面的例子举的很好,图画的也很清楚,可是对于我这种愚钝的人,还是理解了半天,请允许我在此狗尾续貂一次吧:

其实上面的图就如同这样一个故事

1、我的一个同事小张(Client),向我或者公司(RO)借钱,他会给我说:“借给我2000元,可以吗?”(也就是说向我请求一个有限的资源,就是Authorization Request);

2、这时我肯定不会把我的银行卡给他,并告诉他密码,我要用到OAuth。首先我会说“好的”,并且给他写一个条,上面写着“允许小张借款2000元”的请求(Authorization Grant);

3、小张拿着我写的条(AuthorizationGrant),给财务总监(AS);

4、财务总监看到我的条后会给小张一个提款单(Access Token),上面写着请出纳给小张支出2000元的借款。

5、小张拿到了提款单(AccessToken)到出纳那里,按照公司的规定,出纳只有看到了财务总监给的提款单(Access Token),才能过支出钱,这是出纳核对后,小张就顺利的拿到了2000元钱(Protected Resource)。

 

根据上面的故事我们来说已说OAuth的特点:

1、小张不可能知道,我或者公司总共有多少钱;

2、我也保护了账户的密码,维护的公司的制度;

3、就像现实中一样,财务总监和出纳一般都在一起办公,而AS和RS一般也会在同一个服务器上;

4、按照同样的公司制度(也就是OAuth),其他人不管谁来支出钱都要这样,我只需要保证制度执行的严密,其他任何人都不会知道我的帐务信息了。

这也就是OAuth全称— OpenAuthorization(开放式授权)的意义了。

 

下面我来看看为微信中是如何实现这个过程的,我引用了方倍工作室教程里面的图(http://www.cnblogs.com/txw1958/p/weixin71-oauth20.html):

 

在这个途中User就是我例子中的小张,APP就是我,Auth_svr就是财务总监和出纳。

如果我使用微信中的官法提法因该这么说:

1、小张向我提出访问,访问过程涉及以下参数:

 

也就是要知道我的APPID:其实就是我的名字;redirect_uri:到财务那里怎么走;response_type:知道我想我要批条(请填写code);scope批条的类别(后面会介绍到);state:到财务那里带些什么(参数)。

 

2、我同意了他的要求

也就是微信帮助提到的“如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。”也就是我给了小张一个批条(code),告诉小张去财务的路,以及到那里需要带些什么。

 

3、小张拿着我的批条到财务总监那里得到提款单

小张拿着我的批条(code),按照“redirect_uri/?code=CODE&state=STATE”这个路径去财务那里“通过code换取网页授权access_token”

 

上图可以知道我还可以获得OPENID!这个是非常重要的,前一章提到了它的用处, 微信在这里还特别强调了“获取用户基本信息接口是在用户和公众号产生消息交互时,才能根据用户OpenID获取用户基本信息,而网页授权的方式获取用户基 本信息,则无需消息交互,只是用户进入到公众号的网页,就可弹出请求用户授权的界面,用户授权后,就可获得其基本信息(此过程甚至不需要用户已经关注公众 号。)”

 

4、小张拿到了提款单又到出纳那里提款

得到了access_token后,拉取用户信息,也就是“通过access_token和openid拉取用户信息了。”

 

最终得到是如下的信息:

 

本来就此已经说完了,不过不少研发人员可能还是会有疑问:“费这么半天劲,不就得到了个用户的基本信息嘛,我通过其他方式也可以得到!”。

其实不然,这还是非常有用的:

第一,这是唯一一个重不需要关注你的微信好就可获得用户信息的办法;

第二,我们来设想一下下面场景:如果我们开发一个系统想过没想过不要用户名和密码了?用户登录,并授权以后,进入我们的系统,我系统里面存放着这个 微信号(OPENID)对应的权限的功能。这样我再也不用担心用户忘记密码或者密码泄露的事情了,因为这些事情微信已经帮助给做了。说到这里可能你还会 问:“那岂不是每个人必须有微信号”,我先不说这里我们做的就是微信平台的应用,也不说腾讯有多大的野心,这种OAuth协议已经在微 博,google,baidu,邮箱等广泛应用,我做他们响应的接口,你就说一个上用你系统的人,没有微信还不能有微博,google,baidu这些帐 号,没有这些帐号你还能没有个邮箱?这些我觉得因该是以后的趋势。

何况,随着微信的完善,以后或许我们拿到的不仅是用户的基本信息,还有用户朋友圈的帖子,图片,用户的喜好等等,这些都为我们新开发的系统提供了有效的可分析的数据!

微信重视版权的措施

mikel阅读(1011)

微信最近推出了《公众平台面向认证的媒体类型公众号公测原创声明功能》,具体操作如下:

原创者可在微信公众平台后台的“素材管理”处进入“图文消息”编辑,点击“申请原创声明”。

0

并填写提交原创声明的信息。
01

申请原创声明的文章在群发成功后,原创声明系统会对其进行智能比对,系统比对需要一定时间(半小时以内),比对通过后,系统会自动对文章添加“原创”标识。

从官方的解释文档中可以看出,微信对公众号的文章和内容不再听之任之,原创得将越来越受欢迎,自媒体可以发挥更大的能量,从官方的态度来看维护微信生态圈的平衡,抵制粗制滥造的内容,避免重蹈微博的覆辙。

最近微信频频出大招,从微信6.1的搜索功能,到朋友圈的广告,让人将注意力集中在微信得同时,也在猜测微信的态度,其实不用猜,微信就是想办法让用户体验更好,使用更方便。

[转载]Force.com微信开发系列(八)生成带参数的二维码 - JohnsonWang - 博客园

mikel阅读(1092)

[转载]Force.com微信开发系列(八)生成带参数的二维码 – JohnsonWang – 博客园.

为了满足用户渠道推广分析的需要,公众平台提供了生成带二维码的接口。使用该 接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送。目前有两种类型的二维码,分别是临时二维码和永久二维码,前者有过期时 间,最大为1800秒,但能够生成较多数量,后者无过期时间,数量较少(目前参数只支持1到100000)。两种二维码分别适用于账号绑定、用户来源统计 等场景。

用户扫描带场景值二维码时,可能推送以下两种事件:

1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者(例如为特定活动准备的二维码,与会者扫描后关注微信账号,与此同时微信账号可以将活动相关的信息推送给用户);

2. 如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值扫描事件推送给开发者(上例同样适用)。

获取带参数的二维码的过程包括两步,首先创建二维码ticket,然后凭借ticket到指定URL换取二维码。

 

创建二维码ticket

每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

临时二维码请求说明:

1 http请求方式: POST
2 URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
3 POST数据格式:json
4 POST数据例子:{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}

 

永久二维码请求说明

1 http请求方式: POST
2 URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
3 POST数据格式:json
4 POST数据例子:{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}}

 

请求JSON数据参数的具体说明如下:

image

正确的Json返回结果示例如下:

1 {"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm3sUw==","expire_seconds":60,"url":"http:\/\/weixin.qq.com\/q\/kZgfwMTm72WWPkovabbI"}

 

返回Json数据的格式说明如下:

image

错误的Json返回示例如下:

1 {"errcode":40013,"errmsg":"invalid appid"}

 

下面我们构造一个Visualforce Page来生成ticket。

Visualforce Page代码如下:


<span style="font-family: 微软雅黑;"><strong>第一步,创建二维码Ticket</strong></span>

请输入授权AccessToken:

{!msg}

上面代码第4行放置了一个apex:inputText控件,相当于HTML 的文本框,value的值指定了accessToken,这个必须是WeChatQRCodeGeneratorController类里一个有 Getter Setter的公开属性,如过该属性有默认值则文本框会显示这个默认值,如果用户修改了文本框的内容,accessToken属性的值也会自动改变。第5 行放置了一个apex:commandButton控件,相当于HTML的按钮,点击这个按钮将触发action处指定的方法send。最后第8行直接显 示msg变量,该变量会用来显示微信接口返回的Json。画面显示效果如下:

image

public class WeChatQRCodeGeneratorController {
public String msg { get; set; }
public String accessToken { get; set; }
public void send() {
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setHeader('Accept-Encoding','gzip,deflate');
req.setHeader('Content-Type','text/xml;charset=UTF-8');
req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1');

String json = '{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 12345}}}';

req.setBody(json);
req.setEndpoint('https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=' + accessToken);
String bodyRes = '';

try{
HttpResponse res = h.send(req);
bodyRes = res.getBody();
}
catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage()));
}
msg = bodyRes;
}
}

WeChatQRCodeGeneratorController类的代码如下:

public class WeChatQRCodeGeneratorController {
public String msg { get; set; }
public String accessToken { get; set; }
public void send() {
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setHeader('Accept-Encoding','gzip,deflate');
req.setHeader('Content-Type','text/xml;charset=UTF-8');
req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1');

String json = '{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 12345}}}';

req.setBody(json);
req.setEndpoint('https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=' + accessToken);
String bodyRes = '';

try{
HttpResponse res = h.send(req);
bodyRes = res.getBody();
}
catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage()));
}
msg = bodyRes;
}
}

完成后保存代码,输入正确有效的Access Token,点击“生成创建二维码Ticket”按钮将会得到如下图所示的用来换取二维码的票据。其实返回的json里最后一个参数url的值即是二维码的值,可以拿这个结果通过在线二维码生成器生成二维码:

image

 

通过ticket换取二维码

获取二维码ticket后,开发者可用ticket换取二维码图片,本接口无需登录即可调用,接口请求说明如下:

1 HTTP GET请求(请使用https协议)
2 https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

 

返回说明:
ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。
HTTP头(示例)如下:
Accept-Ranges:bytes
Cache-control:max-age=604800
Connection:keep-alive
Content-Length:28026
Content-Type:image/jpg
Date:Wed, 16 Oct 2013 06:37:10 GMT
Expires:Wed, 23 Oct 2013 14:37:10 +0800
Server:nginx/1.4.1

错误情况下(如ticket非法)返回HTTP错误码404。

利用前面返回的票据调用该接口示例如下:

1 https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQH97zoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2YweHpqY1hrX255RGdnckl0V0otAAIENIwAVAMECAcAAA==

 

直接在浏览器访问该接口将返回如下图所示的二维码图片,扫描该二维码图片将直接关注相应公众账号:

 

image

[转载]微信公众平台开发生成带参数二维码 83 _IT技术门户_LAMP_LNMP_搜集网 www.sosge.com

mikel阅读(789)

[转载]微信公众平台开发生成带参数二维码 83 _IT技术门户_LAMP_LNMP_搜集网 www.sosge.com.

本文介绍在微信公众平台上如何使用高级接口开发生成带参数二维码的功能。

 

一、场景二维码

为了满足用户渠道推广分析的需要,公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送。

目前有2种类型的二维码,分别是临时二维码和永久二维码,前者有过期时间,最大为1800秒,但能够生成较多数量,后者无过期时间,数量较少(目前参数只支持1–100000)。两种二维码分别适用于帐号绑定、用户来源统计等场景。

用户扫描带场景值二维码时,可能推送以下两种事件:

如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。

如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值扫描事件推送给开发者。

获取带参数的二维码的过程包括两步,首先创建二维码ticket,然后凭借ticket到指定URL换取二维码。

 

二、创建二维码ticket

每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

临时二维码请求说明

http请求URL

POST数据格式

{
"expire_seconds": 1800,
"action_name": "QR_SCENE",
"action_info": {
"scene": {
"scene_id": 100000
}
}
}

返回格式:

{
"ticket": "gQFK8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL3kweXE0T3JscWY3UTltc3ZPMklvAAIEG9jUUgMECAcAAA==",
"expire_seconds": 1800
}

永久二维码请求说明

http请求URL

POST数据格式


{
"action_name": "QR_LIMIT_SCENE",
"action_info": {
"scene": {
"scene_id": 1000
}
}
}

返回格式


{
"ticket": "gQHi8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0UweTNxNi1sdlA3RklyRnNKbUFvAAIELdnUUgMEAAAAAA=="
}

提交数据参数说明:

返回结果参数说明

程序实现

$access_token = " xDx0pD_ZvXkHM3oeu5oGjDt1_9HxlA-9g0vtR6MZ-v4r7MpvZYC4ee4OxN97Lr4irkPKE94tzBUhpZG_OvqAC3D3XaWJIGIn0eeIZnfaofO1C3LNzGphd_rEv3pIimsW9lO-4FOw6D44T3sNsQ5yXQ";

//临时
$qrcode = '{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 10000}}}';
//永久
$qrcode = '{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 1000}}}';

$url = ""color: #800080;"&gt;$access_token";
$result = https_post($url,$qrcode);
$jsoninfo = json_decode($result, true);
$ticket = $jsoninfo["ticket"];

function https_post($url, $data = null){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)){
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}

三、通过ticket换取二维码

获取二维码ticket后,开发者可用ticket换取二维码图片。无须登录态即可调用。

HTTPS GET请求说明(TICKET必需UrlEncode)

 

ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。

HTTP头示例如下:

 {
      "url": "",
      "content_type": "image/jpg",
      "http_code": 200,
      "header_size": 162,
      "request_size": 181,
      "filetime": -1,
      "ssl_verify_result": 20,
      "redirect_count": 0,
     "total_time": 0.509,
     "namelookup_time": 0,
     "connect_time": 0.058,
     "pretransfer_time": 0.343,
     "size_upload": 0,
     "size_download": 28497,
     "speed_download": 55986,
     "speed_upload": 0,
     "download_content_length": 28497,
     "upload_content_length": 0,
     "starttransfer_time": 0.481,
     "redirect_time": 0
 }

下面是二种场景二维码的URL及生成的图片

四、下载二维码

二维码生成以后,可以在浏览器中右键另存为本地图片,但如果有很多二维码的时候,用程序来下载就比较方便。

我们使用CURL获取图片的所有信息,并将图片数据保存为一个文件。一个完整的下载代码如下:



$ticket = "gQHi8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0UweTNxNi1sdlA3RklyRnNKbUFvAAIELdnUUgMEAAAAAA==";
 
$url = "".urlencode($ticket);
$imageInfo = downloadImageFromWeiXin($url);
 
$filename = "";
$local_file = fopen($filename, 'w');
if (false !== $local_file){
    if (false !== fwrite($local_file, $imageInfo["body"])) {
        fclose($local_file);
    }
}
 
function downloadImageFromWeiXin($url)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);    
    curl_setopt($ch, CURLOPT_NOBODY, 0);    //只取body头
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $package = curl_exec($ch);
    $httpinfo = curl_getinfo($ch);
    curl_close($ch);
    return array_merge(array('body' => $package), array('header' => $httpinfo)); 
}

这样,在程序当前目录就会生成一个包含二维码的图片文件。

五、扫描带参数二维码事件

用户扫描带场景值二维码时,可能推送以下两种事件:

如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。

如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。

1. 用户未关注时,进行关注后的事件推送



<xml>
<ToUserName><![CDATA[gh_45072270791c]]></ToUserName>
<FromUserName><![CDATA[o7Lp5t6n59DeX3U0C7Kric9qEx-Q]]></FromUserName>
<CreateTime>1389684286</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_1000]]></EventKey>
<Ticket><![CDATA[gQHi8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0UweTNxNi1sdlA3RklyRnNKbUFvAAIELdnUUgMEAAAAAA==]]></Ticket>
</xml>

参数说明

2. 用户已关注时的事件推送



<xml>
<ToUserName><![CDATA[gh_45072270791c]]></ToUserName>
<FromUserName><![CDATA[o7Lp5t6n59DeX3U0C7Kric9qEx-Q]]></FromUserName>
<CreateTime>1389684184</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[1000]]></EventKey>
<Ticket><![CDATA[gQHi8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0UweTNxNi1sdlA3RklyRnNKbUFvAAIELdnUUgMEAAAAAA==]]></Ticket>
</xml>

参数说明

事件代码:

以下代码判定了扫描带参数二维码的两种情形

private function receiveEvent($object)
{
    $contentStr = "";
    switch ($object->Event)
    {
        case "subscribe":
            $contentStr = "欢迎关注 ";
            if (isset($object->EventKey)){
                $contentStr = "关注二维码场景 ".$object->EventKey;
            }
            break;
        case "SCAN":
            $contentStr = "扫描 ".$object->EventKey;
            break;
        default:
            break;      
 
    }
    $resultStr = $this->transmitText($object, $contentStr);
    return $resultStr;
}

[转载]微信公共服务平台开发(.Net 的实现)9-------处理二维码 - 大西瓜3721 - 博客园

mikel阅读(1102)

[转载]微信公共服务平台开发(.Net 的实现)9——-处理二维码 – 大西瓜3721 – 博客园.

今天我们来共同学习一下微信公共服务平台中一个重要内容—二维码扫描。众所周知二维码目前应用范围很广,在这里不再叙述背景了,但是值得一提的 是目前大家手机上面应用的二维码扫描工具是支持的都是QR码和PDF417码标准,这就是为什么你用各种扫码工具扫出来的都一样的原因,因为大家都使用了 同样的标准。

在微信中的“扫一扫”功能也支持了这种标准,但是我们如果在微信公共服务平台中开发,使用的就不是这种标准了,而是微信自己的标准。也就是说如果你用通用 软件生成的二维码“微信”,“我查查”等都可以扫描出来,但是你用微信公共服务平台中生成的二维码,就“只能用微信的扫一扫进行扫描”,别的软件扫描出来 的结果均是微信自己加密的连接。这一点真的可以看出腾讯的野心了,不过话说回来了,你用人家的微信平台,肯定必须用人家的微信的扫一扫了。

只是有一点,如果我们把生成好的二维码贴出来,让用户扫的时候必须都要加上一句“请使用微信的扫一扫进行扫描”,也就是说用户要想扫你的二维码就必须用微信软件,自己就必须用微信号!好阴险啊!!

下面说一说我理解的微信二维码使用的过程,首先我们可以把自己想放在二维码里面的信息,使用自己的微信公共号生成这个二维码,通过web或者什么以图片的 形式输出出来,然后我们对这这个二维码用微信的“扫一扫”扫描,如果你还未关注这个微信公共号,微信会提醒你关注,并反馈相应的信息,如果你关注了这个 号,那么会自动反馈给你二维码里面的内容。

下面开始代码的内容:
首先我们参考微信的教程,使用自己的微信公共号生成一个二维码,并通过web显示出来。利用我们之前讲过的Access_Token创建一个含有自己信息 的Ticket,自己的信息在这里叫做“scene_id”。微信平台使用了这样Json对象来描述Ticket:

临时二维码:
{“expire_seconds”: 1800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}}
第一个参数代表有效时间,第二个参数就是临时二维码的标识,是一个常量,第三个参数就是我们要放进去的“scene_id”

永久二维码:
{“action_name”: “QR_LIMIT_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}}
只是缺少了有效时间,其他不再解释了

看看下面创建Ticket的代码,函数返回值是包含了我们自定义信息的Ticket:


    /// <summary>  
        /// 创建二维码ticket  
        /// </summary>  
        /// <returns></returns>  
        public static string CreateTicket(string TOKEN)  
        {  
      
            string result = "";  
            //string strJson = @"{""expire_seconds"":1800, ""action_name"": ""QR_SCENE"", ""action_info"": {""scene"": {""scene_id"":100000023}}}";  
            string strJson = @"{""action_name"": ""QR_LIMIT_SCENE"", ""action_info"": {""scene"": {""scene_id"":100000024}}}";  
            string wxurl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + TOKEN;  
      
            WebClient myWebClient = new WebClient();  
            myWebClient.Credentials = CredentialCache.DefaultCredentials;  
            try  
            {  
      
                result = myWebClient.UploadString(wxurl, "POST", strJson);  
                //WriteLog("上传result:" + result);  
                Ticket _mode = JsonHelper.ParseFromJson<Ticket>(result);  
                //UploadMM _mode = JsonHelper.ParseFromJson<UploadMM>(result);  
                //result = _mode.ticket;  
                result = _mode.ticket + "_" + _mode.expire_seconds;  
            }  
            catch (Exception ex)  
            {  
                result = "Error:" + ex.Message;  
            }  
            //WriteLog("上传MediaId:" + result);  
      
            return result;  
        }  

    /// <summary>  
        /// 创建二维码ticket  
        /// </summary>  
        /// <returns></returns>  
        public static string CreateTicket(string TOKEN)  
        {  
      
            string result = "";  
            //string strJson = @"{""expire_seconds"":1800, ""action_name"": ""QR_SCENE"", ""action_info"": {""scene"": {""scene_id"":100000023}}}";  
            string strJson = @"{""action_name"": ""QR_LIMIT_SCENE"", ""action_info"": {""scene"": {""scene_id"":100000024}}}";  
            string wxurl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + TOKEN;  
      
            WebClient myWebClient = new WebClient();  
            myWebClient.Credentials = CredentialCache.DefaultCredentials;  
            try  
            {  
      
                result = myWebClient.UploadString(wxurl, "POST", strJson);  
                //WriteLog("上传result:" + result);  
                Ticket _mode = JsonHelper.ParseFromJson<Ticket>(result);  
                //UploadMM _mode = JsonHelper.ParseFromJson<UploadMM>(result);  
                //result = _mode.ticket;  
                result = _mode.ticket + "_" + _mode.expire_seconds;  
            }  
            catch (Exception ex)  
            {  
                result = "Error:" + ex.Message;  
            }  
            //WriteLog("上传MediaId:" + result);  
      
            return result;  
        }  

创建了Ticket以后,我们再根据这个Ticket创建一个图片,并保存在服务器上,函数返回的是图片在服务器上的路径(便于我们使用image控件显示出来):


    public string GetTicketImage(string TICKET)  
        {  
            string content = string.Empty;  
            string strpath = string.Empty;  
            string savepath = string.Empty;  
      
            string stUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + Server.UrlEncode(TICKET);  
      
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(stUrl);  
      
            req.Method = "GET";  
      
            using (WebResponse wr = req.GetResponse())  
            {  
                HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse();  
                strpath = myResponse.ResponseUri.ToString();  
      
                WebClient mywebclient = new WebClient();  
      
                savepath = Server.MapPath("image") + "\\" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next().ToString().Substring(0, 4) + "." + myResponse.ContentType.Split('/')[1].ToString();  
      
                try  
                {  
                    mywebclient.DownloadFile(strpath, savepath);  
                }  
                catch (Exception ex)  
                {  
                    savepath = ex.ToString();  
                }  
      
      
            }  
            return strpath.ToString();  
        }  

    public string GetTicketImage(string TICKET)  
        {  
            string content = string.Empty;  
            string strpath = string.Empty;  
            string savepath = string.Empty;  
      
            string stUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + Server.UrlEncode(TICKET);  
      
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(stUrl);  
      
            req.Method = "GET";  
      
            using (WebResponse wr = req.GetResponse())  
            {  
                HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse();  
                strpath = myResponse.ResponseUri.ToString();  
      
                WebClient mywebclient = new WebClient();  
      
                savepath = Server.MapPath("image") + "\\" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next().ToString().Substring(0, 4) + "." + myResponse.ContentType.Split('/')[1].ToString();  
      
                try  
                {  
                    mywebclient.DownloadFile(strpath, savepath);  
                }  
                catch (Exception ex)  
                {  
                    savepath = ex.ToString();  
                }  
      
      
            }  
            return strpath.ToString();  
        }  

然后我们把这个图片显示出来就可以了:


    protected void Page_Load(object sender, EventArgs e)  
        {  
            string _access_token = string.Empty;  
            _access_token = IsExistAccess_Token();  
      
            string str = CreateTicket(_access_token);  
            Response.Write(str + "<br/>");  
            str = str.Split('_')[0].ToString();  
            str = GetTicketImage(str);  
            Image1.ImageUrl = str;  
            Response.Write(str);  
        }  

    protected void Page_Load(object sender, EventArgs e)  
        {  
            string _access_token = string.Empty;  
            _access_token = IsExistAccess_Token();  
      
            string str = CreateTicket(_access_token);  
            Response.Write(str + "<br/>");  
            str = str.Split('_')[0].ToString();  
            str = GetTicketImage(str);  
            Image1.ImageUrl = str;  
            Response.Write(str);  
        }  

上面用到了一个Ticket类,我把代码也粘贴出来:


    /// <summary>  
    ///Ticket 的摘要说明  
    /// </summary>  
    public class Ticket  
    {  
        public Ticket()  
        {  
            //  
            //TODO: 在此处添加构造函数逻辑  
            //  
        }  
      
        string _ticket;  
        string _expire_seconds;  
      
        /// <summary>  
        /// 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。  
        /// </summary>  
        public string ticket  
        {  
            get { return _ticket; }  
            set { _ticket = value; }  
        }  
      
        /// <summary>  
        /// 凭证有效时间,单位:秒  
        /// </summary>  
        public string expire_seconds  
        {  
            get { return _expire_seconds; }  
            set { _expire_seconds = value; }  
        }  
    }  

    /// <summary>  
    ///Ticket 的摘要说明  
    /// </summary>  
    public class Ticket  
    {  
        public Ticket()  
        {  
            //  
            //TODO: 在此处添加构造函数逻辑  
            //  
        }  
      
        string _ticket;  
        string _expire_seconds;  
      
        /// <summary>  
        /// 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。  
        /// </summary>  
        public string ticket  
        {  
            get { return _ticket; }  
            set { _ticket = value; }  
        }  
      
        /// <summary>  
        /// 凭证有效时间,单位:秒  
        /// </summary>  
        public string expire_seconds  
        {  
            get { return _expire_seconds; }  
            set { _expire_seconds = value; }  
        }  
    }  

第一部的工作我们做完了,按照微信自己的标准生成了一个包含我们自定义信息的二维码了,下面就是扫码过程了,这里您可以使用其他标准的扫码工具扫一下试试,我们包含的“scene_id”信息是扫不出来的,刚才说过了,这不是一个标准的二维码标准,是微信自己的!下面我们来写一下微信扫码的事件响应就OK了,这里有两个事件:第一是我们没有关注这个微信号时直接扫(扫完会提醒您关注),第二是我们已经关注后扫,我们把之前的代码加上这两个事件,不再解释了:


    protected void Page_Load(object sender, EventArgs e)  
         {  
    wxmessage wx = GetWxMessage();  
             string res = "";  
      
      
             if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.Trim() == "subscribe")  
             {  
                 string content = "";  
                 if (!wx.EventKey.Contains("qrscene_"))  
                 {  
                     content = "/:rose欢迎北京永杰友信科技有限公司/:rose\n直接回复“你好”";  
                     res = sendTextMessage(wx, content);  
                 }  
                 else  
                 {  
                     content = "二维码参数:\n" + wx.EventKey.Replace("qrscene_", "");  
                     res = sendTextMessage(wx, content);  
                 }  
             }  
      
             else if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.ToLower() == "scan")  
             {  
                 string str = "二维码参数:\n" + wx.EventKey;  
                 res = sendTextMessage(wx, str);  
             }  
             else if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.Trim() == "CLICK")  
             {  
                 if(wx.EventKey=="HELLO")  
                     res = sendTextMessage(wx, "你好,欢迎使用北京永杰友信科技有限公司公共微信平台!");  
             }  
             else  
             {  
                 if (wx.MsgType == "text" && wx.Content == "你好")  
                 {  
                     res = sendTextMessage(wx, "你好,欢迎使用北京永杰友信科技有限公司公共微信平台!");  
                 }  
                 else if (wx.MsgType == "voice")  
                 {  
                     res = sendTextMessage(wx, wx.Recognition);  
                 }  
                 else  
                 {  
                     res = sendTextMessage(wx, "你好,未能识别消息!");  
                 }  
             }  
      
             Response.Write(res);  
         }  

    protected void Page_Load(object sender, EventArgs e)  
         {  
    wxmessage wx = GetWxMessage();  
             string res = "";  
      
      
             if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.Trim() == "subscribe")  
             {  
                 string content = "";  
                 if (!wx.EventKey.Contains("qrscene_"))  
                 {  
                     content = "/:rose欢迎北京永杰友信科技有限公司/:rose\n直接回复“你好”";  
                     res = sendTextMessage(wx, content);  
                 }  
                 else  
                 {  
                     content = "二维码参数:\n" + wx.EventKey.Replace("qrscene_", "");  
                     res = sendTextMessage(wx, content);  
                 }  
             }  
      
             else if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.ToLower() == "scan")  
             {  
                 string str = "二维码参数:\n" + wx.EventKey;  
                 res = sendTextMessage(wx, str);  
             }  
             else if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.Trim() == "CLICK")  
             {  
                 if(wx.EventKey=="HELLO")  
                     res = sendTextMessage(wx, "你好,欢迎使用北京永杰友信科技有限公司公共微信平台!");  
             }  
             else  
             {  
                 if (wx.MsgType == "text" && wx.Content == "你好")  
                 {  
                     res = sendTextMessage(wx, "你好,欢迎使用北京永杰友信科技有限公司公共微信平台!");  
                 }  
                 else if (wx.MsgType == "voice")  
                 {  
                     res = sendTextMessage(wx, wx.Recognition);  
                 }  
                 else  
                 {  
                     res = sendTextMessage(wx, "你好,未能识别消息!");  
                 }  
             }  
      
             Response.Write(res);  
         }  

OK,这样我们就完成了上面所设想的功能,扫码后先判断了是否关注,没有关注的提示关注,然后反馈我们自定义的参数“scene_id”,如果关注了同样直接反馈“scene_id”信息。

这种应用很广的,我现在能想象到的有以下几个场景:
1、餐馆/商店
我们把每道菜都用一个二维码进行标注,客人在点餐的时候使用微信的扫一扫功能进行点餐,第一次扫就提示客人请关注我们餐馆的微信号,并点了第一个菜,然后第二道。。这样我们就能很轻松的做到两件事情,第一,他关注了我们的微信号(为了留住回头客),第二,我们知道他喜欢吃什么口味,如喜欢川菜还是粤菜(为了给回头客更好的推荐)

2、调查表
虽然大家都关注的是同一个微信号,但是不同的二维码有不同的意义,大家在扫码的过程中会有不同的需求,比如说我们准备3个二维码分别对应不同的表格给老中青三种客户,用户在扫码的同时就可以进入表格进行填写,如果在二维码中加入调查员的信息,我们还能对调查员进行绩效考核。

3、产品溯源
同样的道理,没种产品或者不同的批次不同的地区都可以用不同的二维码进行编码,返回不同的信息,同时用户在扫码的同时又关注了我们的微信号。

4、扫码支付
利用临时的二维码,每一笔交易,不同的帐户,不同的金额,生成不同的码,用户拍完以后进入不同的“微支付”。

用途很多很多,不过还是那个前提—–“只能用微信扫!”
先就说这么多吧,等我把这些最基础的技术开发写完,我们在来共同探讨和开发几个成品案例

微商做得是产品还是服务

mikel阅读(1025)

微商到底做得是产品还是服务?比淘宝、京东产品的价格竞争优势在哪?唯一不同得就只能是服务了,只有拼谁的服务好,谁得体验好,才能在差异中生存,同样的产品网上很多,同质化低价化严重,其实很多人在微信上买商品,都是买得就是服务,毕竟朋友圈上大家都是熟识的,服务起来自然顺风顺水,也比较贴心。

从微信的新功能推出可以看出微信开始回笼了,从广告收入到各种接口的接入,竞价收入也不远了,岌岌可危的不是微商而是更多的第三方微信平台,尽管开放还在继续,可是有多少人心底有底微信不会突然改变了策略让各种第三方应用平台难以对接,从微信出现到现在为止,就没有停止过争议,很多产品依赖微信平台生存着,如今面临微信的态度,大家是生存还是死亡,就只能看微信的脸色,人无远虑必有近忧。

20141110080510ce3ae

[转载]关于SqlServer SMO - GuiYong - 博客园

mikel阅读(2100)

[转载]关于SqlServer SMO – GuiYong – 博客园.

最近在改代码生成器的时候,无意发现的,据说SQL Server Management Studio就是用smo开发的,所以只要 SQL Server Management Studio可以实现的东西,利用smo也同样可以实现!所有特意记下来,以后再好好研究…

MSDN:http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.aspx

MSDN相关说明:

Microsoft.SQLServer.Management.Smo命名空间包含核心的SQL Server数据库引擎对象。这些包括实例,数据库,表,存储过程和视图。

Microsoft.SQLServer.Management.Smo命名空间包含表示SQL Server数据库引擎的对象和一些实用工具类,代表特定的任务,如脚本的实例对象类。当连接到SQL Server数据库引擎的实例已经建立了使用Server对象变量,对象实例上可以访问使用SMO实例对象。例如,您可以使用数据库对象来访问数据库连接SQL Server数据库引擎实例。所有类实例对象层次中涉及到的服务器类。存在的实用工具类,服务器类对象层次结构之外的,代表特定的任务,如备份或脚本。

大多数类Microsoft.SqlServer.Management.Smo命 名空间驻留在Microsoft.SqlServer.Smo.dll和Microsoft.SqlServer.SmoExtended.dll文件。 此外,一些枚举类在Microsoft.SqlServer.SqlEnum.dll和Microsoft.SqlServer.SmoEnum.dll 的装配文件。您必须将所有四个文件导入,访问所有Microsoft.SqlServer.Management.Smo命名空间中的类

通过使用Microsoft.SqlServer.Management.Smo命名空间,你可以做到以下几点:

  • 连接到SQL Server数据库引擎的实例。
  • 查看和修改实例设置和配置选项。
  • 查看和修改数据库对象。
  • 执行DDL(数据定义语言)的任务在SQL Server数据库引擎的实例。
  • 脚本的数据库的依赖关系。
  • 执行数据库维护任务,如备份和恢复操作。
  • 传输数据库模式和数据。

一个基础例子:通过smo获得表的Create脚本

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.SqlServer.Management.Smo;
using System.Collections.Specialized;

namespace Sql_smoDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

//smo是SqlServer Management Ojbects的简称
//msdn参考文档:http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.aspx。
//需要引用的dll:
//Microsoft.SqlServer.ConnectionInfo.dll
//Microsoft.SqlServer.Smo.dll
//Microsoft.SqlServer.Management.Sdk.Sfc.dll
private void Form1_Load(object sender, EventArgs e)
{
Microsoft.SqlServer.Management.Common.ServerConnection conn = new Microsoft.SqlServer.Management.Common.ServerConnection(
new System.Data.SqlClient.SqlConnection("Data Source=ACE_YONG-PC;Initial Catalog=Gd_Dg;Integrated Security=True"));
Microsoft.SqlServer.Management.Smo.Server server = new Microsoft.SqlServer.Management.Smo.Server(conn);
Microsoft.SqlServer.Management.Smo.Database db = server.Databases["gd_dg"];
Microsoft.SqlServer.Management.Smo.Table tb = db.Tables["J001"];
//初始化脚本生成器
Microsoft.SqlServer.Management.Smo.Scripter a = new Microsoft.SqlServer.Management.Smo.Scripter(server);
//下面这些是可选项:作用是向脚本生成器中添加需要生成的脚本内容
//ScripOption类的属性:http://msdn.microsoft.com/zh-cn/library/microsoft.sqlserver.management.smo.scriptingoptions.aspx
a.Options.Add(ScriptOption.DriAllConstraints);
//DriAllConstraints 获取或设置布尔属性值,指定是否所有声明的参照完整性约束包含在生成的脚本。
a.Options.Add(ScriptOption.DriAllKeys);
//DriAllKeys 获取或设置布尔属性值指定的依赖关系是否生成的脚本中定义的所有声明的参照完整性密钥。
a.Options.Add(ScriptOption.Default);
//Default 获取或设置布尔属性值指定创建所引用对象是否包含在生成的脚本。
a.Options.Add(ScriptOption.ContinueScriptingOnError);
//ContinueScriptingOnError 获取或设置布尔属性值指定的脚本是否继续操作时遇到错误后。
a.Options.Add(ScriptOption.ConvertUserDefinedDataTypesToBaseType);
//ConvertUserDefinedDataTypesToBaseType 获取或设置布尔属性值指定是否将用户定义的数据类型转换成最合适的SQL Server基本数据类型生成的脚本中。
a.Options.Add(ScriptOption.IncludeIfNotExists);
// IncludeIfNotExists 获取或设置一个布尔属性值,指定包括它在脚本之前,是否检查一个对象是否存在。
a.Options.Add(ScriptOption.ExtendedProperties);
//声明统一资源名称集合对象
UrnCollection collection = new UrnCollection();
//The UrnCollection class represents a collection of Urn objects that represent Uniform Resource Name (URN) addresses.
collection.Add(tb.Urn);

//声明字符串集合对象:存储collection中的所有string对象(在这里其中有3个string对象)
StringCollection sqls = a.Script(collection);
//遍历字符串集合对象sqls中的string对象,选择要输出的脚本语句:
foreach (string s in sqls)
{
this.richTextBox1.Text += s;
}
//System.Collections.Specialized.StringCollection sc = tb.Script();
//foreach (String s in sc)
//{
// this.richTextBox1.Text += s;
//}

/* ///遍历表
foreach( var item in db.Tables ) {
//sb.AppendLine( item.ToString() );
}

//遍历字段
foreach( var item in tb.Columns ) {
//sb.AppendLine(item.ToString());
}

//遍历索引
foreach( var item in tb.Indexes ) {
//sb.AppendLine(item.ToString());
}

//遍历触发器
foreach( var item in tb.Triggers ) {
sb.AppendLine( item.ToString() );
}*/
}
}
}

结果示例:

 

 

<p!–出处!–>

 

ace_yong的头像

[作者]:ace_yong
[出处]:http://www.cnblogs.com/aceyong
[说明]:文章仅作个人知识整理备忘,方便自己查阅之用!如有雷同,敬请指出!

[转载]今日遇到了困难,顺便看了下SMO - Halower - 博客园

mikel阅读(1232)

[转载]今日遇到了困难,顺便看了下SMO – Halower – 博客园.

C# 如何获取本地数据库SQL Server2008的数据库服务器名称和数据库名称?不知道为什么我的Win7就是注册不了SQLdmo.dll,经过学习,基本可以自己使用SMO解决这些问题。

我在百度中找到了这个问题的答案,不过不知道为什么我的Win7就是注册不了sqldmo.dll,因此无法引用SQL Distributed Management Objects。

问题关键是我安装的是SQL2008EXPRESS版本没有安装SQL2000的 企业版附带不了这个COM组件,也不想安装2000,解决不了问题很纠结。因此我就看了下MSDN,里面说SQLDMO已经被SMO取代了,所以附带的看 了下,SMO,太菜只是知道了怎么使用SMO来控制数据库的简单操作。现在晚上,修改下内容,我已经基本了解SMO的用法了,以后共享给和我一样的新手, 谢谢大家的帮助,对了要是谁还用SQLMDO.DLL,下面的代码可以借鉴一下:

使用 SQLDMO(SQL Distributed Management Objects,SQL分布式管 理对象)。
添加引用-&gt;COM里面
//得到所有本地网络中可使用的SQL服务器列表。
SQLDMO.Application sqlApp = new SQLDMO.ApplicationClass();
SQLDMO.NameList sqlServers = sqlApp.ListAvailableSQLServers();
for(int i=0;i&lt;sqlServers.Count;i++) { object srv = sqlServers.Item(i + 1); if(srv != null) { this.cboServers.Items.Add(srv); } } if(this.cboServers.Items.Count &gt; 0)
this.cboServers.SelectedIndex = 0;
else
this.cboServers.Text = "";

//得到指定SQL服务器所有数据库的列表
SQLDMO.Application sqlApp = new SQLDMO.ApplicationClass();
SQLDMO.SQLServer srv = new SQLDMO.SQLServerClass();
srv.Connect(this.cboServers.SelectedItem.ToString(),this.txtUser.Text,this.txtPassword.Text);
foreach(SQLDMO.Database db in srv.Databases)
{
if(db.Name!=null)
this.cboDatabase.Items.Add(db.Name);
}

//下面是SMO的项目创建部分,就我不知道,所以记下来,给比我还新的新手看看,不过我也发现这个必须结合.NET3.5以上的版本的LINQ支持

 

在 Visual Studio .NET 中创建 Visual C# SMO 项目

本节介绍了如何生成简单的 SMO 控制台应用程序。

此示例导入命名空间,这样,程序即可以引用 SMO 类型。可以选择导入 Agent 命名空间。当编写使用 SQL Server 代理的程序时使用此命名空间。需要使用 Common 命名空间来建立与 SQL Server 实例的安全连接。使用 SqlClient 命名空间处理 SQL 异常错误。

在 Visual Studio .NET 中创建 Visual C# SMO 项目

启动 Visual Studio 2008(或 Visual Studio 2005)。

文件菜单上,单击新建项目。此时将显示新建项目对话框。

项目类型对话框中,选择“Visual C#”,然后选择“Windows”。在“Visual Studio 已安装的模板窗格中,选择“Windows 应用程序

(可选)在名称字段中,键入新应用程序的名称。

选择 Visual C# 应用程序类型。在下面的示例中,请选择控制台应用程序

项目菜单上,选择添加引用。此时将显示添加引用对话框。

单击浏览,在 C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\ 文件夹中找到 SMO 程序集,然后选择下列文件。这些文件是构建一个 SMO 应用程序至少需要的文件:

Microsoft.SQLServer.ConnectionInfo.dll

Microsoft.SQLServer.Smo.dll

Microsoft.SQLServer.Management.Sdk.Sfc.dll

Microsoft.SqlServer.SqlEnum.dll

注意

使用 Ctrl 键可选择多个文件。

添加需要的任何其他 SMO 程序集。例如,如果您要专门对 Service Broker 进行编程,则可以添加以下程序集:

Microsoft.SqlServer.ServiceBrokerEnum.dll

单击打开

视图菜单中,单击代码。或者选择“Program1.cs [设计窗口,然后双击 Windows 窗体以显示代码窗口。

在代码的命名空间语句前,键入以下 using 语句,以限定 SMO 命名空间中的类型:

using Microsoft.SqlServer.Management.Smo;

using Microsoft.SqlServer.Management.Common;

SMO 在 Microsoft.SqlServer.Management.Smo 下具有各种命名空间,如 Microsoft.SqlServer.Management.Smo.Agent。请根据需要添加这些命名空间。

您可以立即添加 SMO 代码。

[转载]手写代码生成器 - 汉城节度使 - 博客园

mikel阅读(869)

[转载]手写代码生成器 – 汉城节度使 – 博客园.

初来咋到,小试了手写代码生成器,望大牛们指点,下面是成型效果图

需求:

1、采用ORM(对象映射关系)模式;

2、主要解决提供现有表结构,生成Model、DAL层;

不多说了,下面进入正题

/// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns>返回文件信息</returns>
        private static string GetConfigFilePath(string fileName)
        {
            string currenctDir = AppDomain.CurrentDomain.BaseDirectory;//存放路径
            string configFile = System.IO.Path.Combine(currenctDir, fileName);
            return configFile;
        }

读取文件

窗体加载时,读取默认路径连接数据库文件信息

void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            string strcon = File.ReadAllText(GetConfigFilePath("connstr.txt"), Encoding.Default);//字符串以默认标准格式读取
            string[] str = strcon.Split(';');
            for (int i = 0; i < str.Count(); i++)
            {
                switch (i)
                {
                    case 0:
                        txtDataSource.Text = str[i].Split('=')[1];//数据库地址
                        break;
                    case 1:
                        txtDatabase.Text = str[i].Split('=')[1];//数据库名称
                        break;
                    case 2:
                        txtUserid.Text = str[i].Split('=')[1];//用户
                        break;
                    case 3:
                        txtPassword.Password = str[i].Split('=')[1];//密码
                        break;
                    default:
                        break;
                }
            }
        }

窗体加载

connstr.txt 文件信息

data source=10.10.198.111;database=systemconfig;user id=sa;password=sa

private void btnConnect_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                //查询所有表名称
                DataTable table = ExcuteDataTable(@"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
                        WHERE TABLE_TYPE = 'BASE TABLE'");
                tables = new List<string>();
                for (int i = 0; i < table.Rows.Count; i++)
                {
                    DataRow row = table.Rows[i];
                    tables.Add(row["TABLE_NAME"].ToString());
                }
                if (tables != null && tables.Count > 0)
                {
                    cmbTables.ItemsSource = TablesDesc(tables);
                    cmbTables.IsEnabled = true;
                    btnGenerateCode.IsEnabled = true;
                    cmbTables.SelectedIndex = 0;
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show("连接失败" + exc.Message);
                return;
            }
            string configFile = GetConfigFilePath("connstr.txt");
            File.WriteAllText(configFile, strconn);//创建一个新的文件,如果存在则覆盖
        }

连接数据库

private void btnGenerateCode_Click(object sender, RoutedEventArgs e)
        {
            string tableName = cmbTables.SelectedItem.ToString();
            if (ckbTable.IsChecked == true)//判断是否指定表生成
            {
                tableName = txtTableName.Text;
                if (string.IsNullOrEmpty(tableName))
                {
                    MessageBox.Show("请输入表名!");
                    return;
                }
                else
                {
                    if (!tables.Contains(tableName))
                    {
                        MessageBox.Show("您输入表名的不存在!");
                        return;
                    }
                }
            }
            CreatModelCode(tableName);//生成Model文件
            CreatDALCode(tableName);//生成DAL文件
        }

生成代码

private void CreatModelCode(string tablename)
        {
            //根据表列名创建Model层属性
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = tablename;
            if (tablename.Contains("_"))
            {
                Retable = tablename.Split('_')[1];
            }
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Text;");

            sb.Append("public class ").AppendLine(Retable + "Dto").AppendLine("{");
            foreach (DataColumn item in table.Columns)
            {
                sb.Append("public ").Append(RemoveSystem(GetDataType(item))).
                    Append(" ").Append(item.ColumnName).AppendLine(" {get;set;}");
            }
            sb.Append("}");
            txtModelCode.Text = sb.ToString();
            string configFile = GetConfigFilePath(Retable + "Dto.cs");//创建ModelDto文件
            File.WriteAllText(configFile, sb.ToString());
        }

创建Mode层代码

private DataTable ExcuteDataTable(string sql)
        {
            strconn = @"data source=" + txtDataSource.Text + ";database=" + txtDatabase.Text + ";user id="
               + txtUserid.Text + ";password=" + txtPassword.Password;
            using (SqlConnection cnn = new SqlConnection(strconn))//连接数据库
            {
                cnn.Open();
                using (SqlCommand cmd = cnn.CreateCommand())
                {
                    cmd.CommandText = sql;//执行sql
                    DataSet dataset = new DataSet();
                    SqlDataAdapter dapter = new SqlDataAdapter(cmd);
                    dapter.FillSchema(dataset, SchemaType.Source);
                    dapter.Fill(dataset);//将dataset添加到SqlDataAdapter容器中
                    return dataset.Tables[0];
                }
            }
        }

查询表信息

/// <summary>
        /// 判断表中列是否为空处理,范围属性类型
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        private static string GetDataType(DataColumn column)
        {
            if (column.AllowDBNull && column.DataType.IsValueType)
            {
                return column.DataType + "?";//表字段为空,类属性中添加?
            }
            else
            {
                return column.DataType.ToString();
            }
        }

根据表中各列字段类型返回model属性类型

/// <summary>
        /// 剔除列类型中包含system.字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string RemoveSystem(string str)
        {
            if (str.Contains("System."))
            {
                return str.Replace("System.", "");
            }
            else
            {
                return str;
            }
        }

剔除列类型中包含system.字符串

private void CreatDALCode(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("using System.Data.SqlClient;");

            sb.Append("public class ").AppendLine(Retable + "DAL").AppendLine("{");

            //ToModel
            sb.Append("private static ").Append(Retable + "Dto ").AppendLine("ToModel(DataRow row)").AppendLine("{");
            sb.Append(Retable + "Dto " + "dto").Append("=new ").AppendLine(Retable + "Dto();");
            foreach (DataColumn column in table.Columns)
            {
                sb.Append("dto.").Append(column.ColumnName).Append("=(")
                  .Append(RemoveSystem(GetDataType(column))).Append(")SqlHelper.FromDbValue(row[\"")
                 .Append(column.ColumnName).AppendLine("\"]);");
            }
            sb.AppendLine("return dto;");
            sb.AppendLine("}");

            //查询所有
            sb.Append("public static List<").AppendLine(Retable + "Dto> ListALL()").AppendLine("{");
            sb.Append("List<").AppendLine(Retable + "Dto>  lst=new List<" + Retable + "Dto>();");
            sb.Append("DataTable table = SqlHelper.ExecuteDataTable(\"select * from ").AppendLine(tablename + "\");");
            sb.AppendLine("for (int i = 0; i < table.Rows.Count; i++)");
            sb.AppendLine("{");
            sb.AppendLine(Retable + "Dto dto = ToModel(table.Rows[i]);");
            sb.AppendLine("lst.Add(dto);");
            sb.AppendLine("}");
            sb.AppendLine("return lst;");
            sb.AppendLine("}");

            //根据ID获取数据
            sb.Append("public static ").Append(Retable + "Dto ").AppendLine("GetById(string id)");
            sb.AppendLine("{");
            sb.AppendLine("DataTable table = SqlHelper.ExecuteDataTable(\"select * from " + tablename + " where id=@id\", new SqlParameter(\"@id\",id));");
            sb.AppendLine(Retable + "Dto dto=ToModel(table.Rows[0]);");
            sb.AppendLine("return dto;");
            sb.AppendLine("}");

            //删除
            sb.AppendLine("public static void Delete(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"delete from " + tablename + " where id=@id\", new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //停用(软删除)
            sb.AppendLine("public static void UnUser(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set status=1 where id=@id\",new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //启用
            sb.AppendLine("public static void User(string id)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set status=0 where id=@id\",new SqlParameter(\"@id\",id));");
            sb.AppendLine("}");

            //新增
            sb.AppendLine("public static void Insert(" + Retable + "Dto dto)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"insert into " + tablename + "(" + GetCoulmns(tablename) + ") values (" + GetValues(tablename) + ")\"," + GetSqlParameter(tablename, true) + ");");
            sb.AppendLine("}");

            //编辑
            sb.AppendLine("public static void Update(" + Retable + "Dto dto)");
            sb.AppendLine("{");
            sb.AppendLine("SqlHelper.ExecuteNonQuery(\"update " + tablename + "set" + GetUpdateValues(tablename) + " where id=@id\"," + GetSqlParameter(tablename, false) + ");");
            sb.AppendLine("}");
            sb.AppendLine("}");
            txtDALCode.Text = sb.ToString();
            string configFile = GetConfigFilePath(Retable + "DAL.cs");
            File.WriteAllText(configFile, sb.ToString());
        }

生成数据处理层代码

private string GetUpdateValues(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (table.Columns[i].ColumnName.ToLower() != "id" && table.Columns[i].ColumnName.ToLower() != "status")//id、status字段不做更新
                {
                    strs.Add(table.Columns[i].ColumnName + "=@" + table.Columns[i].ColumnName);
                }
            }
            return string.Join(",", strs);
        }

编辑操作,更新列值

private string GetCoulmns(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                strs.Add(table.Columns[i].ColumnName);
            }
            return string.Join(",", strs);
        }

获取表中所以列拼接

private string GetValues(string tablename)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (table.Columns[i].ColumnName == "id")
                {
                    strs.Add("newid()");
                }
                else if (table.Columns[i].ColumnName.ToLower() == "status")
                {
                    strs.Add("0");
                }
                else
                {
                    strs.Add("@" + table.Columns[i].ColumnName);
                }
            }
            return string.Join(",", strs);
        }

获取更新或插入的列值

private string GetSqlParameter(string tablename, bool isInsert)
        {
            DataTable table = ExcuteDataTable("select top 0 * from " + tablename);
            string Retable = GetTableName(tablename);
            List<string> strs = new List<string>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                if (isInsert)
                {
                    if (table.Columns[i].ColumnName.ToLower() != "id" && table.Columns[i].ColumnName.ToLower() != "status")
                    {
                        strs.Add("new SqlParameter(\"@" + table.Columns[i].ColumnName + "\", dto." + table.Columns[i].ColumnName + ")");
                    }
                }
                else
                {
                    if (table.Columns[i].ColumnName.ToLower() != "status")
                    {
                        strs.Add("new SqlParameter(\"@" + table.Columns[i].ColumnName + "\", dto." + table.Columns[i].ColumnName + ")");
                    }
                }
            }
            return string.Join(",", strs);
        }

获取SqlParameter参数集


其他忽略部分,作为优化用

/// <summary>
        /// 表名排序
        /// </summary>
        /// <param name="s"></param>
        private List<string> TablesDesc(List<string> lsttable)
        {
            var query = from s in lsttable orderby s ascending select s;
            List<string> tables = new List<string>();
            foreach (string item in query)
            {
                tables.Add(item);
            }
            return tables;
        }

        /// <summary>
        /// 获取表名后缀
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private string GetTableName(string tableName)
        {
            if (!tableName.Contains("_"))
            {
                return tableName;
            }
            return tableName.Split('_')[1];
        }