如何禁用从 C# WebApi 到 MarkLogic 服务器的预检请求

How to disable preflight request to MarkLogic server from C# WebApi

技术栈如下

我们开发了带有基本身份验证的 MarkLogic API,在从 C# WebApi 调用它时,我们在 header 中传递凭据。

我们注意到 MarkLogic 访问日志有 2 个条目,即

同样的请求返回 401,即未授权,然后立即返回 200,即成功。

我们意识到从 C# WebAPI 调用 API 端点是在调用实际请求之前使用 OPTIONS 进行预检请求。

我们发现可以选择将内容类型发送为 'text/plain' 来解决这个问题,但我们不能,因为我们有 JSON object 然后我们需要做更多将文本解析为 JSON object.

当我们从 PostMan 调用时,我们面临着同样的问题。

因此,当我们在 API 上有大量请求时,ML API returns 在防火墙上出现 401 错误,然后 FW 阻止所有请求,将其视为暴力攻击.

有什么办法可以禁用预检请求调用吗?

MarkLogicHttpClient _client;

var queryParams = new Dictionary<string, string>
{
    { "query", searchRequest.Query },
    { "offset", searchRequest.Offset.ToString() },
    { "size", searchRequest.Size.ToString() },
    { "format", searchRequest.Format },
    { "sortOrder", searchRequest.SortOrder.ToString().ToUpper() },
    { "transform", transform }
};

var searchUri = QueryHelpers.AddQueryString("getcontent", queryParams);

var response = await _client.GetAsync(searchUri);

//Startup.cs

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>
{
    client.BaseAddress = MarkLogicEndpoint;
})
.ConfigurePrimaryHttpMessageHandler(() =>
    new HttpClientHandler
    {
        Credentials = new NetworkCredential
        {
            UserName = UserName,
            Password = Password
        }
    }
);

HttpClient 仅在首次从服务器收到带有 WWW-Authenticate header 的 401 响应后才发送授权 header。

默认情况下,HttpClient 在授权每个 请求之前等待 401 响应。

有两种方法可以减少 401 响应的数量。

设置HttpClientHandler.PreAuthenticate属性

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>
{
    client.BaseAddress = MarkLogicEndpoint;
})
.ConfigurePrimaryHttpMessageHandler(() =>
    new HttpClientHandler
    {
        // cache the WWW-Authenticate response and send Authorize header
        // on all subsequent requests
        PreAuthenticate = true,
        Credentials = new NetworkCredential
        {
            UserName = UserName,
            Password = Password
        }
    });

或者,手动添加 Authorize Header 到 HttpClient.DefaultRequestHeaders

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>
{
    // construct HTTP Basic Authorization header and send on every request
    // Do not add credentials to HttpClientHandler
    var authorization= "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(Username + ":" + Password));
    client.BaseAddress = MarkLogicEndpoint;
    client.DefaultRequestHeaders.Add("Authorization", authorization);
});