C# SQL 服务器 CLR 函数 GET 和 POST 请求错误
C# SQL Server CLR Request error on functions GET and POST
我按照 GitHub documentation 实现了带有 CURL 扩展的 http 请求,在 SQL Server 2008 R2、Visual Studio 2010 和 .NET 3.5 中工作。
我成功编译并正确签署了 visual studio 中的 .dll,然后在 SQL 服务器中创建了模式和函数,因为一切正常,我可以执行 GET 和 POST 来自 SQL 服务器,但是,当想要在 SABA API 执行 GET 或 POST 时,它会生成一系列错误。
A .NET Framework error occurred during execution of user-defined
routine or aggregate "XGET": System.Net.WebException: The underlying
connection was closed: An unexpected error occurred on a send. --->
System.IO.IOException: Received an unexpected EOF or 0 bytes from the
transport stream. System.IO.IOException: at
System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset,
Int32 count) at System.Net.Security.SslState.StartReadFrame(Byte[]
buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer,
AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.CheckCompletionBeforatextReceive(ProtocolTokat
message, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.StartSatdBlob(Byte[] incoming, Int32
count, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ForceAuthattication(Boolean receiveFirst,
Byte[] buffer, AsyncProtocolRequest asyncRequest) at
System.Net.Security.SslState.ProcessAuthattication(LazyAsyncResult
lazyResult) at
System.Net.TlsStream.CallProcessAuthattication(Object state) at
System.Threading.ExecutionContext.runTryCode(Object userData) at
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode
code, CleanupCode backoutCode, Object userData) at
System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state) at
System.Net.TlsStream.ProcessAuthattication(LazyAsyncResult result)
at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32
size) at System.Net.ConnectStream.WriteHeaders(Boo ...
System.Net.WebException: at
System.Net.WebCliatt.DownloadDataInternal(Uri address, WebRequest&
request) at System.Net.WebCliatt.DownloadString(Uri address) ...
这是程序集的代码
using Microsoft.SqlServer.Server;
using System;
using System.Data.SqlTypes;
using System.Net;
using System.Threading;
public static class Curl
{
[SqlFunction]
[return: SqlFacet(MaxSize = -1)]
public static SqlChars Get(SqlChars H, SqlChars url)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
var client = new WebClient();
AddHeader(H, client);
return new SqlChars(
client.DownloadString(
Uri.EscapeUriString(url.ToSqlString().Value)
).ToCharArray());
}
[SqlProcedure]
public static void Post(SqlChars H, SqlChars d, SqlChars url)
{
var client = new WebClient();
AddHeader(H, client);
if (d.IsNull)
throw new ArgumentException("You must specify data that will be sent to the endpoint", "@d");
var response =
client.UploadString(
Uri.EscapeUriString(url.ToSqlString().Value),
d.ToSqlString().Value
);
SqlContext.Pipe.Send("Request is executed. " + response);
}
[SqlProcedure]
public static void PostWithRetry(SqlChars H, SqlChars d, SqlChars url)
{
var client = new WebClient();
AddHeader(H, client);
if (d.IsNull)
throw new ArgumentException("You must specify data that will be sent to the endpoint", "@d");
int i = RETRY_COUNT;
string response = "";
do try
{
response =
client.UploadString(
Uri.EscapeUriString(url.ToSqlString().Value),
d.ToSqlString().Value
);
i = -1;
break;
}
catch (Exception ex)
{
SqlContext.Pipe.Send("Error:\t" + ex.Message + ". Waiting " + DELAY_ON_ERROR + "ms.");
i--;
Thread.Sleep(DELAY_ON_ERROR);
}
while (i > 0);
if (i == -1)
SqlContext.Pipe.Send("Request is executed." + response);
}
static readonly int RETRY_COUNT = 3;
static readonly int DELAY_ON_ERROR = 50;
public static bool IsNullOrWhiteSpace(this string theString)
{
if (theString == null)
{
return false;
}
if (theString.Trim() == string.Empty)
{
return false;
}
return true;
}
private static void AddHeader(SqlChars H, WebClient client)
{
if (!H.IsNull)
{
string header = H.ToString();
if (!IsNullOrWhiteSpace(header))
client.Headers.Add(HttpRequestHeader.UserAgent, header);
}
}
};
以及如何在 SQL 查询中使用
declare @hkey nvarchar(4000) = 'SabaCertificate: 31336132353061666330315E235E756F6E6555E6261536974655E235E656E5F55535E235E536162615E235E24414021463393C69358BE384802BA1BBEAD3B4661862F193021435F7E28A30F7540FE661B9C5F30FDB06C';
declare @endpoint nvarchar(1000) = 'https://libertad-api.sabacloud.com/v1/location?count=10&startPage=1';
select curl.xget(@hkey, @endpoint)
我已经在 PostMan 中测试过,输入 SabaCertificate 的 Header,如果它向我抛出结果,但是,当证书不正确时,它也会抛出一个响应,但没有显示。
错误请求示例:
{"errorCode":123,"errorMessage":"Invalid or expired Certificate"}
但它也没有给我证书错误的答案,我必须更改我的 WebClient 才能工作。
此外,我认为证书太大,因为有时我会收到此错误:
The identifier that starts with 'SabaCertificate:
31336132353061666330315E235E756F6E6555E6261536974655E235E656E5F55535E235E536162615E235E24414021463393C69358BE384802BA1BBEAD3B4661862F193021435F7E28A30F7540FE661B9C5F30FDB06C'
is too long. Maximum length is 128.
代码中的一个明确问题是您对原始代码进行了细微的更改。在您的 AddHeader
方法中,您有以下行:
client.Headers.Add(HttpRequestHeader.UserAgent, header);
您需要删除 HttpRequestHeader.UserAgent
,因为代码现在正在创建一个 "UserAgent" header,其值为您传入的值,即 "SabaCertificate: 31336132...."。
您还需要更改正在设置的安全协议,因为它们不正确。你应该试试:
ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072; // TLS 1.2
由于您通过 SQL Server 2008 R2 使用 .NET 3.5,因此您无法指定 SecurityProtocolType.Tls12
,因为该值尚未添加到 Framework Version 3.5 的枚举中,因此您必须使用如上所示的数值。请记住,执行安全协议的实际能力是底层 OS 的功能,因此 Windows / Windows 服务器的旧版本可能不支持 TLS 1.2,或者可能需要更改注册表设置才能这样做。如果你继续从 System.Net.TlsStream
.
得到类似的错误,你将不得不尝试解决这个问题
另外,出现以下错误:
The identifier that starts with 'SabaCertificate: 31336...30FDB06C' is too long. Maximum length is 128.
来自 user-error。 "identifier" 是 SQL 服务器中的项目名称(objects、登录名、变量等)。这意味着当该错误发生时您正在做一些不同的事情(并且是错误的),但我看不出它是如何来自您的代码的,至少不是 Get
方法,因为它与数据库。
我按照 GitHub documentation 实现了带有 CURL 扩展的 http 请求,在 SQL Server 2008 R2、Visual Studio 2010 和 .NET 3.5 中工作。
我成功编译并正确签署了 visual studio 中的 .dll,然后在 SQL 服务器中创建了模式和函数,因为一切正常,我可以执行 GET 和 POST 来自 SQL 服务器,但是,当想要在 SABA API 执行 GET 或 POST 时,它会生成一系列错误。
A .NET Framework error occurred during execution of user-defined routine or aggregate "XGET": System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream. System.IO.IOException: at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count) at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.CheckCompletionBeforatextReceive(ProtocolTokat message, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.StartSatdBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.ForceAuthattication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.ProcessAuthattication(LazyAsyncResult lazyResult) at System.Net.TlsStream.CallProcessAuthattication(Object state) at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Net.TlsStream.ProcessAuthattication(LazyAsyncResult result)
at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size) at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size) at System.Net.ConnectStream.WriteHeaders(Boo ... System.Net.WebException: at System.Net.WebCliatt.DownloadDataInternal(Uri address, WebRequest& request) at System.Net.WebCliatt.DownloadString(Uri address) ...
这是程序集的代码
using Microsoft.SqlServer.Server;
using System;
using System.Data.SqlTypes;
using System.Net;
using System.Threading;
public static class Curl
{
[SqlFunction]
[return: SqlFacet(MaxSize = -1)]
public static SqlChars Get(SqlChars H, SqlChars url)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
var client = new WebClient();
AddHeader(H, client);
return new SqlChars(
client.DownloadString(
Uri.EscapeUriString(url.ToSqlString().Value)
).ToCharArray());
}
[SqlProcedure]
public static void Post(SqlChars H, SqlChars d, SqlChars url)
{
var client = new WebClient();
AddHeader(H, client);
if (d.IsNull)
throw new ArgumentException("You must specify data that will be sent to the endpoint", "@d");
var response =
client.UploadString(
Uri.EscapeUriString(url.ToSqlString().Value),
d.ToSqlString().Value
);
SqlContext.Pipe.Send("Request is executed. " + response);
}
[SqlProcedure]
public static void PostWithRetry(SqlChars H, SqlChars d, SqlChars url)
{
var client = new WebClient();
AddHeader(H, client);
if (d.IsNull)
throw new ArgumentException("You must specify data that will be sent to the endpoint", "@d");
int i = RETRY_COUNT;
string response = "";
do try
{
response =
client.UploadString(
Uri.EscapeUriString(url.ToSqlString().Value),
d.ToSqlString().Value
);
i = -1;
break;
}
catch (Exception ex)
{
SqlContext.Pipe.Send("Error:\t" + ex.Message + ". Waiting " + DELAY_ON_ERROR + "ms.");
i--;
Thread.Sleep(DELAY_ON_ERROR);
}
while (i > 0);
if (i == -1)
SqlContext.Pipe.Send("Request is executed." + response);
}
static readonly int RETRY_COUNT = 3;
static readonly int DELAY_ON_ERROR = 50;
public static bool IsNullOrWhiteSpace(this string theString)
{
if (theString == null)
{
return false;
}
if (theString.Trim() == string.Empty)
{
return false;
}
return true;
}
private static void AddHeader(SqlChars H, WebClient client)
{
if (!H.IsNull)
{
string header = H.ToString();
if (!IsNullOrWhiteSpace(header))
client.Headers.Add(HttpRequestHeader.UserAgent, header);
}
}
};
以及如何在 SQL 查询中使用
declare @hkey nvarchar(4000) = 'SabaCertificate: 31336132353061666330315E235E756F6E6555E6261536974655E235E656E5F55535E235E536162615E235E24414021463393C69358BE384802BA1BBEAD3B4661862F193021435F7E28A30F7540FE661B9C5F30FDB06C';
declare @endpoint nvarchar(1000) = 'https://libertad-api.sabacloud.com/v1/location?count=10&startPage=1';
select curl.xget(@hkey, @endpoint)
我已经在 PostMan 中测试过,输入 SabaCertificate 的 Header,如果它向我抛出结果,但是,当证书不正确时,它也会抛出一个响应,但没有显示。
错误请求示例:
{"errorCode":123,"errorMessage":"Invalid or expired Certificate"}
但它也没有给我证书错误的答案,我必须更改我的 WebClient 才能工作。
此外,我认为证书太大,因为有时我会收到此错误:
The identifier that starts with 'SabaCertificate: 31336132353061666330315E235E756F6E6555E6261536974655E235E656E5F55535E235E536162615E235E24414021463393C69358BE384802BA1BBEAD3B4661862F193021435F7E28A30F7540FE661B9C5F30FDB06C' is too long. Maximum length is 128.
代码中的一个明确问题是您对原始代码进行了细微的更改。在您的 AddHeader
方法中,您有以下行:
client.Headers.Add(HttpRequestHeader.UserAgent, header);
您需要删除 HttpRequestHeader.UserAgent
,因为代码现在正在创建一个 "UserAgent" header,其值为您传入的值,即 "SabaCertificate: 31336132...."。
您还需要更改正在设置的安全协议,因为它们不正确。你应该试试:
ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072; // TLS 1.2
由于您通过 SQL Server 2008 R2 使用 .NET 3.5,因此您无法指定 SecurityProtocolType.Tls12
,因为该值尚未添加到 Framework Version 3.5 的枚举中,因此您必须使用如上所示的数值。请记住,执行安全协议的实际能力是底层 OS 的功能,因此 Windows / Windows 服务器的旧版本可能不支持 TLS 1.2,或者可能需要更改注册表设置才能这样做。如果你继续从 System.Net.TlsStream
.
另外,出现以下错误:
The identifier that starts with 'SabaCertificate: 31336...30FDB06C' is too long. Maximum length is 128.
来自 user-error。 "identifier" 是 SQL 服务器中的项目名称(objects、登录名、变量等)。这意味着当该错误发生时您正在做一些不同的事情(并且是错误的),但我看不出它是如何来自您的代码的,至少不是 Get
方法,因为它与数据库。