HTTP POST 与 Delphi 上的 Wininet API
HTTP POST with Wininet API on Delphi
我正在使用 Delphi 2010 向 Java 应用程序发送 HTTP 请求。具体来说,我正在发送一个 JSON 对象。但是,发送请求时,我不知道发生了什么,但对象不正确。
我这样发送对象:
{"entidad":"1","username":"A","password":"1234"}
我的嗅探器读取对象是这样的:
%�7�B�%�2�2�e�n�t�i�d�a�d�%�2�2�%�3�A�%�2�2�8�3�0�0�2�3�0�0�0�%�2�2�%�2�C�
因此,我的 Java 应用程序没有读取对象,它导致了空指针异常。
我的代码在这里:
function TFormMain.JSONPostRequest(Server,Url,jo : String; blnSSL: Boolean): String;
var
aBuffer : Array[0..4096] of Char;
Header : TStringStream;
BufStream : TMemoryStream;
BytesRead : Cardinal;
pSession : HINTERNET;
pConnection : HINTERNET;
pRequest : HINTERNET;
port : Integer;
flags : DWord;
begin
Result := '';
pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(pSession) then
try
if blnSSL then
Port := INTERNET_DEFAULT_HTTPS_PORT
else
Port := 9000;
pConnection := InternetConnect(pSession, PChar(Server), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
if Assigned(pConnection) then
try
if blnSSL then
flags := INTERNET_FLAG_SECURE or INTERNET_FLAG_KEEP_CONNECTION
else
flags := INTERNET_SERVICE_HTTP;
pRequest := HTTPOpenRequest(pConnection, 'POST', PChar(Url), nil, nil, nil, flags, 0);
if Assigned(pRequest) then
try
Header := TStringStream.Create('');
try
with Header do
begin
WriteString('Host: ' + Server + ':' + IntToStr(Port) + sLineBreak);
end;
HttpAddRequestHeaders(pRequest, PChar(Header.DataString), Length(Header.DataString), HTTP_ADDREQ_FLAG_ADD);
if HTTPSendRequest(pRequest, nil, 0, Pointer(jo), Length(jo)) then
begin
BufStream := TMemoryStream.Create;
try
while InternetReadFile(pRequest, @aBuffer, SizeOf(aBuffer), BytesRead) do
begin
if (BytesRead = 0) then Break;
BufStream.Write(aBuffer, BytesRead);
end;
aBuffer[0] := #0;
BufStream.Write(aBuffer, 1);
Result := WideCharToString(PChar(BufStream.Memory));
finally
BufStream.Free;
end;
end
else
raise Exception.Create('HttpOpenRequest failed. ' + SysErrorMessage(GetLastError));
finally
Header.Free;
end;
finally
InternetCloseHandle(pRequest);
end;
finally
InternetCloseHandle(pConnection);
end;
finally
InternetCloseHandle(pSession);
end;
end;
JSON 默认使用 UTF-8,Delphi(2009 及更新版本)使用 UTF-16 作为默认编码。您的代码需要先将 JSON 转换为 UTF-8 字符串,然后再将其传递给 HTTPSendRequest。
我还要加一个请求头来指明编码方式,这样Java端就知道是UTF-8了。
My sniffer reads the object like this:
%�7�B�%�2�2�e�n�t�i�d�a�d�%�2�2�%�3�A�%�2�2�8�3�0�0�2�3�0�0�0�%�2�2�%�2�C�
您的 JSON 数据以 UTF-16 编码,因为 string
是 Delphi 2010 中 UnicodeString
的别名。您发送的是原始字节字符串而不是将它们编码为 UTF-8,这是 JSON 的默认字符集。正如 mjn 所说,您在这方面滥用了 HttpSendRequest()
。
此外,您输入的 JSON 字符串在传递给 JSONPostRequest()
之前似乎已经过 url 编码(HTTPSendRequest()
没有 url 编码原始数据)。不要 url 对 JSON 进行编码,按原样传递原始 JSON。
此外,INTERNET_SERVICE_HTTP
不是 HTTPOpenRequest()
的有效标志。
您没有显示调用 JSONPostRequest()
的代码,因此我无法向您展示如何解决 url 编码问题。但是对于实际的 post:
尝试更像这样的东西
function WinInetErrorMsg(Err: DWORD): string;
var
ErrMsg: array of Char;
ErrLen: DWORD;
begin
if Err = ERROR_INTERNET_EXTENDED_ERROR then
begin
ErrLen := 0;
InternetGetLastResponseInfo(Err, nil, ErrLen);
if GetLastError() = ERROR_INSUFFICIENT_BUFFER then
begin
SetLength(ErMsg, ErrLen);
InternetGetLastResponseInfo(Err, PChar(ErMsg), ErrLen);
SetString(Result, PChar(ErrMsg), ErrLen);
end else begin
Result := 'Unknown WinInet error';
end;
end else
Result := SysErrorMessage(Err);
end;
function TFormMain.JSONPostRequest(const Server, Url: string; const jo : UTF8String; blnSSL: Boolean): String;
var
aBuffer : Array of Byte;
Header : String;
BufStream : TStringStream;
BytesRead : DWORD;
pSession : HINTERNET;
pConnection : HINTERNET;
pRequest : HINTERNET;
port : Integer;
flags : DWORD;
begin
Result := '';
pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if not Assigned(pSession) then
raise Exception.Create('InternetOpen failed. ' + WinInetErrorMsg(GetLastError));
try
if blnSSL then
Port := INTERNET_DEFAULT_HTTPS_PORT
else
Port := 9000;
pConnection := InternetConnect(pSession, PChar(Server), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
if not Assigned(pConnection) then
raise Exception.Create('InternetConnect failed. ' + WinInetErrorMsg(GetLastError));
try
if blnSSL then
flags := INTERNET_FLAG_SECURE
else
flags := 0;
pRequest := HTTPOpenRequest(pConnection, 'POST', PChar(Url), nil, nil, nil, flags, 0);
if not Assigned(pRequest) then
raise Exception.Create('HttpOpenRequest failed. ' + WinInetErrorMsg(GetLastError));
try
Header := 'Host: ' + Server + ':' + IntToStr(Port) + #13#10 +
'Content-Type: application/json; charset=UTF-8'#13#10;
if not HttpAddRequestHeaders(pRequest, PChar(Header), Length(Header), HTTP_ADDREQ_FLAG_ADD) then
raise Exception.Create('HttpAddRequestHeaders failed. ' + WinInetErrorMsg(GetLastError));
if not HTTPSendRequest(pRequest, nil, 0, PAnsiChar(jo), Length(jo)) then
raise Exception.Create('HTTPSendRequest failed. ' + WinInetErrorMsg(GetLastError));
SetLength(aBuffer, 4096);
BufStream := TStringStream.Create('', TEncoding.Default);
try
repeat
if not InternetReadFile(pRequest, PByte(aBuffer), Length(aBuffer), BytesRead) then
raise Exception.Create('InternetReadFile failed. ' + WinInetErrorMsg(GetLastError));
if (BytesRead = 0) then Break;
BufStream.WriteBuffer(PByte(aBuffer)^, BytesRead);
until False;
Result := BufStream.DataString;
finally
BufStream.Free;
end;
finally
InternetCloseHandle(pRequest);
end;
finally
InternetCloseHandle(pConnection);
end;
finally
InternetCloseHandle(pSession);
end;
end;
我正在使用 Delphi 2010 向 Java 应用程序发送 HTTP 请求。具体来说,我正在发送一个 JSON 对象。但是,发送请求时,我不知道发生了什么,但对象不正确。
我这样发送对象:
{"entidad":"1","username":"A","password":"1234"}
我的嗅探器读取对象是这样的:
%�7�B�%�2�2�e�n�t�i�d�a�d�%�2�2�%�3�A�%�2�2�8�3�0�0�2�3�0�0�0�%�2�2�%�2�C�
因此,我的 Java 应用程序没有读取对象,它导致了空指针异常。
我的代码在这里:
function TFormMain.JSONPostRequest(Server,Url,jo : String; blnSSL: Boolean): String;
var
aBuffer : Array[0..4096] of Char;
Header : TStringStream;
BufStream : TMemoryStream;
BytesRead : Cardinal;
pSession : HINTERNET;
pConnection : HINTERNET;
pRequest : HINTERNET;
port : Integer;
flags : DWord;
begin
Result := '';
pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(pSession) then
try
if blnSSL then
Port := INTERNET_DEFAULT_HTTPS_PORT
else
Port := 9000;
pConnection := InternetConnect(pSession, PChar(Server), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
if Assigned(pConnection) then
try
if blnSSL then
flags := INTERNET_FLAG_SECURE or INTERNET_FLAG_KEEP_CONNECTION
else
flags := INTERNET_SERVICE_HTTP;
pRequest := HTTPOpenRequest(pConnection, 'POST', PChar(Url), nil, nil, nil, flags, 0);
if Assigned(pRequest) then
try
Header := TStringStream.Create('');
try
with Header do
begin
WriteString('Host: ' + Server + ':' + IntToStr(Port) + sLineBreak);
end;
HttpAddRequestHeaders(pRequest, PChar(Header.DataString), Length(Header.DataString), HTTP_ADDREQ_FLAG_ADD);
if HTTPSendRequest(pRequest, nil, 0, Pointer(jo), Length(jo)) then
begin
BufStream := TMemoryStream.Create;
try
while InternetReadFile(pRequest, @aBuffer, SizeOf(aBuffer), BytesRead) do
begin
if (BytesRead = 0) then Break;
BufStream.Write(aBuffer, BytesRead);
end;
aBuffer[0] := #0;
BufStream.Write(aBuffer, 1);
Result := WideCharToString(PChar(BufStream.Memory));
finally
BufStream.Free;
end;
end
else
raise Exception.Create('HttpOpenRequest failed. ' + SysErrorMessage(GetLastError));
finally
Header.Free;
end;
finally
InternetCloseHandle(pRequest);
end;
finally
InternetCloseHandle(pConnection);
end;
finally
InternetCloseHandle(pSession);
end;
end;
JSON 默认使用 UTF-8,Delphi(2009 及更新版本)使用 UTF-16 作为默认编码。您的代码需要先将 JSON 转换为 UTF-8 字符串,然后再将其传递给 HTTPSendRequest。
我还要加一个请求头来指明编码方式,这样Java端就知道是UTF-8了。
My sniffer reads the object like this:
%�7�B�%�2�2�e�n�t�i�d�a�d�%�2�2�%�3�A�%�2�2�8�3�0�0�2�3�0�0�0�%�2�2�%�2�C�
您的 JSON 数据以 UTF-16 编码,因为 string
是 Delphi 2010 中 UnicodeString
的别名。您发送的是原始字节字符串而不是将它们编码为 UTF-8,这是 JSON 的默认字符集。正如 mjn 所说,您在这方面滥用了 HttpSendRequest()
。
此外,您输入的 JSON 字符串在传递给 JSONPostRequest()
之前似乎已经过 url 编码(HTTPSendRequest()
没有 url 编码原始数据)。不要 url 对 JSON 进行编码,按原样传递原始 JSON。
此外,INTERNET_SERVICE_HTTP
不是 HTTPOpenRequest()
的有效标志。
您没有显示调用 JSONPostRequest()
的代码,因此我无法向您展示如何解决 url 编码问题。但是对于实际的 post:
function WinInetErrorMsg(Err: DWORD): string;
var
ErrMsg: array of Char;
ErrLen: DWORD;
begin
if Err = ERROR_INTERNET_EXTENDED_ERROR then
begin
ErrLen := 0;
InternetGetLastResponseInfo(Err, nil, ErrLen);
if GetLastError() = ERROR_INSUFFICIENT_BUFFER then
begin
SetLength(ErMsg, ErrLen);
InternetGetLastResponseInfo(Err, PChar(ErMsg), ErrLen);
SetString(Result, PChar(ErrMsg), ErrLen);
end else begin
Result := 'Unknown WinInet error';
end;
end else
Result := SysErrorMessage(Err);
end;
function TFormMain.JSONPostRequest(const Server, Url: string; const jo : UTF8String; blnSSL: Boolean): String;
var
aBuffer : Array of Byte;
Header : String;
BufStream : TStringStream;
BytesRead : DWORD;
pSession : HINTERNET;
pConnection : HINTERNET;
pRequest : HINTERNET;
port : Integer;
flags : DWORD;
begin
Result := '';
pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if not Assigned(pSession) then
raise Exception.Create('InternetOpen failed. ' + WinInetErrorMsg(GetLastError));
try
if blnSSL then
Port := INTERNET_DEFAULT_HTTPS_PORT
else
Port := 9000;
pConnection := InternetConnect(pSession, PChar(Server), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
if not Assigned(pConnection) then
raise Exception.Create('InternetConnect failed. ' + WinInetErrorMsg(GetLastError));
try
if blnSSL then
flags := INTERNET_FLAG_SECURE
else
flags := 0;
pRequest := HTTPOpenRequest(pConnection, 'POST', PChar(Url), nil, nil, nil, flags, 0);
if not Assigned(pRequest) then
raise Exception.Create('HttpOpenRequest failed. ' + WinInetErrorMsg(GetLastError));
try
Header := 'Host: ' + Server + ':' + IntToStr(Port) + #13#10 +
'Content-Type: application/json; charset=UTF-8'#13#10;
if not HttpAddRequestHeaders(pRequest, PChar(Header), Length(Header), HTTP_ADDREQ_FLAG_ADD) then
raise Exception.Create('HttpAddRequestHeaders failed. ' + WinInetErrorMsg(GetLastError));
if not HTTPSendRequest(pRequest, nil, 0, PAnsiChar(jo), Length(jo)) then
raise Exception.Create('HTTPSendRequest failed. ' + WinInetErrorMsg(GetLastError));
SetLength(aBuffer, 4096);
BufStream := TStringStream.Create('', TEncoding.Default);
try
repeat
if not InternetReadFile(pRequest, PByte(aBuffer), Length(aBuffer), BytesRead) then
raise Exception.Create('InternetReadFile failed. ' + WinInetErrorMsg(GetLastError));
if (BytesRead = 0) then Break;
BufStream.WriteBuffer(PByte(aBuffer)^, BytesRead);
until False;
Result := BufStream.DataString;
finally
BufStream.Free;
end;
finally
InternetCloseHandle(pRequest);
end;
finally
InternetCloseHandle(pConnection);
end;
finally
InternetCloseHandle(pSession);
end;
end;