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