Compare commits
6 Commits
bugfix/raw
...
a924e32291
| Author | SHA1 | Date | |
|---|---|---|---|
| a924e32291 | |||
| 28a4146069 | |||
| 17d40817f2 | |||
| 330443d2c9 | |||
| 6ca876c762 | |||
| e89af1cbcd |
@@ -48,10 +48,13 @@ try
|
||||
?? throw new InvalidOperationException("Connection string is not found.");
|
||||
|
||||
var logger = provider.GetRequiredService<ILogger<RecDbContext>>();
|
||||
var enableSensitiveDataLogging = config.GetValue("EfCore:EnableSensitiveDataLogging", true);
|
||||
var enableDetailedErrors = config.GetValue("EfCore:EnableDetailedErrors", false);
|
||||
|
||||
opt.UseSqlServer(cnnStr)
|
||||
.LogTo(log => logger.LogInformation("{log}", log), LogLevel.Trace)
|
||||
.EnableSensitiveDataLogging()
|
||||
.EnableDetailedErrors();
|
||||
.EnableSensitiveDataLogging(enableSensitiveDataLogging)
|
||||
.EnableDetailedErrors(enableDetailedErrors);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
<Product>ReC.API</Product>
|
||||
<PackageIcon>Assets\icon.ico</PackageIcon>
|
||||
<PackageTags>digital data rest-caller rec api</PackageTags>
|
||||
<Version>2.2.1-beta</Version>
|
||||
<AssemblyVersion>2.2.1.0</AssemblyVersion>
|
||||
<FileVersion>2.2.1.0</FileVersion>
|
||||
<InformationalVersion>2.2.0-beta</InformationalVersion>
|
||||
<Version>2.4.0-beta</Version>
|
||||
<AssemblyVersion>2.4.0.0</AssemblyVersion>
|
||||
<FileVersion>2.4.0.0</FileVersion>
|
||||
<InformationalVersion>2.4.0-beta</InformationalVersion>
|
||||
<Copyright>Copyright © 2025 Digital Data GmbH. All rights reserved.</Copyright>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"LuckyPennySoftwareLicenseKey": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx1Y2t5UGVubnlTb2Z0d2FyZUxpY2Vuc2VLZXkvYmJiMTNhY2I1OTkwNGQ4OWI0Y2IxYzg1ZjA4OGNjZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2x1Y2t5cGVubnlzb2Z0d2FyZS5jb20iLCJhdWQiOiJMdWNreVBlbm55U29mdHdhcmUiLCJleHAiOiIxNzg0ODUxMjAwIiwiaWF0IjoiMTc1MzM2MjQ5MSIsImFjY291bnRfaWQiOiIwMTk4M2M1OWU0YjM3MjhlYmZkMzEwM2MyYTQ4NmU4NSIsImN1c3RvbWVyX2lkIjoiY3RtXzAxazB5NmV3MmQ4YTk4Mzg3aDJnbTRuOWswIiwic3ViX2lkIjoiLSIsImVkaXRpb24iOiIwIiwidHlwZSI6IjIifQ.ZqsFG7kv_-xGfxS6ACk3i0iuNiVUXX2AvPI8iAcZ6-z2170lGv__aO32tWpQccD9LCv5931lBNLWSblKS0MT3gOt-5he2TEftwiSQGFwoIBgtOHWsNRMinUrg2trceSp3IhyS3UaMwnxZDrCvx4-0O-kpOzVpizeHUAZNr5U7oSCWO34bpKdae6grtM5e3f93Z1vs7BW_iPgItd-aLvPwApbaG9VhmBTKlQ7b4Jh64y7UXJ9mKP7Qb_Oa97oEg0oY5DPHOWTZWeE1EzORgVr2qkK2DELSHuZ_EIUhODojkClPNAKtvEl_qEjpq0HZCIvGwfCCRlKlSkQqIeZdFkiXg",
|
||||
"EfCore": {
|
||||
"EnableSensitiveDataLogging": true,
|
||||
"EnableDetailedErrors": false
|
||||
},
|
||||
"RecAction": {
|
||||
"AddedWho": "ReC.API",
|
||||
"UseHttp1ForNtlm": false
|
||||
"UseHttp1ForNtlm": false,
|
||||
"AutoDetectHeaders": false
|
||||
},
|
||||
// Bad request SqlException numbers numbers can be updated at runtime; no restart required.
|
||||
"SqlException": {
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
public class RecActionOptions
|
||||
{
|
||||
public bool UseHttp1ForNtlm { get; set; } = false;
|
||||
public bool AutoDetectHeaders { get; set; } = false;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReC.Application.Common.Constants;
|
||||
using ReC.Application.Common.Dto;
|
||||
@@ -35,7 +36,8 @@ public class InvokeRecActionViewCommandHandler(
|
||||
IOptions<RecActionOptions> options,
|
||||
ISender sender,
|
||||
IHttpClientFactory clientFactory,
|
||||
IConfiguration? config = null
|
||||
IConfiguration? config = null,
|
||||
ILogger<InvokeRecActionViewCommandHandler>? logger = null
|
||||
) : IRequestHandler<InvokeRecActionViewCommand>
|
||||
{
|
||||
private readonly RecActionOptions _options = options.Value;
|
||||
@@ -57,11 +59,47 @@ public class InvokeRecActionViewCommandHandler(
|
||||
using var httpReq = CreateHttpRequestMessage(restType, action.EndpointUri);
|
||||
|
||||
if (action.Body is not null)
|
||||
{
|
||||
httpReq.Content = new StringContent(action.Body);
|
||||
|
||||
var contentType = action.Headers?.FirstOrDefault(h => h.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase));
|
||||
if (contentType is not null && !string.IsNullOrWhiteSpace(contentType.Value.Value))
|
||||
try { httpReq.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType.Value.Value); }
|
||||
catch (FormatException ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "Content-Type '{Value}' could not be parsed with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", contentType.Value.Value, action.Id, action.ProfileId);
|
||||
httpReq.Content.Headers.TryAddWithoutValidation("Content-Type", contentType.Value.Value);
|
||||
}
|
||||
else if (_options.AutoDetectHeaders)
|
||||
{
|
||||
var body = action.Body.TrimStart();
|
||||
if (body.StartsWith('{') || body.StartsWith('['))
|
||||
{
|
||||
httpReq.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json") { CharSet = "utf-8" };
|
||||
logger?.LogWarning("Content-Type header was not specified. Auto-detected 'application/json; charset=utf-8' based on body content. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId);
|
||||
}
|
||||
else if (body.StartsWith('<'))
|
||||
{
|
||||
httpReq.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml") { CharSet = "utf-8" };
|
||||
logger?.LogWarning("Content-Type header was not specified. Auto-detected 'application/xml; charset=utf-8' based on body content. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action.Headers is not null)
|
||||
foreach (var header in action.Headers)
|
||||
httpReq.Headers.Add(header.Key, header.Value);
|
||||
foreach (var header in action.Headers.Where(h => !h.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)))
|
||||
try { httpReq.Headers.Add(header.Key, header.Value); }
|
||||
catch (FormatException ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "Header '{Key}' could not be added with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", header.Key, action.Id, action.ProfileId);
|
||||
httpReq.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
if (_options.AutoDetectHeaders && !httpReq.Headers.Contains("Accept"))
|
||||
{
|
||||
httpReq.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
logger?.LogWarning("Accept header was not specified. Defaulting to 'application/json'. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId);
|
||||
}
|
||||
|
||||
switch (action.EndpointAuthType)
|
||||
{
|
||||
@@ -74,7 +112,12 @@ public class InvokeRecActionViewCommandHandler(
|
||||
switch (action.EndpointAuthApiKeyAddTo)
|
||||
{
|
||||
case ApiKeyLocation.Header:
|
||||
httpReq.Headers.Add(apiKey, apiValue);
|
||||
try { httpReq.Headers.Add(apiKey, apiValue); }
|
||||
catch (FormatException ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "ApiKey header '{Key}' could not be added with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", apiKey, action.Id, action.ProfileId);
|
||||
httpReq.Headers.TryAddWithoutValidation(apiKey, apiValue);
|
||||
}
|
||||
break;
|
||||
case ApiKeyLocation.Query:
|
||||
var uriBuilder = new UriBuilder(httpReq.RequestUri!);
|
||||
@@ -97,14 +140,24 @@ public class InvokeRecActionViewCommandHandler(
|
||||
case EndpointAuthType.JwtBearer:
|
||||
case EndpointAuthType.OAuth2:
|
||||
if (action.EndpointAuthToken is string authToken)
|
||||
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||
try { httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken); }
|
||||
catch (FormatException ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "Bearer token could not be set with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId);
|
||||
httpReq.Headers.TryAddWithoutValidation("Authorization", $"Bearer {authToken}");
|
||||
}
|
||||
break;
|
||||
|
||||
case EndpointAuthType.BasicAuth:
|
||||
if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword)
|
||||
{
|
||||
var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}"));
|
||||
httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth);
|
||||
try { httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); }
|
||||
catch (FormatException ex)
|
||||
{
|
||||
logger?.LogWarning(ex, "Basic auth could not be set with strict validation, falling back to TryAddWithoutValidation. ActionId: {ActionId}, ProfileId: {ProfileId}", action.Id, action.ProfileId);
|
||||
httpReq.Headers.TryAddWithoutValidation("Authorization", $"Basic {basicAuth}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user