Compare commits
24 Commits
6044d0bcb6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b3c4d95c6a | |||
| 6a4817cbcd | |||
| a63bccf47d | |||
| 33df010573 | |||
| 6433e0b39b | |||
| c14e6033cb | |||
| df089af385 | |||
| 8b9f7b911d | |||
| 49c4960f05 | |||
| 66cfe0525c | |||
| e9a7ef910f | |||
| 0273beb6f8 | |||
| 20b5b8124d | |||
| beadc3c4bb | |||
| 9256dc6baf | |||
| e8fd49d75d | |||
| b5082daa1a | |||
| 48a69f884e | |||
| b3a27ba24f | |||
| a7f02e1079 | |||
| 265862d63d | |||
| 060ba64268 | |||
| 21cc348c6c | |||
| ccecf47dca |
@@ -1,31 +0,0 @@
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Leanetec.EConnect.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a lazily initialized <see cref="IServiceProvider"/> for accessing the EConnect client services,
|
||||
/// including an <see cref="IMediator"/> instance for sending and publishing messages.
|
||||
/// </summary>
|
||||
public static class Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Lazily initializes the <see cref="IServiceProvider"/> that registers and builds the EConnect client services.
|
||||
/// </summary>
|
||||
private static readonly Lazy<IServiceProvider> _serviceProvider = new(() =>
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddClientServices();
|
||||
return services.BuildServiceProvider();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets the initialized <see cref="IServiceProvider"/> that provides access to registered EConnect client services.
|
||||
/// </summary>
|
||||
public static IServiceProvider Provider => _serviceProvider.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IMediator"/> instance used for sending commands, queries, and publishing events within the EConnect client.
|
||||
/// </summary>
|
||||
public static IMediator Mediator => Provider.GetRequiredService<IMediator>();
|
||||
}
|
||||
@@ -6,10 +6,5 @@ public interface IEConnectClient<TError> where TError : class
|
||||
{
|
||||
public Task<Response<TData, TError>> GetAsync<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default) where TData : class;
|
||||
|
||||
public Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||
where TData : class;
|
||||
|
||||
public Task<Response<TError>> PostAsync(string? route = null, object? queryParams = null, CancellationToken cancel = default);
|
||||
|
||||
public Task<Response<TError>> PostAsync(StreamContent content, string? route = null, object? queryParams = null, CancellationToken cancel = default);
|
||||
Task<Response<TError>> PostAsync(Stream stream, string fileName, string? route = null, object? queryParams = null, CancellationToken cancel = default);
|
||||
}
|
||||
@@ -1,17 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="13.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||
<PackageReference Include="MediatR" Version="12.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="MediatR" Version="13.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||
<PackageReference Include="MediatR" Version="13.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Leanetec.EConnect.Domain\Leanetec.EConnect.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -5,6 +5,7 @@ using MediatR;
|
||||
|
||||
namespace Leanetec.EConnect.Client.Order;
|
||||
|
||||
//TODO: EConnectClient.GetListAsAsyncEnumerable
|
||||
public record GetDocumentRequest(string TenantId, int OrderId) : HttpRequest<Response<IEnumerable<OrderDocument>, ProblemDetail>>
|
||||
{
|
||||
}
|
||||
|
||||
@@ -5,11 +5,17 @@ using MediatR;
|
||||
|
||||
namespace Leanetec.EConnect.Client.Order;
|
||||
|
||||
public record PostDocumentRequest(string TenantId, int OrderId, StreamContent Content) : HttpRequest<Response<ProblemDetail>>
|
||||
public record PostDocumentRequest(string TenantId, int OrderId) : HttpRequest<Response<ProblemDetail>>
|
||||
{
|
||||
public UploadDocumentRequest ToUploadDocument(Stream stream, string fileName)
|
||||
{
|
||||
return new UploadDocumentRequest(this, stream, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
public class PostDocumentRequestHandler : IRequestHandler<PostDocumentRequest, Response<ProblemDetail>>
|
||||
public record UploadDocumentRequest(PostDocumentRequest Original, Stream Stream, string FileName) : PostDocumentRequest(Original);
|
||||
|
||||
public class PostDocumentRequestHandler : IRequestHandler<UploadDocumentRequest, Response<ProblemDetail>>
|
||||
{
|
||||
private readonly IEConnectClient<ProblemDetail> _client;
|
||||
|
||||
@@ -18,11 +24,12 @@ public class PostDocumentRequestHandler : IRequestHandler<PostDocumentRequest, R
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task<Response<ProblemDetail>> Handle(PostDocumentRequest request, CancellationToken cancel)
|
||||
public async Task<Response<ProblemDetail>> Handle(UploadDocumentRequest request, CancellationToken cancel)
|
||||
{
|
||||
return await _client.PostAsync(
|
||||
request.Content,
|
||||
$"api/public/v{request.ApiVersion}/econnect/order/document/list",
|
||||
request.Stream,
|
||||
request.FileName,
|
||||
$"api/public/v{request.ApiVersion}/econnect/order/document",
|
||||
new { request.TenantId, request.OrderId },
|
||||
cancel
|
||||
);
|
||||
|
||||
@@ -6,8 +6,16 @@ public record Response<TError>() where TError : class
|
||||
{
|
||||
public bool Ok { get; init; }
|
||||
|
||||
public HttpStatusCode? StatusCode { get; init; }
|
||||
|
||||
private HttpStatusCode? _statusCode;
|
||||
|
||||
public HttpStatusCode StatusCode
|
||||
{
|
||||
get => _statusCode ?? (Ok ? HttpStatusCode.OK : HttpStatusCode.InternalServerError);
|
||||
init => _statusCode = value;
|
||||
}
|
||||
|
||||
public int StatusCodeInt => (int)StatusCode;
|
||||
|
||||
public TError? Error { get; init; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Leanetec.EConnect.Client.Interface;
|
||||
using Leanetec.EConnect.Domain.Entities;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Leanetec.EConnect.Infrastructure;
|
||||
@@ -13,10 +16,13 @@ public class EConnectClient<TError> : IEConnectClient<TError> where TError : cl
|
||||
|
||||
private HttpClient Http => LazyHttp.Value;
|
||||
|
||||
public EConnectClient(IOptions<ClientOptions> options, IHttpClientFactory httpFactory)
|
||||
private readonly ILogger<EConnectClient<TError>>? _logger;
|
||||
|
||||
public EConnectClient(IOptions<ClientOptions> options, IHttpClientFactory httpFactory, ILogger<EConnectClient<TError>>? logger = null)
|
||||
{
|
||||
_options = options.Value;
|
||||
LazyHttp = new Lazy<HttpClient>(httpFactory.CreateEConnectClient);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private string? AddQueryString(string? route = null, object? queryParams = null)
|
||||
@@ -38,109 +44,75 @@ public class EConnectClient<TError> : IEConnectClient<TError> where TError : cl
|
||||
route = AddQueryString(route, queryParams);
|
||||
|
||||
var res = await Http.GetAsync(route, cancel);
|
||||
_logger?.LogCurl(Http, HttpMethod.Get, route);
|
||||
|
||||
if (res.IsSuccessStatusCode)
|
||||
{
|
||||
var data = await res.Content.ReadFromJsonAsync<TData>(_options.JsonSerializerOptions, cancel);
|
||||
return new ()
|
||||
return res.IsSuccessStatusCode
|
||||
? new()
|
||||
{
|
||||
Ok = true,
|
||||
StatusCode = res.StatusCode,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||
return new ()
|
||||
Data = (res.Content.Headers.ContentLength > 0)
|
||||
? await res.Content.ReadFromJsonAsync<TData>(_options.JsonSerializerOptions, cancel)
|
||||
: null
|
||||
}
|
||||
: new()
|
||||
{
|
||||
Ok = false,
|
||||
StatusCode = res.StatusCode,
|
||||
Error = error
|
||||
Error = (res.Content.Headers.ContentLength > 0)
|
||||
? await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel)
|
||||
: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Response<IAsyncEnumerable<TData?>, TError>> GetListAsAsyncEnumerable<TData>(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||
where TData : class
|
||||
public async Task<Response<TError>> PostAsync(Stream stream, string fileName, string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||
{
|
||||
route = AddQueryString(route, queryParams);
|
||||
|
||||
var res = await Http.GetAsync(route, cancel);
|
||||
// create message and add accept header
|
||||
using var message = new HttpRequestMessage(HttpMethod.Post, route);
|
||||
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
|
||||
|
||||
if (res.IsSuccessStatusCode)
|
||||
// add file type
|
||||
var fileContent = new StreamContent(stream);
|
||||
var mimeType = GetMimeType(fileName);
|
||||
fileContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
|
||||
|
||||
// Create multipart form data form
|
||||
using var form = new MultipartFormDataContent
|
||||
{
|
||||
var data = res.Content.ReadFromJsonAsAsyncEnumerable<TData>(_options.JsonSerializerOptions, cancel);
|
||||
return new ()
|
||||
{
|
||||
Ok = true,
|
||||
StatusCode = res.StatusCode,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||
return new()
|
||||
{
|
||||
Ok = false,
|
||||
StatusCode = res.StatusCode,
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
}
|
||||
{ fileContent, "file", fileName }
|
||||
};
|
||||
|
||||
message.Content = form;
|
||||
|
||||
public async Task<Response<TError>> PostAsync(string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||
where TData : class
|
||||
{
|
||||
route = AddQueryString(route, queryParams);
|
||||
var res = await Http.SendAsync(message, cancel);
|
||||
|
||||
var res = await Http.PostAsync(route, null, cancel);
|
||||
_logger?.LogCurl(Http, message);
|
||||
|
||||
if (res.IsSuccessStatusCode)
|
||||
{
|
||||
return new()
|
||||
return res.IsSuccessStatusCode
|
||||
? new()
|
||||
{
|
||||
Ok = true,
|
||||
StatusCode = res.StatusCode
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||
return new()
|
||||
}
|
||||
: new()
|
||||
{
|
||||
Ok = false,
|
||||
StatusCode = res.StatusCode,
|
||||
Error = error
|
||||
Error = (res.Content.Headers.ContentLength > 0)
|
||||
? await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel)
|
||||
: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Response<TError>> PostAsync(StreamContent content, string? route = null, object? queryParams = null, CancellationToken cancel = default)
|
||||
where TData : class
|
||||
private static string GetMimeType(string fileName)
|
||||
{
|
||||
route = AddQueryString(route, queryParams);
|
||||
|
||||
var res = await Http.PostAsync(route, content, cancel);
|
||||
|
||||
if (res.IsSuccessStatusCode)
|
||||
var provider = new FileExtensionContentTypeProvider();
|
||||
if (!provider.TryGetContentType(fileName, out var contentType))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Ok = true,
|
||||
StatusCode = res.StatusCode
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = await res.Content.ReadFromJsonAsync<TError>(_options.JsonSerializerOptions, cancel);
|
||||
return new()
|
||||
{
|
||||
Ok = false,
|
||||
StatusCode = res.StatusCode,
|
||||
Error = error
|
||||
};
|
||||
contentType = "application/octet-stream"; // fallback
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.19" />
|
||||
<PackageReference Include="HttpClientToCurl" Version="2.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.19" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.19" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Leanetec.EConnect.Client\Leanetec.EConnect.Client.csproj" />
|
||||
|
||||
100
src/Leanetec.EConnect.Infrastructure/LogExtensions.cs
Normal file
100
src/Leanetec.EConnect.Infrastructure/LogExtensions.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using HttpClientToCurl;
|
||||
using HttpClientToCurl.Config;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace Leanetec.EConnect.Infrastructure;
|
||||
|
||||
public static class LogExtensions
|
||||
{
|
||||
public static void LogCurl(
|
||||
this ILogger logger,
|
||||
HttpClient client,
|
||||
HttpRequestMessage request,
|
||||
Action<StringConfig>? config = null,
|
||||
int maxLength = 1000,
|
||||
LogLevel logLevel = LogLevel.Information)
|
||||
{
|
||||
try
|
||||
{
|
||||
var curl = client.GenerateCurlInString(
|
||||
request,
|
||||
config
|
||||
).Truncate(maxLength);
|
||||
logger?.Log(logLevel, "{curl}", curl);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An unexpected error occurred while logging the HTTP request as cURL.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogCurl(
|
||||
this ILogger logger,
|
||||
HttpClient client,
|
||||
HttpMethod method,
|
||||
string? uri = null,
|
||||
HttpRequestHeaders? headers = null,
|
||||
HttpContent? content = null,
|
||||
Action<StringConfig>? config = null,
|
||||
int maxLength = 1000,
|
||||
LogLevel logLevel = LogLevel.Information)
|
||||
{
|
||||
try
|
||||
{
|
||||
var curl = client.GenerateCurlInString(
|
||||
method,
|
||||
uri ?? "",
|
||||
headers,
|
||||
content,
|
||||
config
|
||||
).Truncate(maxLength);
|
||||
logger?.Log(logLevel, "{curl}", curl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An unexpected error occurred while logging the HTTP request as cURL.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogCurl(
|
||||
this ILogger logger,
|
||||
HttpClient client,
|
||||
HttpMethod method,
|
||||
Uri uri,
|
||||
HttpRequestHeaders? headers = null,
|
||||
HttpContent? content = null,
|
||||
Action<StringConfig>? config = null,
|
||||
int maxLength = 1000,
|
||||
LogLevel logLevel = LogLevel.Information)
|
||||
{
|
||||
try
|
||||
{
|
||||
var curl = client.GenerateCurlInString(
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
content,
|
||||
config
|
||||
).Truncate(maxLength);
|
||||
logger?.Log(logLevel, "{curl}", curl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An unexpected error occurred while logging the HTTP request as cURL.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates the string to the specified max length and appends a suffix if truncated.
|
||||
/// </summary>
|
||||
public static string Truncate(this string? value, int maxLength, string suffix = "...[truncated]")
|
||||
{
|
||||
if (string.IsNullOrEmpty(value) || maxLength <= 0)
|
||||
return string.Empty;
|
||||
|
||||
return value.Length <= maxLength
|
||||
? value
|
||||
: value[..maxLength] + suffix;
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,24 @@ public class OrderController : ControllerBase
|
||||
{
|
||||
var res = await _mediator.Send(request, cancel);
|
||||
if(res.Ok)
|
||||
{
|
||||
return res.Data is null || !res.Data.Any() ? NotFound() : Ok(res.Data);
|
||||
}
|
||||
return res.Data is null || !res.Data.Any()
|
||||
? NotFound()
|
||||
: Ok(res.Data);
|
||||
else
|
||||
{
|
||||
return StatusCode(res?.Error?.Status ?? 500, res?.Error);
|
||||
}
|
||||
return StatusCode(res.StatusCodeInt, res?.Error);
|
||||
}
|
||||
|
||||
[HttpPost("document")]
|
||||
public async Task<IActionResult> PostDocument(IFormFile file, [FromQuery] PostDocumentRequest request, CancellationToken cancel)
|
||||
{
|
||||
using var stream = file.OpenReadStream();
|
||||
|
||||
var uploadRequest = request.ToUploadDocument(stream, file.FileName);
|
||||
|
||||
var res = await _mediator.Send(uploadRequest, cancel);
|
||||
|
||||
return res.Ok
|
||||
? StatusCode(res.StatusCodeInt)
|
||||
: StatusCode(res.StatusCodeInt, res?.Error);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PackageId>Leanetec.EConnect.Proxy</PackageId>
|
||||
<Version>1.0.1</Version>
|
||||
<Company>Digital Data GmbH</Company>
|
||||
<Product>Leanetec.EConnect.Proxy</Product>
|
||||
<Title>Leanetec.EConnect.Proxy</Title>
|
||||
<AssemblyVersion>1.0.1</AssemblyVersion>
|
||||
<FileVersion>1.0.1</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
||||
<PackageReference Include="NLog" Version="6.0.3" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="6.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using DigitalData.Core.Exceptions;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Leanetec.EConnect.Proxy.Middleware;
|
||||
|
||||
//TODO: Fix and use DigitalData.Core.Exceptions.Middleware
|
||||
/// <summary>
|
||||
/// Middleware for handling exceptions globally in the application.
|
||||
/// Captures exceptions thrown during the request pipeline execution,
|
||||
/// logs them, and returns an appropriate HTTP response with a JSON error message.
|
||||
/// </summary>
|
||||
[Obsolete("Use DigitalData.Core.Exceptions.Middleware")]
|
||||
public class ExceptionHandlingMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the request pipeline.</param>
|
||||
/// <param name="logger">The logger instance for logging exceptions.</param>
|
||||
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the middleware to handle the HTTP request.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context of the current request.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(context); // Continue down the pipeline
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HandleExceptionAsync(context, ex, _logger);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles exceptions by logging them and writing an appropriate JSON response.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context of the current request.</param>
|
||||
/// <param name="exception">The exception that occurred.</param>
|
||||
/// <param name="logger">The logger instance for logging the exception.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
|
||||
{
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
string message;
|
||||
|
||||
switch (exception)
|
||||
{
|
||||
case BadRequestException badRequestEx:
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
message = badRequestEx.Message;
|
||||
break;
|
||||
|
||||
case NotFoundException notFoundEx:
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
message = notFoundEx.Message;
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.LogError(exception, "Unhandled exception occurred.");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
message = "An unexpected error occurred.";
|
||||
break;
|
||||
}
|
||||
|
||||
await context.Response.WriteAsync(JsonSerializer.Serialize(new
|
||||
{
|
||||
message
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,65 @@
|
||||
using Leanetec.EConnect.Client;
|
||||
using Leanetec.EConnect.Infrastructure;
|
||||
using Leanetec.EConnect.Proxy.Middleware;
|
||||
using NLog;
|
||||
using NLog.Web;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var config = builder.Configuration;
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Info("Logging initialized!");
|
||||
|
||||
// Add services to the container.
|
||||
var clientOpt = config.GetSection("EConnect");
|
||||
builder.Services.AddClientServices().AddInfrastructureServices(opt => opt.ConfigureClient(clientOpt));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
try
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var config = builder.Configuration;
|
||||
|
||||
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
||||
if (!builder.Environment.IsDevelopment())
|
||||
{
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Host.UseNLog();
|
||||
}
|
||||
|
||||
// Add services to the container.
|
||||
var clientOpt = config.GetSection("EConnect");
|
||||
builder.Services.AddClientServices().AddInfrastructureServices(opt => opt.ConfigureClient(clientOpt));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||
|
||||
bool useSwagger = config.GetValue<bool>("UseSwagger");
|
||||
if(useSwagger)
|
||||
app.Services.GetRequiredService<ILogger<Program>>()
|
||||
.LogWarning("Swagger UI is enabled. Using Swagger in a production environment may expose sensitive API information and pose security risks.");
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment() || useSwagger)
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
catch (Exception ex)
|
||||
{
|
||||
// NLog: catch setup errors
|
||||
logger.Error(ex, "Application stopped because of exception");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
LogManager.Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>Package</WebPublishMethod>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish />
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>79d9868e-6a67-45c1-ba3e-1c2e822ecc58</ProjectGuid>
|
||||
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\EConnect\Proxy\$(Version)\EConnect.Proxy.zip</DesktopBuildPackageLocation>
|
||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||
<DeployIisAppPath>EConnect.Proxy</DeployIisAppPath>
|
||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -5,6 +5,7 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"UseSwagger": true,
|
||||
"AllowedHosts": "*",
|
||||
"EConnect": {
|
||||
"BaseAddress": "https://portal.demoportal01.leanetec.com",
|
||||
@@ -12,5 +13,56 @@
|
||||
"apiKey": "HGpGp3vk3MsKgSe0tKVZ1ZRCuq6bFoJ1"
|
||||
},
|
||||
"JsonSerializerDateFormat": "yyyy-MM-dd HH:mm"
|
||||
},
|
||||
"NLog": {
|
||||
"throwConfigExceptions": true,
|
||||
"variables": {
|
||||
"logDirectory": "E:\\LogFiles\\Digital Data\\EConnect-Proxy",
|
||||
"logFileNamePrefix": "${shortdate}-ECM.EConnect-Proxy.Web"
|
||||
},
|
||||
"targets": {
|
||||
"infoLogs": {
|
||||
"type": "File",
|
||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
|
||||
"maxArchiveDays": 30
|
||||
},
|
||||
"warningLogs": {
|
||||
"type": "File",
|
||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log",
|
||||
"maxArchiveDays": 30
|
||||
},
|
||||
"errorLogs": {
|
||||
"type": "File",
|
||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
|
||||
"maxArchiveDays": 30
|
||||
},
|
||||
"criticalLogs": {
|
||||
"type": "File",
|
||||
"fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
|
||||
"maxArchiveDays": 30
|
||||
}
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"logger": "*",
|
||||
"level": "Info",
|
||||
"writeTo": "infoLogs"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"level": "Warn",
|
||||
"writeTo": "warningLogs"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"level": "Error",
|
||||
"writeTo": "errorLogs"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"level": "Fatal",
|
||||
"writeTo": "criticalLogs"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user