This commit is contained in:
xiaolipro 2024-03-22 18:12:29 +08:00
parent a686d5d3a3
commit ba53652de6
21 changed files with 299 additions and 17 deletions

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/Monitoring/IsToolWindowHidden/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- <WarningsAsErrors>Nullable</WarningsAsErrors>-->
</PropertyGroup>
</Project>

View File

@ -0,0 +1,9 @@
using Fake.Modularity;
using InterfaceForward.Domain;
namespace InterfaceForward.Api.Contract;
[DependsOn(typeof(InterfaceForwardDomainModule))]
public class InterfaceForwardApiContractModule:FakeModule
{
}

View File

@ -14,8 +14,8 @@ public class InterfaceController: ControllerBase
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/SystemInterfacePaginatedList")]
public async Task<PagedResultDto<>> SystemInterfacePaginatedListAsync(SystemInterfacePaginatedInputObjectValue input)
public async Task SystemInterfacePaginatedListAsync()
{
return await _interfaceRepository.GetSystemInterfacePaginatedListAsync(input);
return;
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace InterfaceForward.Api.Filters;
public class CustomerResultAttribute : Attribute
{
}

View File

@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace InterfaceForward.Api.Filters;
public class DefaultUnifiedResultHandler : IUnifiedResultHandler
{
public void HandleActionResult(ResultExecutingContext context)
{
// void、Task会被包装成EmptyResult
if (context.Result is EmptyResult)
{
var res = ResultFactory.CreateSimpleResult();
context.Result = new ObjectResult(res);
return;
}
// string、int、list以及自定义的模型都会被包装成为ObjectResult
if (context.Result is ObjectResult objectResult)
{
var value = objectResult.Value;
// 已unified的无须再包装
if (value is SimpleResult) return;
var res = ResultFactory.CreateDataResult<object>();
// A machine-readable format for specifying errors in HTTP API responses based on https://tools.ietf.org/html/rfc7807.
if (value is ProblemDetails details)
{
res.Code = details.Status;
res.Message = details.Title;
res.Data = context.ModelState.Keys
.SelectMany(key =>
context.ModelState[key]!.Errors
.Where(x => !string.IsNullOrWhiteSpace(key))
.Select(x => new
{
Field = key,
Message = x.ErrorMessage
})
)
.ToList();
objectResult.Value = res;
return;
}
res.Data = value;
objectResult.Value = res;
// 解决string格式问题
objectResult.DeclaredType = res.GetType();
}
}
}

View File

@ -0,0 +1,36 @@
using System.Net;
using System.Text;
using Fake;
using Fake.DependencyInjection;
using Fake.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace InterfaceForward.Api.Filters;
public class GlobalExceptionFilter(IHostEnvironment environment) : IAsyncExceptionFilter, ITransientDependency
{
public async Task OnExceptionAsync(ExceptionContext context)
{
if (context.ExceptionHandled) return;
var result = ResultFactory.CreateErrorResult();
result.Message = context.Exception.Message;
if (environment.IsDevelopment())
{
result.StackTrace = context.Exception.StackTrace;
}
if (context.Exception is BusinessException) //业务异常
{
result.Code = (int)HttpStatusCode.BadRequest;
}
else //未处理异常
{
result.Code = (int)HttpStatusCode.InternalServerError;
}
context.Result = new JsonResult(result);
context.ExceptionHandled = true;
}
}

View File

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc.Filters;
namespace InterfaceForward.Api.Filters;
/// <summary>
/// 在这里定义你的结果格式
/// </summary>
public interface IUnifiedResultHandler
{
void HandleActionResult(ResultExecutingContext context);
}

View File

@ -0,0 +1,19 @@
namespace InterfaceForward.Api.Filters;
public static class ResultFactory
{
public static SimpleResult CreateSimpleResult()
{
return new SimpleResult();
}
public static DataResult<T> CreateDataResult<T>() where T : class
{
return new DataResult<T>();
}
public static ErrorResult CreateErrorResult()
{
return new ErrorResult();
}
}

View File

@ -0,0 +1,42 @@
using System.Net;
namespace InterfaceForward.Api.Filters;
public class SimpleResult
{
/// <summary>
/// 响应状态码
/// </summary>
public int? Code { get; set; }
/// <summary>
/// 响应消息
/// </summary>
public string? Message { get; set; }
public SimpleResult(int code = (int)HttpStatusCode.OK)
{
Code = code;
}
}
public class DataResult<T> : SimpleResult where T : class
{
/// <summary>
/// 响应数据
/// </summary>
public T? Data { get; set; }
}
public class ErrorResult : SimpleResult
{
public ErrorResult()
{
Code = (int)HttpStatusCode.InternalServerError;
}
/// <summary>
/// 异常堆栈
/// </summary>
public string? StackTrace { get; set; }
}

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Mvc.Filters;
namespace InterfaceForward.Api.Filters;
/// <summary>
/// 统一包装action-result
/// </summary>
public class UnifiedResultFilter : IAsyncResultFilter
{
private readonly IUnifiedResultHandler _unifiedResultHandler;
public UnifiedResultFilter(IUnifiedResultHandler unifiedResultHandler)
{
_unifiedResultHandler = unifiedResultHandler;
}
public virtual async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.ActionDescriptor.EndpointMetadata.Any(
x => x.GetType() == typeof(CustomerResultAttribute))) return;
_unifiedResultHandler.HandleActionResult(context);
await next();
}
}

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="../../common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview.2" />
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

