Расширение SQL CLR для SQL Server c функциями работы с HTTP-запросами.
Собранную DLL для установки расширения SQLCLR можно скачать в разделе релизы.
Вопросы, предложения и любую другую информацию отправляйте на электронную почту.
Новости по проектам или новым материалам в Telegram-канале.
В текущей версии расширение SQL CLR позволяет выполнять HTTP-запросы в скриптах T-SQL с обработкой результата.
В модуле EntryHttpClient содержатся следующие методы по категориям:
-
Catalogs - базовая справочная информация.
- GetHttpMethods - список доступных для использования методов HTTP-запросов.
- GetUserAgentExamples - примеры заголовка "User-Agent"
-
SecurityProtocol - управление протоколом безопасности для HTTP-запросов.
- GetAvailableSecurityProtocols - список всех доступных протоколов безопасности.
- GetCurrentSecurityProtocols - список протоколов безопасности, включенных для запросов в текущий момент времени.
- SetupSecurityProtocol - процедура для установки использования протоколов безопасности. Задается через запятую в виде строки.
-
HttpQuery - процедуры и функции для выполнения HTTP-запросов.
-
HttpQuery - функция для выполнения HTTP-запроса с параметрами:
- url - адрес для выполнения запроса.
- method - метод HTTP-запроса. Список доступных методов смотрите в функции GetHttpMethods.
- headers - XML с настройками заголовков запроса в виде:
<Headers> <Header Name="Accept">application/json</Header> <Header Name="Content-Type">application/json</Header> </Headers>
- body - тело запроса в виде строки.
- timeoutMs - таймаут выполнения запроса в миллисекундах.
- ignoreCertificateValidation - отключить проверку SSL-сертификатов.
Результат возвращается в виде XML с информацией о результате выполнения запроса (тело, заголовки и др.). Например:
<Response> <QueryId>d3aec9f5-a7c0-42a0-8300-6bdec8f89605</QueryId> <CharacterSet>utf-8</CharacterSet> <ContentEncoding /> <ContentLength>-1</ContentLength> <ContentType>application/json; charset=utf-8</ContentType> <CookiesCount>0</CookiesCount> <HeadersCount>4</HeadersCount> <Headers> <Header> <Name>Transfer-Encoding</Name> <Values> <Value>chunked</Value> </Values> </Header> <Header> <Name>Content-Type</Name> <Values> <Value>application/json; charset=utf-8</Value> </Values> </Header> <Header> <Name>Date</Name> <Values> <Value>Sun, 08 Sep 2024 08:49:58 GMT</Value> </Values> </Header> <Header> <Name>Server</Name> <Values> <Value>Kestrel</Value> </Values> </Header> </Headers> <IsFromCache>false</IsFromCache> <IsMutuallyAuthenticated>false</IsMutuallyAuthenticated> <LastModified>2024-09-08T13:49:58.7867246+05:00</LastModified> <Method>GET</Method> <ProtocolVersion>1.1</ProtocolVersion> <ResponseUri>https://api.tinydevtools.ru/myip</ResponseUri> <Server>Kestrel</Server> <StatusCode>OK</StatusCode> <StatusNumber>200</StatusNumber> <StatusDescription>OK</StatusDescription> <SupportsHeaders>true</SupportsHeaders> <Body>{"IP":"8.8.8.8","UserAgent":null,"ClientRequestHeaders":[{"Key":"Connection","Value":"keep-alive"},{"Key":"Host","Value":"api.tinydevtools.ru"},{"Key":"X-Forwarded-Host","Value":"api.tinydevtools.ru"},{"Key":"X-Forwarded-Server","Value":"api.tinydevtools.ru"},{"Key":"X-Original-For","Value":"127.0.0.1:54876"}]}</Body> </Response>
В случае возникновения ошибки выполнения будет возвращена информация об исключении:
<Exception> <QueryId>3b05bed5-7c71-4c7d-af63-0e0624d64fad</QueryId> <Message>Время ожидания операции истекло</Message> <StackTrace> в System.Net.HttpWebRequest.GetResponse() в YPermitin.SQLCLR.HttpHelper.EntryHttpClient.HttpQuery(SqlChars url, SqlChars method, SqlXml headers, SqlInt32 timeoutMs, SqlBoolean ignoreCertificateValidation)</StackTrace> <Source>System</Source> <ToString>System.Net.WebException: Время ожидания операции истекло в System.Net.HttpWebRequest.GetResponse() в YPermitin.SQLCLR.HttpHelper.EntryHttpClient.HttpQuery(SqlChars url, SqlChars method, SqlXml headers, SqlInt32 timeoutMs, SqlBoolean ignoreCertificateValidation)</ToString> </Exception>
Имеет смысл проверять код возврата HTTP и при возникновении ошибки запроса обрабатывать его. То есть требуется обрабатывать не только ошибки выполнения, но и некорректные результаты выполнения HTTP-запросов.
-
HttpQueryProc - хранимая процедура для выполнения HTTP-запроса. Работает аналогично функции HttpQuery, кроме логирования запросов в базу данных. Возвращает XML с результатами работы той же структуры, что и в примере выше.
Поле QueryId позволяет идентифицировать запрос и получить по этому ID запись в таблице базы с логами.
-
-
LoggingToDatabase - логирование выполняемых запросов в базу данных.
- EnableLoggingToDatabase - включение логирования запросов. При включении в базе создается служебная таблица "HttpQueriesLog" для логирования. Логирование работает только при использовании процедуры HttpQueryProc. Т.к. HttpQuery является функцией, то запись логов в базу данных из нее недоступна.
- DisableLoggingToDatabase - отключение логирования запросов.
-
Service - служебные процедуры и функции для диагностики работы.
- GetHttpHelperInstanceId - идентификатор текущего объекта расширения.
- GetHttpHelperInstanceCreateDateUtc - дата создания по UTC текущего объекта расширения.
- GetClrVersion - версия исполняемой среды CLR.
В целом этой функциональности достаточно для решения большинства задач по отправке HTTP-запросов.
Для окружение разработчика необходимы:
Проекты и библиотеки в составе решения:
-
Libs - библиотеки и вспомогательные проекты.
- HttpHelper - проект библиотеки для расширения SQLCLR.
-
Tests - модульные тесты и связанные проверки.
- HttpHelperTests - проект тестов для библиотеки DevAdmHelpers.
Для установки нужно выполнить несколько шагов:
-
Собрать проект HttpHelper в режиме Release.
-
Полученную DLL HttpHelper.dll скопировать на сервер, где установлен экземпляр SQL Server. Пусть для примера путь к DLL на сервере будет "C:\Share\SQLCLR\HttpHelper.dll".
-
Выбрать базу для установки. Например, пусть она называется PowerSQLCLR.
-
Включим интеграцию с CLR. Для упрощения настройки опустим некоторые аспекты безопасности и разрешим установку неподписанных расширений.
EXEC sp_configure 'clr enabled', 1;
RECONFIGURE;
GO
ALTER DATABASE PowerSQLCLR SET TRUSTWORTHY ON;
GO
Также разрешим текущему пользователю доступ к внешним ресурсам. Например, это пользователь YY\ypermitin.
use [master]; GRANT EXTERNAL ACCESS ASSEMBLY TO [YY\ypermitin];
- Далее добавим сборку SQLCLR в базу PowerSQLCLR.
USE [PowerSQLCLR]
GO
CREATE ASSEMBLY [HttpHelper]
FROM 'C:\Share\SQLCLR\HttpHelper.dll'
WITH PERMISSION_SET = UNSAFE;
GO
- Теперь добавим функции, чтобы обращаться к методам сборки из T-SQL.
CREATE PROCEDURE [dbo].[sp_SetupSecurityProtocol](
@protocols nvarchar(max)
)
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[SetupSecurityProtocol];
GO
CREATE FUNCTION fn_GetHttpHelperInstanceCreateDateUTC()
RETURNS datetime
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetHttpHelperInstanceCreateDateUtc];
GO
CREATE FUNCTION fn_GetHttpHelperInstanceId()
RETURNS uniqueidentifier
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetHttpHelperInstanceId];
GO
CREATE FUNCTION fn_GetClrVersion()
RETURNS nvarchar(50)
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetClrVersion];
GO
CREATE FUNCTION fn_HttpQuery (
@url nvarchar(max),
@method nvarchar(150) = 'GET',
@headers xml,
@timeoutMs int = 0,
@ignoreCertificateValidation bit = 0
)
RETURNS xml
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[HttpQuery];
GO
CREATE FUNCTION [dbo].[fn_GetHttpMethods]()
RETURNS TABLE (
[Name] nvarchar(150)
)
AS
EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetHttpMethods];
GO
CREATE FUNCTION [dbo].[fn_GetUserAgentExamples]()
RETURNS TABLE (
[Browser] nvarchar(max),
[OperationSystem] nvarchar(max),
[UserAgent] nvarchar(max)
)
AS
EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetUserAgentExamples];
GO
CREATE FUNCTION [dbo].[fn_GetAvailableSecurityProtocols]()
RETURNS TABLE (
[Name] nvarchar(150)
)
AS
EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetAvailableSecurityProtocols];
GO
CREATE FUNCTION [dbo].[fn_GetCurrentSecurityProtocols]()
RETURNS TABLE (
[Name] nvarchar(150)
)
AS
EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[GetCurrentSecurityProtocols];
GO
CREATE FUNCTION fn_HttpGet
(
@url nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE @response xml,
@bodyJson nvarchar(max);
SELECT @response = [dbo].[fn_HttpQuery] (
@url,
DEFAULT,
null,
60000,
DEFAULT
);
SELECT @bodyJson = @response.value('(/Response/Body)[1]', 'nvarchar(max)');
RETURN @bodyJson;
END
GO
CREATE PROCEDURE [dbo].[sp_EnableLoggingToDatabase]
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[EnableLoggingToDatabase];
GO
CREATE PROCEDURE [dbo].[sp_DisableLoggingToDatabase]
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[DisableLoggingToDatabase];
GO
CREATE PROCEDURE sp_HttpQueryProc (
@url nvarchar(max),
@method nvarchar(150) = 'GET',
@headers xml,
@timeoutMs int = 0,
@ignoreCertificateValidation bit = 0,
@result xml out
)
AS EXTERNAL NAME [HttpHelper].[YPermitin.SQLCLR.HttpHelper.EntryHttpClient].[HttpQueryProc];
GO
Полный скрипт команд T-SQL для установки можно найти здесь.
- Все готово для использования.
Ниже пример использования упрощенного синтаксиса через функцию fn_HttpGet:
USE [PowerSQLCLR]
GO
SELECT
*
FROM OPENJSON([dbo].[fn_HttpGet](N'https://api.tinydevtools.ru/myip')) WITH (
IP nvarchar(max) '$.IP'
)
Этот же результат можно достичь при использовании процедуры sp_HttpQueryProc:
USE [PowerSQLCLR]
GO
DECLARE @url nvarchar(max) = N'https://api.tinydevtools.ru/myip';
DECLARE @method nvarchar(150) = 'GET';
DECLARE @headers xml = null;
DECLARE @timeoutMs int = 60000;
DECLARE @body nvarchar(max) = null;
DECLARE @ignoreCertificateValidation bit = 0;
DECLARE @result xml;
EXECUTE [dbo].[sp_HttpQueryProc]
@url
,@method
,@headers
,@body
,@timeoutMs
,@ignoreCertificateValidation
,@result OUTPUT;
SELECT
[IP]
FROM OPENJSON(@result.value('(/Response/Body)[1]', 'nvarchar(max)')) WITH (
IP nvarchar(max) '$.IP'
)
В более сложных случаях, когда запрос должежн содержать тело запроса и различные заголовке, плюс использовать метод отличный от GET (например, POST) целесообразно использовать либо процедуру sp_HttpQueryProc, либо fn_HttpQuery, где все эти параметры можно указать. Вот пример POST-запроса в публичный тестовый REST API petstore.swagger.io для создания пользователя.
USE [PowerSQLCLR]
GO
DECLARE @url nvarchar(max) = N'https://petstore.swagger.io/v2/user';
DECLARE @method nvarchar(150) = 'POST';
DECLARE @headers xml = N'
<Headers>
<Header Name="Content-Type">application/json</Header>
</Headers>
';
DECLARE @timeoutMs int = 60000;
DECLARE @body nvarchar(max) = N'
{
"username": "Joe",
"firstName": "Joe",
"lastName": "Peshi",
"email": "[email protected]",
"password": "123456",
"phone": "+1111111111",
"userStatus": 1
}';
DECLARE @ignoreCertificateValidation bit = 0;
DECLARE @result xml;
EXECUTE [dbo].[sp_HttpQueryProc]
@url
,@method
,@headers
,@body
,@timeoutMs
,@ignoreCertificateValidation
,@result OUTPUT;
SELECT
*
FROM OPENJSON(@result.value('(/Response/Body)[1]', 'nvarchar(max)')) WITH (
Message nvarchar(max) '$.message',
Code nvarchar(max) '$.code'
)
Подробнее о настройках и разработке расширений SQLCLR можно найти материалы здесь.