一、采用IdHTTPServer
unit fun_send; interface uses Winapi.Windows, System.Classes, IPPeerClient, REST.Client, REST.Types, System.JSON; function rest_get(server_url: string; send_name_strlist: TStringList; send_info_strlist: TStringList): string; function rest_post(server_url: string; send_json: TJSONObject;json_name:string): string; implementation function rest_get(server_url: string; send_name_strlist: TStringList; send_info_strlist: TStringList): string; var i: integer; // 发送用 temp_RESTClient1: TRESTClient; temp_RESTResponse1: TRESTResponse; temp_RESTRequest1: TRESTRequest; //接收用 recv_str: string; begin // 发送 temp_RESTClient1 := TRESTClient.Create(nil); temp_RESTClient1.HandleRedirects := true; temp_RESTResponse1 := TRESTResponse.Create(nil); temp_RESTRequest1 := TRESTRequest.Create(nil); temp_RESTRequest1.Client := temp_RESTClient1; temp_RESTRequest1.Method := rmGET; temp_RESTRequest1.Response := temp_RESTResponse1; temp_RESTRequest1.SynchronizedEvents := false; temp_RESTClient1.BaseURL := server_url; temp_RESTRequest1.Params.Clear; for i := 0 to send_name_strlist.Count - 1 do begin temp_RESTRequest1.AddParameter(send_name_strlist[i], send_info_strlist[i]); end; try temp_RESTRequest1.Execute; recv_str := temp_RESTResponse1.Content; except recv_str := '提交失败'; end; temp_RESTClient1.Free; temp_RESTRequest1.Free; temp_RESTResponse1.Free; result := recv_str; end; function rest_post(server_url: string; send_json: TJSONObject;json_name:string): string; var i: integer; // 发送用 temp_RESTClient1: TRESTClient; temp_RESTResponse1: TRESTResponse; temp_RESTRequest1: TRESTRequest; //接收用 recv_str: string; begin // 发送 temp_RESTClient1 := TRESTClient.Create(nil); temp_RESTClient1.HandleRedirects := true; temp_RESTResponse1 := TRESTResponse.Create(nil); temp_RESTRequest1 := TRESTRequest.Create(nil); temp_RESTRequest1.Client := temp_RESTClient1; temp_RESTRequest1.Method := rmPOST; temp_RESTRequest1.Response := temp_RESTResponse1; temp_RESTRequest1.SynchronizedEvents := false; temp_RESTClient1.BaseURL := server_url; temp_RESTRequest1.Params.Clear; temp_RESTRequest1.AddParameter(json_name, send_json); try temp_RESTRequest1.Execute; recv_str := temp_RESTResponse1.Content; except recv_str := '提交失败'; end; temp_RESTClient1.Free; temp_RESTRequest1.Free; temp_RESTResponse1.Free; result := recv_str; end; end.二、主窗体代码
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.JSON, Vcl.ExtCtrls, Vcl.StdCtrls, fun_send, IdContext, Web.HTTPApp, IdCustomHTTPServer, IdBaseComponent, IdComponent, IdCustomTCPServer, IdHTTPServer, DCPcrypt2, DCPblockciphers, DCPdes, DCPsha256,IdHashSHA; type Pwx_info_in = ^Twx_info_in; Twx_info_in = record url: string; AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo end; type TForm1 = class(TForm) GroupBox1: TGroupBox; GroupBox2: TGroupBox; Label3: TLabel; Label4: TLabel; Edit4: TEdit; Edit5: TEdit; Button3: TButton; Button2: TButton; Label1: TLabel; Edit2: TEdit; Label2: TLabel; Edit3: TEdit; Timer1: TTimer; GroupBox3: TGroupBox; Button1: TButton; Edit1: TEdit; CheckBox1: TCheckBox; IdHTTPServer1: TIdHTTPServer; Memo1: TMemo; DCP_sha2561: TDCP_sha256; DCP_des1: TDCP_des; DCP_des2: TDCP_des; procedure Button2Click(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin // 设置绑定参数 IdHTTPServer1.Bindings.Clear; IdHTTPServer1.DefaultPort := StrToInt(edit1.Text); IdHTTPServer1.Bindings.Add.IP := '0.0.0.0'; // 启动服务器 IdHTTPServer1.Active := True; end; procedure TForm1.Button2Click(Sender: TObject); var send_name_strlist: TStringList; send_info_strlist: TStringList; i: integer; recv_str: string; //接收用 recv_json: tjsonobject; //临时生成的 recv_jv: tjsonvalue; recv_jv_num: TJSONNumber; access_token: string; expires_in: integer; begin //关闭有效期定时器 Timer1.Enabled := false; send_name_strlist := TStringList.Create; send_info_strlist := TStringList.Create; send_name_strlist.Add('grant_type'); send_info_strlist.Add('client_credential'); send_name_strlist.Add('appid'); send_info_strlist.Add(Edit4.Text); send_name_strlist.Add('secret'); send_info_strlist.Add(Edit5.Text); recv_str := rest_get('https://api.weixin.qq.com/cgi-bin/token', send_name_strlist, send_info_strlist); // recv_str := AnsiToUtf8(recv_str); //接收 recv_json := tjsonobject.Create; recv_json := TJSONObject.parsejsonvalue(tencoding.utf8.getbytes(recv_str), 0) as TJSONObject; recv_jv := recv_json.get('access_token').jsonvalue; access_token := recv_jv.Value; recv_jv_num := recv_json.get('expires_in').JsonValue as TJSONNumber; expires_in := recv_jv_num.AsInt; //接收完成 Edit2.Text := access_token; Edit3.Text := IntToStr(expires_in); send_name_strlist.Free; send_info_strlist.Free; //打开有效期定时器 Edit4.Text := ''; Edit5.Text := ''; Timer1.Enabled := True; end; procedure TForm1.Button3Click(Sender: TObject); begin Edit4.Text := ''; Edit5.Text := ''; end; procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); procedure wx_fun(var wx_info_in: Twx_info_in); function wx_access(var wx_info_in: Twx_info_in): string; function sha1(input: string): string; begin with tidhashsha1.create do try result := HashStringAsHex(input); finally free; end; end; function checktoken(token, signature, timestamp, nonce, echostr: string): string; var s, s1: string; tmp: TStringList; begin tmp := TStringList.Create; try tmp.Delimiter := ','; s := token + ',' + timestamp + ',' + nonce; tmp.DelimitedText := s; tmp.Sorted := true; s := tmp.Strings[0] + tmp.Strings[1] + tmp.Strings[2]; s1 := sha1(s); if s1.ToUpper = signature.ToUpper then Result := echostr else Result := 'error'; finally tmp.Free; end; end; var url: string; AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; //提取参数 recv_signature: string; recv_timestamp: string; recv_nonce: string; recv_echostr: string; temp_str: string; RespStr: string; begin url := wx_info_in.url; AContext := wx_info_in.AContext; ARequestInfo := wx_info_in.ARequestInfo; AResponseInfo := wx_info_in.AResponseInfo; //微信认证 recv_signature := ARequestInfo.Params.Values['signature']; recv_timestamp := ARequestInfo.Params.Values['timestamp']; recv_nonce := ARequestInfo.Params.Values['nonce']; recv_echostr := ARequestInfo.Params.Values['echostr']; temp_str := checktoken('微信网站上设置的 token', recv_signature, recv_timestamp, recv_nonce, recv_echostr); //生成返回 AResponseInfo.ContentType := 'text/HTML;charset=utf-8'; RespStr := temp_str; AResponseInfo.ContentText := AnsiToUtf8(RespStr); end; var url: string; AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; begin Form1.Memo1.Lines.Add('处理微信函数'); url := wx_info_in.url; AContext := wx_info_in.AContext; ARequestInfo := wx_info_in.ARequestInfo; AResponseInfo := wx_info_in.AResponseInfo; if Form1.CheckBox1.Checked = true then begin Form1.Memo1.Lines.Add('接收到微信消息类型为:' + ARequestInfo.Command); Form1.Memo1.Lines.Add('访问模块为:' + url); end; if (url = '/Twx/Rwx') or (url = '/Twx/Rwx/') then begin //微信认证 if ARequestInfo.Command = 'GET' then begin wx_access(wx_info_in) end; //微信POST消息 if ARequestInfo.Command = 'POST' then // post begin if Form1.CheckBox1.Checked = true then begin Form1.Memo1.Lines.Add('接收到微信POST消息'); end; //wx_recv_post(wx_info_in); //返回微信消息 end; end; end; var RespStr, recvText: string; msgText: string; recv_url: string; //访问模块 wx_info_in: Twx_info_in; Buf: TBytes; temp_str: string; Stream: TStream; begin try try //获取在访问的模块 recv_url := ARequestInfo.Document; //调试模式下显示收到内容 if CheckBox1.Checked = true then begin if ARequestInfo.Command = 'POST' then // post begin if (ARequestInfo.PostStream <> nil) and (ARequestInfo.PostStream.Size > 0) then begin end; end; if ARequestInfo.Command = 'GET' then begin // Memo1.Lines.Add(Httpdecode(ARequestInfo.QueryParams)); // 引用 Httpapp end; end; if Form1.CheckBox1.Checked = true then begin Form1.Memo1.Lines.Add('接收到访问类型为:' + ARequestInfo.Command + ' 访问模块为:' + recv_url + ' 访问时间为:' + DateTimeToStr(Now())); Form1.Memo1.Lines.Add('访问者IP:' + AContext.Connection.Socket.Binding.PeerIP); if ARequestInfo.Command = 'GET' then begin Memo1.Lines.Add('地址栏参数:' + Httpdecode(ARequestInfo.QueryParams)); end; end; //如果是微信模块 if (recv_url = '/Twx/Rwx') or (recv_url = '/Twx/Rwx/') then begin wx_info_in.url := recv_url; wx_info_in.AContext := AContext; wx_info_in.ARequestInfo := ARequestInfo; wx_info_in.AResponseInfo := AResponseInfo; wx_fun(wx_info_in); end else begin AResponseInfo.ContentType := 'text/HTML;charset=utf-8'; RespStr := '<html><body>只是一个练习界面,什么功能都没有。</body></html>'; AResponseInfo.ContentText := AnsiToUtf8(RespStr); end; except end; finally end; end; procedure TForm1.Timer1Timer(Sender: TObject); begin Edit3.Text := IntToStr(StrToInt(Edit3.Text) - 60); end; end.三、编辑界面
四、说明
1、DCP是一套控件,与微信服务器验证的时候用的。
2、附赠一个单元
unit fun_change; interface uses Winapi.Windows, System.SysUtils, System.Classes, fun_wx_recv, Xml.xmldom, Xml.XMLIntf, Xml.XMLDoc, Xml.omnixmldom, System.DateUtils; function format_Json_show(indent: string; inputStr: string): string; function StreamToString(mStream: TStream): string; //拆分微信POST消息 function split_wx_mess(xml: string; var wx_mess_Recv: Twx_mess_Recv): Boolean; //初始化被动回复消息 function reset_wx_mess_send(wx_mess_Recv: Twx_mess_Recv; var wx_mess_send: Twx_mess_send): boolean; //获得时间戳 function Get_Stamp_Time(d: TDateTime): Int64; //生成返回的XML function get_wx_mess_xml(wx_mess_send: Twx_mess_send): string; implementation function Get_Stamp_Time(d: TDateTime): Int64; var dJavaStart: TDateTime; begin //java里的时间是从1970年1月1日0点到当前的间隔 dJavaStart := EncodeDateTime(1970, 1, 1, 0, 0, 0, 0); Result := MilliSecondsBetween(d, dJavaStart); end; function format_Json_show(indent: string; inputStr: string): string; var outStr, s: string; p, i: integer; c: char; level: integer; //缩进级别 quot: integer; //双引号标记 slant: integer; //反斜杠标记 colon: integer; //冒号 function getTab(level: integer): string; var tab: string; j: integer; begin getTab := ''; if level > 0 then begin for j := 1 to level do begin tab := tab + indent; end; end; result := tab; end; begin //去掉换行回车符 inputStr := stringReplace(inputStr, #13#10, '', [rfReplaceAll, rfIgnoreCase]); //去掉tab符 inputStr := stringReplace(inputStr, #9, '', [rfReplaceAll, rfIgnoreCase]); //支持 xxx={...} 格式的数据,保留 xxx= 内容。 p := pos('{', inputStr); if p > 0 then begin outStr := copy(inputStr, 1, p - 1); inputStr := copy(inputStr, p, length(inputStr)); end else begin result := inputStr; exit; end; //json格式化处理 //简易处理规则: //遇到反斜杠 "\",输出,后面紧跟的字符直接输出,不做特殊处理 //遇到双引号 """,输出,等待匹配下一个双引号(除了反斜杠"\"后的双引号外),其间的字符直接输出 //遇到左花括号 "{" 缩进不变输出,回车,后续缩进等级+1 //遇到右花括号 "}" 回车,缩进-1, 输出,后续缩进等级-1 //遇到左方括号 "[" 缩进不变输出,回车,后续缩进等级+1 //遇到右方括号 "]" 回车,缩进-1, 输出,后续缩进等级-1 //遇到双引号外的逗号 "," 输出后回车 //遇到冒号 ":", 输出,加一个空格 //不符合以上规则的字符,除空格外,直接 输出 level := 0; quot := 0; //是否等待匹配双引号 colon := 0; for i := 1 to length(inputStr) do begin c := inputStr[i]; if c <> ' ' then s := c else s := ''; //过滤一般性空格 if (slant = 1) then begin //反斜杠之后的字符直接输出 slant := 0; end else if (quot = 1) and (c <> '"') and (c <> '\') then begin //双引号之后的字符直接输出 s := c; //双引号之间的空格也保留输出 end else begin case c of '\': begin slant := 1; end; '{': begin if colon <> 1 then s := getTab(level) + s; s := s + #13#10; level := level + 1; colon := 0; end; '}': begin s := #13#10 + getTab(level - 1) + s; level := level - 1; end; '[': begin if colon <> 1 then s := getTab(level) + s; s := s + #13#10; level := level + 1; colon := 0; end; ']': begin s := #13#10 + getTab(level - 1) + s; level := level - 1; end; '"': begin quot := 1 - quot; if (quot = 1) and (colon = 0) then s := getTab(level) + s; colon := 0; end; ',': begin s := s + #13#10; colon := 0; end; ':': begin s := s + ' '; colon := 1; end; else // end; end; outStr := outStr + s; end; result := outStr; end; function StreamToString(mStream: TStream): string; { 将内存流转换成字符串 } var I: Integer; begin Result := ''; if not Assigned(mStream) then Exit; SetLength(Result, mStream.Size); for I := 0 to Pred(mStream.Size) do try mStream.Position := I; mStream.Read(Result[Succ(I)], 1); except Result := ''; end; end; { StreamToString } function reset_wx_mess_send(wx_mess_Recv: Twx_mess_Recv; var wx_mess_send: Twx_mess_send): boolean; begin wx_mess_send.ToUserName := wx_mess_Recv.FromUserName; wx_mess_send.FromUserName := wx_mess_Recv.ToUserName; wx_mess_send.CreateTime := IntToStr(Get_Stamp_Time(now)); wx_mess_send.MsgType := 'text'; wx_mess_send.Content := '测试一下。' + chr(13) + chr(10) + '看看能不能收到'; end; function split_wx_mess(xml: string; var wx_mess_Recv: Twx_mess_Recv): Boolean; var Rootnode, node: IXmlNode; xml1: TXMLDocument; doc: IXMLDocument; begin xml1 := TXMLDocument.Create(nil); try xml1.DOMVendor := GetDOMVendor('Omni XML'); doc := xml1; doc.XML.Text := xml; doc.Active := true; Rootnode := doc.DocumentElement; node := Rootnode.ChildNodes.FindNode('ToUserName'); if node <> nil then wx_mess_Recv.ToUserName := node.Text; node := Rootnode.ChildNodes.FindNode('FromUserName'); if node <> nil then wx_mess_Recv.FromUserName := node.Text; node := Rootnode.ChildNodes.FindNode('CreateTime'); if node <> nil then wx_mess_Recv.CreateTime := node.Text; node := Rootnode.ChildNodes.FindNode('MsgType'); if node <> nil then wx_mess_Recv.MsgType := node.Text; node := Rootnode.ChildNodes.FindNode('Content'); if node <> nil then wx_mess_Recv.Content := node.Text; node := Rootnode.ChildNodes.FindNode('MediaId'); if node <> nil then wx_mess_Recv.MediaId := node.Text; node := Rootnode.ChildNodes.FindNode('PicUrl'); if node <> nil then wx_mess_Recv.PicUrl := node.Text; node := Rootnode.ChildNodes.FindNode('Format'); if node <> nil then wx_mess_Recv.Format := node.Text; node := Rootnode.ChildNodes.FindNode('ThumbMediaId'); if node <> nil then wx_mess_Recv.ThumbMediaId := node.Text; node := Rootnode.ChildNodes.FindNode('MsgId'); if node <> nil then wx_mess_Recv.MsgId := node.Text; node := Rootnode.ChildNodes.FindNode('Event'); if node <> nil then wx_mess_Recv.Event := node.Text; node := Rootnode.ChildNodes.FindNode('EventKey'); if node <> nil then wx_mess_Recv.EventKey := node.Text; node := Rootnode.ChildNodes.FindNode('Ticket'); if node <> nil then wx_mess_Recv.Ticket := node.Text; node := Rootnode.ChildNodes.FindNode('Latitude'); if node <> nil then wx_mess_Recv.Latitude := node.Text; node := Rootnode.ChildNodes.FindNode('Longitude'); if node <> nil then wx_mess_Recv.Longitude := node.Text; node := Rootnode.ChildNodes.FindNode('Precision'); if node <> nil then wx_mess_Recv.Precision := node.Text; node := Rootnode.ChildNodes.FindNode('Location_X'); if node <> nil then wx_mess_Recv.Location_X := node.Text; node := Rootnode.ChildNodes.FindNode('Location_Y'); if node <> nil then wx_mess_Recv.Location_Y := node.Text; node := Rootnode.ChildNodes.FindNode('Scale'); if node <> nil then wx_mess_Recv.Scale := node.Text; node := Rootnode.ChildNodes.FindNode('Label'); if node <> nil then wx_mess_Recv.Label_ := node.Text; finally // xml1.Active := false; //xml1.Free; end; Result := true; end; function CDATA(value: string): string; begin Result := '<![CDATA[' + value + ']]>'; end; function get_wx_mess_xml(wx_mess_send: Twx_mess_send): string; var xml1: TXmlDocument; doc: IXMLDocument; Rootnode, node, node1, node2: IXmlNode; xml: string; begin try try xml1 := TXMLDocument.Create(nil); xml1.DOMVendor := GetDOMVendor('Omni XML'); doc := xml1; doc.Active := true; doc.AddChild('xml'); Rootnode := doc.DocumentElement; node := Rootnode.AddChild('ToUserName'); node.Text := CDATA(wx_mess_send.ToUserName); node := Rootnode.AddChild('FromUserName'); node.Text := CDATA(wx_mess_send.FromUserName); node := Rootnode.AddChild('CreateTime'); node.Text := CDATA(wx_mess_send.CreateTime); node := Rootnode.AddChild('MsgType'); node.Text := CDATA(wx_mess_send.MsgType); if wx_mess_send.MsgType = 'text' then begin node := Rootnode.AddChild('Content'); node.Text := CDATA(wx_mess_send.Content); end; if wx_mess_send.MsgType = 'Image' then begin node := Rootnode.AddChild('Image'); node1 := node.AddChild('MediaId'); node1.Text := CDATA(wx_mess_send.Image_MediaId) end; xml := doc.XML.Text; xml := xml.Replace('<', '<').Replace('>', '>'); Result := xml; except Result := ''; end; finally // doc.Free; end; end; end.
