Есть веб-службы, которые я хочу вызвать в своем приложении, я могу использовать их с импортом WSDL или просто используя «HTTP GET» с URL-адресом и параметрами, поэтому я предпочитаю более позднее, потому что это простая вещь.
Я знаю, что могу использовать indy idhttp.get для выполнения этой работы, но это очень простая вещь, и я не хочу добавлять сложный indy-код в свое приложение.
ОБНОВИТЬ: извините, если я не понял, я имел в виду «не добавлять сложный indy-код», что я не хочу добавлять компоненты indy только для этой простой задачи и предпочитаю более легкий способ для этого.
Это если это веб-сервис RESTful.
я не знал, что REST тоже может иметь wsdl
AhmetCiftci, как я уже говорил, у службы есть URL-адрес «HTTP GET» для ее вызова, поэтому можно использовать WSDL или просто запросить «HTTP GET».





Вызвать веб-службу RESTful с помощью Indy довольно просто.
Добавьте IdHTTP в свой пункт uses. Помните, что IdHTTP нуждается в префиксе «HTTP: //» в ваших URL-адресах.
function GetURLAsString(const aURL: string): string;
var
lHTTP: TIdHTTP;
begin
lHTTP := TIdHTTP.Create;
try
Result := lHTTP.Get(aURL);
finally
lHTTP.Free;
end;
end;
Вопрос был в том, чтобы не добавлять сложный код Indy. Код несложный.
Брюс, Ларс прав, я сказал в своих вопросах, что не хочу использовать indyt "idhttp.get", это самый простой способ, но я хотел светлый путь :)
Ваш самый легкий вариант - использовать внешний код, будь то Indy, Synapse или WinINet. Если вы не можете или не хотите использовать компоненты или классы Delphi, используйте функции оболочки Delphi для WinINet. Также внешний, но, по крайней мере, DLL устанавливается вместе с Windows.
В свою защиту Indy поставляется с Delphi, но версия WinInet и поведение меняются с установленной версией IE, что похоже на ад DLL.
В моей ситуации добавление idHTTP заставило мой exe прыгнуть с 300 КБ до 2 МБ, что не всегда имеет значение, но в моем случае имело значение.
@Toby: Мне было бы очень любопытно узнать, почему это так. Какая версия Delphi и какое приложение (VCL / Console)?
@BruceMcGee Я думаю, это был Delphi XE. В Delphi 7 это был небольшой скачок, возможно, примерно до 800K, но в XE он был намного больше. Консольное приложение. Понятия не имею, почему это сделало его намного больше, полагаю, много кода для компиляции.
@Toby: пустое консольное приложение имеет размер 40 КБ в D7 и 83 КБ в DXE. Добавление IdHttp в раздел uses увеличивает их размер до 139 КБ и 739 КБ соответственно. Я не уверен, откуда взялись эти 2 МБ. Похоже, это могла быть более поздняя версия Delphi, скомпилированная в режиме отладки.
TIdHTTP.Create(nil); можно сократить до TIdHTTP.Create; с помощью Indy 10.6
Вы можете использовать WinINet API так:
uses WinInet;
function GetUrlContent(const Url: string): string;
var
NetHandle: HINTERNET;
UrlHandle: HINTERNET;
Buffer: array[0..1024] of Char;
BytesRead: dWord;
begin
Result := '';
NetHandle := InternetOpen('Delphi 5.x', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(NetHandle) then
begin
UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
if Assigned(UrlHandle) then
{ UrlHandle valid? Proceed with download }
begin
FillChar(Buffer, SizeOf(Buffer), 0);
repeat
Result := Result + Buffer;
FillChar(Buffer, SizeOf(Buffer), 0);
InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
until BytesRead = 0;
InternetCloseHandle(UrlHandle);
end
else
{ UrlHandle is not valid. Raise an exception. }
raise Exception.CreateFmt('Cannot open URL %s', [Url]);
InternetCloseHandle(NetHandle);
end
else
{ NetHandle is not valid. Raise an exception }
raise Exception.Create('Unable to initialize Wininet');
end;
источник: http://www.scalabium.com/faq/dct0080.htm
WinINet API использует то же самое, что и InternetExplorer, поэтому вы также можете бесплатно получить любое соединение и настройки прокси, установленные InternetExplorer.
Как говорит Брюс, если бы у вас был IE, неправильно настроенный для использования, например, неработающего прокси-сервера, вас тоже бы задержали. Непонятно и неприятно, если это случилось.
На собственном опыте я узнал, что вы также получаете все ошибки в IE wininet.dll, который установлен в вашей клиентской системе, бесплатно, включая ужасную ошибку TIMEOUT. БУДЬТЕ ВНИМАТЕЛЬНЫ.
@Warren: они исправляются в обновлениях безопасности и пакетах обновлений, в то время как ваш собственный код или кто-либо еще не получает такой же критической оценки со стороны многих глаз.
И они ломаются. Почему неработающие версии WinInet никогда не исправляются, пока вы не обновитесь до новой версии IE? Ужасный. Не фиксируется в пакете обновления, если сам IE не принудительно загружен в пакет обновления.
Обратите внимание, что WinInet не следует использовать в службах.
Обратите внимание, что этот код не учитывает кодировки символов. Он предполагает, что char / string являются 8-битными, и сохраняет необработанные байты ответа как есть в возвращенном string. Тело ответа имеет кодировку, которая указывается / выводится из заголовков ответа HTTP. И string также имеет кодировку, будь то ANSI в D2007 и ранее или UTF-16 в D2009 и позже. Код должен преобразовать данные тела ответа из кодировки ответа в Unicode, а затем из Unicode в собственную кодировку string.
Компонент Indy TIdHTTP обрабатывает эти преобразования за вас, когда возвращает тело ответа как string.
Используйте функцию Synapse TCP / IP в модуле HTTPSEND (HTTPGetText, HTTPGetBinary). Он будет выполнять HTTP-запрос за вас и не требует никаких внешних DLL, кроме Winsock. Последний выпуск SVN отлично работает в Delphi 2009. В нем используются вызовы функций блокировки, поэтому нет событий для программирования.
Обновление: блоки очень легкие и не состоят из компонентов. Последняя версия от SVN также отлично работает в Delphi XE4.
HTTP-вызовы в Indy также блокируются.
Если можно загрузить в файл, вы можете использовать TDownloadURL из модуля ExtActns. Намного проще, чем напрямую использовать WinInet.
procedure TMainForm.DownloadFile(URL: string; Dest: string);
var
dl: TDownloadURL;
begin
dl := TDownloadURL.Create(self);
try
dl.URL := URL;
dl.FileName := Dest;
dl.ExecuteTarget(nil); //this downloads the file
finally
dl.Free;
end;
end;
Также можно получать уведомления о прогрессе при использовании этого. Просто назначьте обработчик события событию TDownloadURL OnDownloadProgress.
На самом деле код в принятом ответе у меня не работал. Поэтому я немного изменил его, так что он фактически возвращает String и изящно закрывает все после выполнения. Пример возвращает полученные данные как UTF8String, поэтому он будет хорошо работать как для ASCII, так и для страниц UTF8.
uses WinInet;
function GetUrlContent(const Url: string): UTF8String;
var
NetHandle: HINTERNET;
UrlHandle: HINTERNET;
Buffer: array[0..1023] of byte;
BytesRead: dWord;
StrBuffer: UTF8String;
begin
Result := '';
NetHandle := InternetOpen('Delphi 2009', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if Assigned(NetHandle) then
try
UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
if Assigned(UrlHandle) then
try
repeat
InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
Result := Result + StrBuffer;
until BytesRead = 0;
finally
InternetCloseHandle(UrlHandle);
end
else
raise Exception.CreateFmt('Cannot open URL %s', [Url]);
finally
InternetCloseHandle(NetHandle);
end
else
raise Exception.Create('Unable to initialize Wininet');
end;
Надеюсь, это поможет кому-то вроде меня, который искал простой код для извлечения содержимого страницы в Delphi. Ура, Алдис :)
Это работает, только если ответ действительно закодирован в ASCII или UTF-8, что не является гарантией.
Если ваше приложение предназначено только для Windows, я бы предложил использовать WinSock. Он достаточно простой, позволяет выполнять любой HTTP-запрос, может работать как синхронно, так и асинхронно (используя неблокирующий WSASend / WSARecv с обратными вызовами или старый добрый send / recv в выделенном потоке).
Хотя Winsock может может использоваться непосредственно для этого, я бы не рекомендовал его. HTTP - сложный зверь, который нужно реализовать вручную с нуля.
Использование Windows HTTP API тоже может быть простым.
procedure TForm1.Button1Click(Sender: TObject);
var http: variant;
begin
http:=createoleobject('WinHttp.WinHttpRequest.5.1');
http.open('GET', 'http://lazarus.freepascal.org', false);
http.send;
showmessage(http.responsetext);
end;
В приведенном выше коде я подразумеваю, что COM уже был инициализирован для основного потока VCL. Сообщается, что это может быть не всегда так для упрощенных приложений или для приложений LCL. Также это определенно не относится к асинхронной (многопоточной) работе.
Ниже приведен фрагмент реального запущенного кода. Обратите внимание - функционал бонусный. Работать не требуется. Поэтому, пока я отправляю запросы, меня не интересуют их результаты, они игнорируются и сбрасываются.
procedure TfmHaspList.YieldBlinkHTTP(const LED: boolean; const Key_Hardware_ID: cardinal);
var URL: WideString;
begin
URL := 'http://127.0.0.1:1947/action.html?blink' +
IfThen( LED, 'on', 'off') + '=' + IntToStr(Key_Hardware_ID);
TThread.CreateAnonymousThread(
procedure
var Request: OleVariant;
begin
// COM library initialization for the current thread
CoInitialize(nil);
try
// create the WinHttpRequest object instance
Request := CreateOleObject('WinHttp.WinHttpRequest.5.1');
// open HTTP connection with GET method in synchronous mode
Request.Open('GET', URL, False);
// set the User-Agent header value
// Request.SetRequestHeader('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0');
// sends the HTTP request to the server, the Send method does not return
// until WinHTTP completely receives the response (synchronous mode)
Request.Send;
// // store the response into the field for synchronization
// FResponseText := Request.ResponseText;
// // execute the SynchronizeResult method within the main thread context
// Synchronize(SynchronizeResult);
finally
// release the WinHttpRequest object instance
Request := Unassigned;
// uninitialize COM library with all resources
CoUninitialize;
end;
end
).Start;
end;
Да, это отличное и простое решение, но не забывайте вызывать CoInitialize(nil); и CoUninitialize в начале и в конце соответственно.
Это упоминается в ссылках для случая асинхронного потока. Однако в этом конкретном примере объект работает в основном потоке VCL, где COM уже был инициализирован. Я полагаю (хотя не проверял) то же самое относится и к LCL в Windows.
Привет да. Я упомянул об этом, потому что мое приложение выдало мне сообщение «Com not initialized or something error», когда я его использовал (стандартный TForm с кнопкой). Также по какой-то причине, когда я закрыл приложение, оно разбилось после использования методов CoInitialize и CoUninitialize. Просто к вашему сведению.
Была ли ваша переменная "http" локальной? Может быть, вы не уничтожили / не очистили переменную до CoUnInitialize, поэтому она сохранила указатель на COM-объект после выключения COM-подсистемы?
В моем конкретном случае мне нужно было вызвать в службу localhost, чтобы он не блокировал графический интерфейс, и мне вообще не нужны были какие-либо результаты, даже статус успеха / ошибки. Поэтому я просто продолжал порождать TThread.Anonymous worker, которые инициализировали и закрывали COM внутри себя. Работал как шарм.
Привет, @supersan. Здесь немного некропостинга. Просто наткнулся на ситуацию в Delphi XE2, когда стандартные диалоги открытия / сохранения просто не работали. В первую очередь функция PromptForFileName, а фактически вся подсистема. Оказалось, что в XE2 функция сохранения / открытия реализована через COM, и эта функция вызывалась в потоке, поэтому об ошибке COM Not Initialized Windows сообщила и проглотила VCL. Однако, поскольку TOpenFileDialog является стандартным компонентом Delphi и работает просто и надежно в основном потоке VCL, это означает, что COM автоматически инициализируется где-то в основном потоке.
Просто для ссылки на мой предыдущий комментарий: stackoverflow.com/questions/6719853/…
В более новых версиях Delphi лучше использовать THTTPClient от блока System.Net.HttpClient, так как он стандартный и кроссплатформенный. Простой пример:
function GetURL(const AURL: string): string;
var
HttpClient: THttpClient;
HttpResponse: IHttpResponse;
begin
HttpClient := THTTPClient.Create;
try
HttpResponse := HttpClient.Get(AURL);
Result := HttpResponse.ContentAsString();
finally
HttpClient.Free;
end;
end;
Как вы думаете, достаточно ли использования HTTP GET для вызова функции веб-сервиса?