iw14.0.50来了,在新的版本中最吸引我的就是增加了完善的httphandler功能:终于可以直接在地址栏输入url打开iw功能页面了;可以自由使用EasyUI等js框架了;显示模式对话框也不再七绕八绕惹人烦了;呵呵,我感觉iw第一次接近主流web开发工具了!
兴奋过了,来尝试一下吧。其实iw的坑还是挺多的,虽然已经接近主流了,但是,后面慢慢都会说到…
    
     
      1、
     
    
    新建一个iw工程,选择
    
     Stand Alone Server / Service
    
    ,这种模式下进行开发是最理想的,调试很方便,正式发布时可以再建立一个library型的工程发布到.net服务器上。(是的,大家没有看错,现在iw已经脱离了isapi模式,可以像部署.net mvc4应用一样部署到iis上,后面会解释,.net虚拟主机也可以发布iw应用了!巨大的进步。)
   
    
     
      2、
     
    
    保存工程后在工程中添加一个新的单元文件,例如起名为wxapi.pas,这个文件中的代码将承担微信接入工作。代码如下:
   
    
     
      
       
        interface
       
      
     
     
      
       
        uses
       
      
      
      
       Classes
      
      
       ,
      
      
       IW
      
      
       .
      
      
       Content
      
      
       .
      
      
       Base
      
      
       ,
      
      
       System
      
      
       .
      
      
       SysUtils
      
      
       ,
      
      
       HTTPApp
      
      
       ,
      
      
       IWApplication
      
      
       ,
      
      
      
       IW
      
      
       .
      
      
       HTTP
      
      
       .
      
      
       Request
      
      
       ,
      
      
       IW
      
      
       .
      
      
       HTTP
      
      
       .
      
      
       Reply
      
      
       ;
      
      
       
        type
       
      
      
      
       
        
         /// <summary>
        
       
      
      
      
       
        
         /// 从TContentBase继承下来的类,就相当于asp.net中的httphandler
        
       
      
      
      
       
        
         /// </summary>
        
       
      
      
      
       TWxApi
      
      
       =
      
      
       
        class
       
      
      
       (
      
      
       TContentBase
      
      
       )
      
      
      
       
        protected
       
      
      
      
       
        function
       
      
      
       Execute
      
      
       (
      
      
       aRequest
      
      
       :
      
      
       THttpRequest
      
      
       ;
      
      
       aReply
      
      
       :
      
      
       THttpReply
      
      
       ;
      
      
       
        const
       
      
      
       aPathname
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
       aSession
      
      
       :
      
      
       TIWApplication
      
      
       ;
      
      
       aParams
      
      
       :
      
      
       TStrings
      
      
       ):
      
      
       Boolean
      
      
       ;
      
      
       
        override
       
      
      
       ;
      
      
      
       
        public
       
      
      
      
       
        constructor
       
      
      
       Create
      
      
       ;
      
      
       
        override
       
      
      
       ;
      
      
      
       
        end
       
      
      
       ;
      
      
       
        implementation
       
      
      
       
        uses
       
      
      
      
       ServerController
      
      
       ,
      
      
       UserSessionUnit
      
      
       ,
      
      
       Crypt
      
      
       .
      
      
       SHA1
      
      
       ;
      
      
       
        
         { TWxApi }
        
       
      
      
       
        constructor
       
      
      
       TWxApi
      
      
       .
      
      
       Create
      
      
       ;
      
      
      
       
        begin
       
      
      
      
       
        inherited
       
      
      
       ;
      
      
      
       
        
         // 文件不需要真实存在
        
       
      
      
      
       FileMustExist
      
      
       :=
      
      
       False
      
      
       ;
      
      
      
       
        end
       
      
      
       ;
      
      
       
        function
       
      
      
       TWxApi
      
      
       .
      
      
       Execute
      
      
       (
      
      
       aRequest
      
      
       :
      
      
       THttpRequest
      
      
       ;
      
      
       aReply
      
      
       :
      
      
       THttpReply
      
      
       ;
      
      
      
       
        const
       
      
      
       aPathname
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
       aSession
      
      
       :
      
      
       TIWApplication
      
      
       ;
      
      
      
       aParams
      
      
       :
      
      
       TStrings
      
      
       ):
      
      
       Boolean
      
      
       ;
      
      
      
       
        var
       
      
      
      
       signature
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
      
       timestamp
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
      
       nonce
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
      
       echostr
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
      
       strs
      
      
       :
      
      
       TStringList
      
      
       ;
      
      
      
       tmpStr
      
      
       :
      
      
       
        string
       
      
      
       ;
      
      
      
       
        begin
       
      
      
      
       Result
      
      
       :=
      
      
       True
      
      
       ;
      
      
       signature
      
      
       :=
      
      
       aParams
      
      
       .
      
      
       Values
      
      
       [
      
      
       
        ‘signature’
       
      
      
       ];
      
      
      
       timestamp
      
      
       :=
      
      
       aParams
      
      
       .
      
      
       Values
      
      
       [
      
      
       
        ‘timestamp’
       
      
      
       ];
      
      
      
       nonce
      
      
       :=
      
      
       aParams
      
      
       .
      
      
       Values
      
      
       [
      
      
       
        ‘nonce’
       
      
      
       ];
      
      
      
       echostr
      
      
       :=
      
      
       aParams
      
      
       .
      
      
       Values
      
      
       [
      
      
       
        ‘echostr’
       
      
      
       ];
      
      
       strs
      
      
       :=
      
      
       TStringList
      
      
       .
      
      
       Create
      
      
       ;
      
      
      
       strs
      
      
       .
      
      
       Add
      
      
       (
      
      
       
        ‘MyTestToken’
       
      
      
       );
      
      
       
        
         // Token,要和微信接口配置信息保持一致
        
       
      
      
      
       strs
      
      
       .
      
      
       Add
      
      
       (
      
      
       timestamp
      
      
       );
      
      
      
       strs
      
      
       .
      
      
       Add
      
      
       (
      
      
       nonce
      
      
       );
      
      
      
       strs
      
      
       .
      
      
       Sort
      
      
       ;
      
      
       tmpStr
      
      
       :=
      
      
       strs
      
      
       [
      
      
       0
      
      
       ]+
      
      
       strs
      
      
       [
      
      
       1
      
      
       ]+
      
      
       strs
      
      
       [
      
      
       2
      
      
       ];
      
      
      
       tmpStr
      
      
       :=
      
      
       SHA1
      
      
       (
      
      
       AnsiString
      
      
       (
      
      
       tmpstr
      
      
       ));
      
      
       
        if
       
      
      
       tmpStr
      
      
       =
      
      
       signature
      
      
       
        then
       
      
      
      
       
        begin
       
      
      
      
       aReply
      
      
       .
      
      
       WriteString
      
      
       (
      
      
       echostr
      
      
       )
      
      
      
       
        end
       
      
      
       
        else
       
      
      
       
        begin
       
      
      
      
       aReply
      
      
       .
      
      
       WriteString
      
      
       (
      
      
       
        ‘如果看到这个提示说明此链接地址可作为微信接口地址使用。’
       
      
      
       );
      
      
      
       
        end
       
      
      
       ;
      
      
      
       aSession
      
      
       .
      
      
       Terminate
      
      
       ;
      
      
       
        
         // 释放会话资源,本handler无需会话
        
       
      
      
      
       
        end
       
      
      
       ;
      
      
       
        end
       
      
      
       .
      
     
    
   
    
     如代码所示,TContentBase即iw版httphandler的基础类,如果不需要显示iwForm,从这个类型继承下来即可。如果需要使用iwForm还有个TContentForm基础类可以使用,哇哦,在浏览器地址栏输入url也可以直接打开iwform了哦。
     
      TWxApi
     
     
      .
     
     
      Execute中的代码即微信接入的代码,非常简单,不了解的看看微信帮助:
      
       微信接入指南
      
      。
     
    
   
    
     
      
       3、
      
     
     在ServerController中注册此httphandler,直接贴出ServerController注册代码:
    
    
     
      
      
       
        procedure
       
      
      
       TIWServerController
      
      
       .
      
      
       IWServerControllerBaseConfig
      
      
       (
      
      
       Sender
      
      
       :
      
      
       TObject
      
      
       );
      
      
      
       
        begin
       
      
      
      
       
        
         // 在ServerController.OnConfig事件中注册我们定义的微信Handler
        
       
      
      
      
       
        
         // ServerController.OnConfig事件在整个应用程序生命周期中只被运行一次
        
       
      
      
      
       
        
         with
        
       
       
        THandlers
       
       
        .
       
       
        Add
       
       
        (
       
       
        ”
       
       
        ,
       
       
        ‘wxapi.php’
       
       
        ,
       
       
        TWxApi
       
       
        .
       
       
        Create
       
       
        )
       
       
        
         do
        
       
      
      
      
       
        begin
       
      
      
      
       CanStartSession
      
      
       :=
      
      
       True
      
      
       ;
      
      
       
        
         // 从字面上理解是能够启动会话
        
       
      
      
      
       RequiresSessionStart
      
      
       :=
      
      
       False
      
      
       ;
      
      
       
        
         // 从字面上理解是需要启动会话,这两个属性必须进行设置,否则输入 /wxapi.php 将转向主窗体
        
       
      
      
      
       
        
         // 也就是不设置CanStartSession和RequiresSessionStart,则必须先执行/$/start 启动会话后才
        
       
      
      
      
       
        
         // 能正常访问 /wxapi.php页面,这个显然不是我们需要的。
        
       
      
      
      
       
        end
       
      
      
       ;
      
      
      
       
        end
       
      
      
       ;
      
     
    
   
    
     
      如代码所示,红色的
      
       
        THandlers
       
       
        .
       
       
        Add
       
       
        (
       
       
        ”
       
       
        ,
       
       
        ‘wxapi.php’
       
       
        ,
       
       
        TWxApi
       
       
        .
       
       
        Create
       
       
        )
        
         这段代码就完成了httphandler的注册,在浏览器地址栏输入
         
          http://localhost/wxapi.php
         
         即可访问刚刚注册的这个控制器。
        
       
      
     
    
   
    
     但是这段代码中有个不大不小的坑,详见我代码中的注释。
    
   
    
     
      
       
        
         这个坑是handler注册后启动iw应用但是不先在浏览器地址栏输入/$/start启动程序,而是直接输入/wxapi.php验证httphandler时失败,页面自动导航到主窗体!阅读帮助后发现,需要设置TContentBase.
         
          RequiresSessionStart:=false否则会导致iw应用必须先启动会话访问主窗体才能使用httphandler,按照帮助的说明设置了TContentBase.
          
           RequiresSessionStart:=false虽然直接输入/wxapi.php不再导航到主窗体了,但是会提示404代码错误,单步跟踪发现httphandler代码确实已经执行了,这样不应该出现404错误,多方查证和实验发现还需要设置TContentBase.
           
            CanStartSession
           
           
            :=
           
           
            True,呵呵,这个在帮助中没有提到,估计是新版本新增加的属性。好了,现在在地址栏输入
            
             http://localhost/wxapi.php
            
            能正常打开页面了。
           
          
         
        
       
      
     
    
   
     
   
    
     
      4、
     
    
    将编译完成的iw应用拷贝到主机上进行测试,实战微信接入
   
    
    ,居然提示配置失败!这是什么情况,我使用的代码是从别人写好的一个Delphi版微信接入接口代码中拷贝的,那个程序相同的代码没有任何问题,当时第一个念头是页面编码不正确,好吧,我改,iw的handler默认的编码是utf-8格式的,于是我尝试了gbk,iso-8859-1等多种编码格式,均提示上述错误。万般无奈只好写日志看看到底handler代码执行没有,结果令人震惊,放到真实服务器上后,微信验证时handler代码居然没有执行,而在我本机调试时和真实服务器的浏览器上浏览时都很好没有任何问题。巨大的坑啊,连着好几天各种测试,各种修改,都准备放弃了,呵呵,结果看了下iw自己的httphandler示例,发现在其ServerController中实现了一个事件:
    
     OnBrowserCheck
    
    ,于是尝试在自己代码中增加了相同事件代码进行测试,哇塞,可以了。。。
   
    
     
      
       
        procedure
       
      
      
       TIWServerController
      
      
       .
      
      
       IWServerControllerBaseBrowserCheck
      
      
       (
      
      
      
       aSession
      
      
       :
      
      
       TIWApplication
      
      
       ;
      
      
       
        var
       
      
      
       rBrowser
      
      
       :
      
      
       TBrowser
      
      
       );
      
      
      
       
        begin
       
      
      
      
       
        
         // 这个事件代码很重要,我在这里卡了好几天!
        
       
      
      
      
       
        
         //
        
       
      
      
      
       
        
         // 在没有实现这个事件的时候,在任何浏览器输入 /wxapi.php 都能成功响应,唯独到了
        
       
      
      
      
       
        
         // 微信中就显示配置失败,后来在代码中使用了日志输出才发现iw能收到微信请求,但是
        
       
      
      
      
       
        
         // TWxApi.Execute方法却没有执行,后来去官网阅读了相关帮助,才发现iw只有支持的浏
        
       
      
      
      
       
        
         // 览器才可正常响应输出,而微信发出的web请求显然不属于任何一个已知的浏览器
        
       
      
      
      
       
        if
       
      
      
       rBrowser
      
      
       
        is
       
      
      
       TOther
      
      
       
        then
       
      
      
       
        begin
       
      
      
      
       rBrowser
      
      
       .
      
      
       Free
      
      
       ;
      
      
      
       rBrowser
      
      
       :=
      
      
       TInternetExplorer
      
      
       .
      
      
       Create
      
      
       (
      
      
       8
      
      
       );
      
      
       
        
         // 以兼容IE8页面浏览进行页面内容输出
        
       
      
      
      
       
        end
       
      
      
       ;
      
      
      
       
        end
       
      
      
       ;
      
     
    
   
    
    ,呵呵,太不容易了,就这么几行代码折腾好几天,不过iw终于可以用于微信开发了。
   
我认为Delphi的强大在于除编译器外所有源代码均提供,这样出现问题了可以通过阅读源码解决,但是iw太封闭了,没有源码就算了,帮助也跟不上,在线那个帮助实在太太太弱了,建议用iw的朋友结合在线帮助和iw自己的示例工程一起看,少走弯路!不过,iw发展到今天确实已经挺好用了,尤其对于有delphi情节的人,能用自己最擅长的语言和开发工具进行web开发真是挺爽的一件事情。
未完,待续。。。
下一次讲讲iw的最新部署方式,以.net mvc4方式部署在iis上,呵呵,如果只看iw在线帮助关于mvc方式部署就去实践,也有小坑哦!
 
