C# SQL Server CLR 函数 GET 和 POST 请求错误

标签 c# sql-server curl sqlclr

我关注了GitHub documentation要使用 CURL 扩展实现 http 请求,请在 SQL Server 2008 R2、Visual Studio 2010 和 .NET 3.5 中工作。

我设法在 Visual Studio 中正确编译和签署 .dll,然后在 SQL Server 中创建架构和函数,因为一切正常,我可以从 SQL Server 执行 GET 和 POST,但是,当想要执行在 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 ....”。

您还需要更改您设置的安全协议(protocol),因为它们不正确。你应该尝试:

ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072; // TLS 1.2

由于您通过 SQL Server 2008 R2 使用 .NET 3.5,因此您无法指定 SecurityProtocolType.Tls12,因为该值尚未添加到 Framework 版本 3.5 中的枚举中,因此您必须使用数值如上所示。请记住,执行安全协议(protocol)的实际能力是底层操作系统的功能,因此旧版本的 Windows/Windows Server 可能不支持 TLS 1.2,或者可能需要更改注册表设置才能这样做。如果您继续从 System.Net.TlsStream 收到类似错误,您将不得不尝试解决这个问题。

另外,出现以下错误:

The identifier that starts with 'SabaCertificate: 31336...30FDB06C' is too long. Maximum length is 128.

来自用户错误。 “标识符”是 SQL Server 中的项目名称(对象、登录名、变量等)。这意味着当该错误发生时,您正在做一些不同的(并且错误的)事情,但我看不出它是如何来自您的代码,至少不是 Get 方法,因为它没有与数据库的内部交互。

关于C# SQL Server CLR 函数 GET 和 POST 请求错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53096403/

相关文章:

c# - 正则表达式中的位置和长度

c# - Html.ValidationMessage 未显示消息

sql-server - SQL Server 复制的可靠性如何?

sql-server - 使用 mssql blob 文件保存 doc 文件的版本

php - 抓取标题、描述和关键字的可靠方法

c# - 在 ASP.NET MVC4 中注入(inject)

c# - Nest和ElasticSearch-UpdateSettings

sql - 从 SQL Server 中的 JSON 文本中提取值

brew install php56 --with-cgi curl 错误

php - 检测代理服务器是否可用的最佳方法是什么?