“Delphi中的SOAP客户端”;对于请求的操作,句柄处于错误状态”;
我已经在VisualStudio中构建了世界上最愚蠢、最简单的SOAP服务器,只需点击大约3次。VisualStudio2010中的确切步骤是:首先创建一个新项目作为web应用程序,然后添加一个web服务类型的新项。(有关图片,请参阅接受的答案。)soap服务器服务Service1有一个简单的方法GetData: clientService1.pas中的一个片段,使用WSDL导入器创建“Delphi中的SOAP客户端”;对于请求的操作,句柄处于错误状态”;,delphi,soap,delphi-xe,Delphi,Soap,Delphi Xe,我已经在VisualStudio中构建了世界上最愚蠢、最简单的SOAP服务器,只需点击大约3次。VisualStudio2010中的确切步骤是:首先创建一个新项目作为web应用程序,然后添加一个web服务类型的新项。(有关图片,请参阅接受的答案。)soap服务器服务Service1有一个简单的方法GetData: clientService1.pas中的一个片段,使用WSDL导入器创建 IService1 = interface(IInvokable) ['{967498E8-4F67-
IService1 = interface(IInvokable)
['{967498E8-4F67-AAA5-A38F-F74D8C7E346A}']
function GetData(const value: Integer): string; stdcall;
function GetDataUsingDataContract(const composite: CompositeType2): CompositeType2; stdcall;
end;
当我尝试运行此方法时,如下所示:
procedure TForm3.Button1Click(Sender: TObject);
var
rio : THTTPRIO;
sv:IService1;
addr : string;
data : string;
begin
//addr := '....'; // url from visual studio 2010 live debug instance.
rio := THTTPRIO.Create(nil);
sv := GetIService1( true, addr, rio );
try
data := sv.GetData( 0);
Button1.Caption := data;
finally
sv := nil;
rio.Free;
end;
end;
我得到的错误是:
ESOAPHTTPException:
The handle is in the wrong state for the requested operation -
URL:http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/ -
SOAPAction:http://tempuri.org/IService1/GetData'.
当我将上面的URL粘贴到web浏览器中时,URL工作正常,因此通常的答案是Delphi中的SOAP代码不会注意到HTTP失败,这似乎不太可能。相反,看起来我要么(a)在WinInet中遇到了破坏(已知在某些windows版本中会发生),要么(b)做错了什么
在我看来,安装了VisualStudio和delphi的任何人都应该能够尝试让VisualStudio中的虚拟starter Soap服务器与delphi中的Soap客户机对话,而无需任何努力。但我想不出最简单的事情。有一次,Embarcadero的工作人员布鲁诺·巴比特(Bruneau Babet)讨论了一段对话中的错误,这段对话早就从Embarcadero论坛上删除了 布鲁诺说: 你好, 我发布了SOAPHTTPTrans.pas的补丁版本,其中包含一个补丁 关于本期: [论坛链接被编辑,不管怎么说都不起作用了,帖子不见了] 您仍然可以按照C++Builder中的描述重写该事件 所指章节;或者,简单得多,至少对于Delphi用户来说,简单得多 将更新的SOAPHTTPTrans.pas添加到应用程序的项目中。让我们知道 如果这对你不起作用 干杯 布鲁诺 您可以从以下内容或其他内容中获得原始论坛格式的修复和注释,这样您就不必从周围的文本中提取文件 Warren Update 2016:有人告诉我,试图在Delphi XE上使用该修复程序,但该修复程序在Delphi XE中不适用。如能对bitbucket中的代码进行任何进一步更新,以解决剩余的bug,我们将不胜感激。我在2018年11月使用Delphi Tokyo 10.2.3遇到了一个问题:对于请求的操作问题,句柄处于错误状态,然后查看了下面的代码补丁 该代码非常旧,测试代码不再工作(SOAP服务不可用)。此外,从布鲁诺的代码中还不清楚他到底修补了什么 将该源代码与我的Delphi版本中的源代码进行比较,似乎这是
HandleWinInetError
过程(“此处的补丁”)中所需的(两个)修改:
函数THTTPReqResp.HandleWinInetError(LastError:DWord;
请求:HINTERNET;
RaiseError:布尔):DWord;
函数CallInternetErrorDlg:DWord;
变量
P:指针;
开始
结果:=InternetErrorDlg(GetDesktopWindow(),请求,LastError,
标记\u错误\u用户界面\u过滤器\u查找\u错误或
标志\u错误\u用户界面\u标志\u更改\u选项或
标志\u错误\u用户界面\u标志\u生成\u数据,P);
{再次选择客户端证书发送请求后,
注意:InternetErrorDlg在使用调用时始终返回错误\u SUCCESS
错误\u INTERNET\u客户端\u需要身份验证\u证书\u}
如果LastError=ERROR\U INTERNET\U CLIENT\U AUTH\U CERT\U需要
结果:=错误\u INTERNET\u强制\u重试;
结束;
常数
{我们的WinInet当前缺少}
互联网选项客户端证书上下文=84;
变量
Flags,FlagsLen,DWCert,DWCertLen:DWord;
ClientCertInfo:IClientCertInfo;
CertSerialNum:字符串;
{$IFDEF客户端\证书\支持}
hStore:HCERTSTORE;
CertContext:PCERT_CONTEXT;
{$ENDIF}
开始
{分派到自定义处理程序(如果有)}
如果已分配(FOnWinInetError),则
结果:=FOnWinInetError(LastError,请求)
其他的
开始
结果:=错误\u INTERNET\u强制\u重试;
{谨慎地处理无效的\u CA}
如果(LastError=ERROR\u INTERNET\u INVALID\u CA)和(soIgnoreInvalidCerts in InvokeOptions),则
开始
FlagsLen:=SizeOf(标志);
InternetQueryOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
标志:=标志或安全标志\u忽略\u未知\u CA;
InternetSetOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
结束
否则,如果(LastError=ERROR\u INTERNET\u SEC\u CERT\u REV\u失败)和(调用选项中的soIgnoreInvalidCerts),则
开始
FlagsLen:=SizeOf(标志);
InternetQueryOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
标志:=标志或安全标志忽略撤销;
InternetSetOption(请求、INTERNET选项、安全标志、指针(@FLAGS)、FlagsLen);
结束
{$IFDEF客户端\证书\支持}
else if(LastError=ERROR\u INTERNET\u CLIENT\u AUTH\u CERT\u需要)和
支持(Self、IClientCertInfo、ClientCertInfo)和
(ClientCertInfo.GetCertSerialNumber“”)然后
开始
CertSerialNum:=ClientCertInfo.GetCertSerialNumber();
hStore:=ClientCertInfo.GetCertStore();
如果hStore=nil,则
开始
hStore:=CertOpenSystemStore(0,PChar('MY'));
ClientCertInfo.SetCertStore(hStore);
结束;
CertContext:=FindCertWithSerialNumber(hStore,CertSerialNum);
如果为零,则
开始
ClientCertInfo.SetCertContext(CertContext);
InternetSetOption(请求、INTERNET选项、客户端证书上下文、,
CertContext,SizeOf(CERT_CONTEXT));
结束
其他的
开始
如果RaiseError,则RaiseCheck(LastError);//在这里打补丁
结果:=CallInternetErrorDlg;
结束;
结束
{$ENDIF}
否则(最后)
function THTTPReqResp.HandleWinInetError(LastError: DWord;
Request: HINTERNET;
RaiseError: Boolean): DWord;
function CallInternetErrorDlg: DWord;
var
P: Pointer;
begin
Result := InternetErrorDlg(GetDesktopWindow(), Request, LastError,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS or
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);
{ After selecting client certificate send request again,
Note: InternetErrorDlg always returns ERROR_SUCCESS when called with
ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
if LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED then
Result := ERROR_INTERNET_FORCE_RETRY;
end;
const
{ Missing from our WinInet currently }
INTERNET_OPTION_CLIENT_CERT_CONTEXT = 84;
var
Flags, FlagsLen, DWCert, DWCertLen: DWord;
ClientCertInfo: IClientCertInfo;
CertSerialNum: string;
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
hStore: HCERTSTORE;
CertContext: PCERT_CONTEXT;
{$ENDIF}
begin
{ Dispatch to custom handler, if there's one }
if Assigned(FOnWinInetError) then
Result := FOnWinInetError(LastError, Request)
else
begin
Result := ERROR_INTERNET_FORCE_RETRY;
{ Handle INVALID_CA discreetly }
if (LastError = ERROR_INTERNET_INVALID_CA) and (soIgnoreInvalidCerts in InvokeOptions) then
begin
FlagsLen := SizeOf(Flags);
InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
Flags := Flags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
end
else if (LastError = ERROR_INTERNET_SEC_CERT_REV_FAILED) and (soIgnoreInvalidCerts in InvokeOptions) then
begin
FlagsLen := SizeOf(Flags);
InternetQueryOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
Flags := Flags or SECURITY_FLAG_IGNORE_REVOCATION;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_FLAGS, Pointer(@Flags), FlagsLen);
end
{$IFDEF CLIENT_CERTIFICATE_SUPPORT}
else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and
Supports(Self, IClientCertInfo, ClientCertInfo) and
(ClientCertInfo.GetCertSerialNumber <> '') then
begin
CertSerialNum := ClientCertInfo.GetCertSerialNumber();
hStore := ClientCertInfo.GetCertStore();
if hStore = nil then
begin
hStore := CertOpenSystemStore(0, PChar('MY'));
ClientCertInfo.SetCertStore(hStore);
end;
CertContext := FindCertWithSerialNumber(hStore, CertSerialNum);
if CertContext <> nil then
begin
ClientCertInfo.SetCertContext(CertContext);
InternetSetOption(Request, INTERNET_OPTION_CLIENT_CERT_CONTEXT,
CertContext, SizeOf(CERT_CONTEXT));
end
else
begin
if RaiseError then RaiseCheck(LastError); // PATCH HERE
Result := CallInternetErrorDlg;
end;
end
{$ENDIF}
else if (LastError = ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) and (soPickFirstClientCertificate in InvokeOptions) then
begin
{ This instructs WinInet to pick the first (a random?) client cerficate }
DWCertLen := SizeOf(DWCert);
DWCert := 0;
InternetSetOption(Request, INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT,
Pointer(@DWCert), DWCertLen);
end
else
begin
if RaiseError then RaiseCheck(LastError); // PATCH HERE
Result := CallInternetErrorDlg;
end;
end;
end;
Uses SOAP.SOAPHTTPTrans;
const Request2 =
'<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ndf="http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl">' +
' <soapenv:Header/>' +
' <soapenv:Body>' +
' <ndf:NDFDgenByDay soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' +
' <latitude xsi:type="xsd:decimal">38.9936</latitude>' +
' <longitude xsi:type="xsd:decimal">-77.0224</longitude>' +
' <startDate xsi:type="xsd:date">%tomorrow%</startDate>' +
' <numDays xsi:type="xsd:integer">5</numDays>' +
' <Unit xsi:type="dwml:unitType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">e</Unit>' +
' <format xsi:type="dwml:formatType" xmlns:dwml="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">12 hourly</format>' +
' </ndf:NDFDgenByDay>' +
' </soapenv:Body>' +
'</soapenv:Envelope>';
const URL2= 'https://graphical.weather.gov:443/xml/SOAP_server/ndfdXMLserver.php';
procedure TFrmHandleWinINetError.Button1Click(Sender: TObject);
var
RR: THTTPReqResp;
Response: TMemoryStream;
U8: UTF8String;
begin
RR := THTTPReqResp.Create(nil);
try
try
RR.URL := URL2;
RR.UseUTF8InHeader := True;
RR.SoapAction := 'NDFDgenByDay';
Response := TMemoryStream.Create;
RR.Execute(Request2, Response);
SetLength(U8, Response.Size);
Response.Position := 0;
Response.Read(U8[1], Length(U8));
ShowMessage(String(U8));
except
on E:Exception do ShowMessage('ERROR CAUGHT: ' + e.message);
end;
finally
Response.Free;
RR.Free;
end;
end;
end;