View File

@ -1,11 +1,14 @@
using Fake.AspNetCore;
using Fake.Authorization;
using Fake.Modularity;
using InterfaceForward.Api.Contract;
using InterfaceForward.Api.Filters;
using InterfaceForward.Domain;
namespace InterfaceForward.Api;
[DependsOn(typeof(FakeAspNetCoreModule))]
[DependsOn(typeof(FakeAuthorizationModule))]
[DependsOn(typeof(InterfaceForwardDomainModule))]
[DependsOn(typeof(InterfaceForwardApiContractModule))]
public class InterfaceForwardApiModule : FakeModule
@ -16,6 +19,14 @@ public class InterfaceForwardApiModule : FakeModule
// Add services to the container.
services.AddProblemDetails();
services.AddControllers(options =>
{
options.Filters.AddService<GlobalExceptionFilter>();
options.Filters.AddService<UnifiedResultFilter>();
});
services.AddSwagger();
}
public override void ConfigureApplication(ApplicationConfigureContext context)
@ -26,5 +37,28 @@ public class InterfaceForwardApiModule : FakeModule
app.MapDefaultEndpoints();
app.MapGet("/", () => "Hello World!");
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
options.RoutePrefix = string.Empty;
});
}
}
}
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
return services;
}
}

View File

@ -1,6 +1,6 @@
using InterfaceForward.Api;
var builder = WebApplication.CreateSlimBuilder(args);
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplication<InterfaceForwardApiModule>();

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using Fake.DomainDrivenDesign.Entities;
using Fake.DomainDrivenDesign.Entities;
using Fake.DomainDrivenDesign.Entities.Auditing;
namespace InterfaceForward.Domain.Aggregates.InterfaceMapAggregate;

View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fake.DomainDrivenDesign" Version="8.0.0-preview.2"/>
<PackageReference Include="Fake.DomainDrivenDesign" Version="8.0.0-preview.3" />
</ItemGroup>
<ItemGroup>

View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fake.EntityFrameworkCore" Version="8.0.0-preview.2" />
<PackageReference Include="Fake.EntityFrameworkCore" Version="8.0.0-preview.3" />
</ItemGroup>
<ItemGroup>

View File

@ -13,12 +13,12 @@
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.1.23557.2"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0-alpha.1"/>
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0-alpha.1"/>
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.6.0-beta.2"/>
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.6.0-beta.2"/>
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.6.0-beta.2"/>
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.5.1"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.0-beta.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.7.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.7.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.7.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.7.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
global using Xunit;

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\InterfaceForward.Domain\InterfaceForward.Domain.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
using Fake.Modularity;
namespace InterfaceForward.Domain.Tests;
[DependsOn(
typeof(InterfaceForwardDomainModule)
)]
public class InterfaceForwardDomainTestModule:FakeModule
{
}