Browse Source

✨ 添加全局异常拦截处理器 添加健康检查Mysql和Redis

old-3.0
王军 5 years ago
parent
commit
b181226c00
  1. 1
      content/aspnetcore/CompanyName.ProjectName.sln
  2. 4
      content/aspnetcore/NuGet.Config
  3. 7
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/CompanyName.ProjectName.HttpApi.Host.csproj
  4. 57
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/CompanyNameProjectNameHttpApiHostModule.cs
  5. 2
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/Dockerfile
  6. 129
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/Extensions/Filters/ResultExceptionFilter.cs
  7. 61
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.Ali.json
  8. 61
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.Staging.json
  9. 2
      content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.json
  10. 12
      content/aspnetcore/src/CompanyName.ProjectName.Application/Attributes/DontWrapResultAttribute.cs
  11. 31
      content/aspnetcore/src/CompanyName.ProjectName.Application/Attributes/WrapResult.cs
  12. 1
      content/aspnetcore/src/CompanyName.ProjectName.Application/Users/LoginAppService.cs
  13. 2
      content/aspnetcore/tools/CompanyName.ProjectName.DbMigrator/appsettings.json
  14. 30
      content/vue/.husky/_/husky.sh

1
content/aspnetcore/CompanyName.ProjectName.sln

@ -46,6 +46,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BC7E3BFE-3644-4893-8475-E543F3957F2A}"
ProjectSection(SolutionItems) = preProject
common.props = common.props
NuGet.Config = NuGet.Config
EndProjectSection
EndProject
Global

4
content/aspnetcore/NuGet.Config

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="yhglobal" value="https://proget.yhglobal.cn/nuget/default/" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</config>
<packageSources>
<add key="yhglobal" value="https://proget.yhglobal.cn/nuget/default/" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

7
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/CompanyName.ProjectName.HttpApi.Host.csproj

@ -26,6 +26,12 @@
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.MySql" Version="5.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.7" />
<PackageReference Include="nacos-sdk-csharp" Version="1.0.0" />
<PackageReference Include="nacos-sdk-csharp.AspNetCore" Version="1.0.0" />
<PackageReference Include="nacos-sdk-csharp.Extensions.Configuration" Version="1.0.0" />
@ -35,7 +41,6 @@
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Swashbuckle" Version="$(AbpVersion)" />

57
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/CompanyNameProjectNameHttpApiHostModule.cs

@ -25,9 +25,6 @@ using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
@ -48,7 +45,6 @@ namespace CompanyNameProjectName
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(CompanyNameProjectNameApplicationModule),
typeof(CompanyNameProjectNameEntityFrameworkCoreDbMigrationsModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpAccountWebIdentityServerModule),
typeof(AbpAspNetCoreSerilogModule),
@ -76,7 +72,6 @@ namespace CompanyNameProjectName
{
var configuration = context.Services.GetConfiguration();
ConfigureOptions(context);
ConfigureBundles();
ConfigureUrls(configuration);
ConfigureConventionalControllers();
ConfigureJwtAuthentication(context, configuration);
@ -88,6 +83,7 @@ namespace CompanyNameProjectName
ConfigureCache(context.Services);
ConfigureAuditLog();
ConfigureHangfire(context.Services);
ConfigureHealthChecks(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -101,12 +97,6 @@ namespace CompanyNameProjectName
}
app.UseAbpRequestLocalization();
if (!env.IsDevelopment())
{
app.UseErrorPage();
}
app.UseCorrelationId();
app.UseStaticFiles();
app.UseRouting();
@ -114,7 +104,6 @@ namespace CompanyNameProjectName
app.UseAuthentication();
app.UseJwtTokenMiddleware();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(c =>
{
@ -131,10 +120,27 @@ namespace CompanyNameProjectName
});
app.UseConfiguredEndpoints();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
}
#region 私有方法
private void ConfigureHealthChecks(ServiceConfigurationContext context)
{
var redisConnectionString =
context.Services.GetConfiguration().GetValue<string>("Cache:Redis:ConnectionString")
+",defaultdatabase="
+ context.Services.GetConfiguration().GetValue<int>("Cache:Redis:DatabaseId", 1);
var mysqlConnectionString = context.Services.GetConfiguration().GetConnectionString("Default");
context.Services.AddHealthChecks().AddRedis(redisConnectionString).AddMySql(mysqlConnectionString);
}
/// <summary>
/// 配置options
/// </summary>
@ -152,8 +158,8 @@ namespace CompanyNameProjectName
{
Configure<AbpAuditingOptions>(options =>
{
options.IsEnabled = true;
options.EntityHistorySelectors.AddAllEntities();
options.IsEnabled = true;
options.EntityHistorySelectors.AddAllEntities();
options.ApplicationName = "CompanyName.ProjectName";
});
}
@ -187,21 +193,18 @@ namespace CompanyNameProjectName
options.SendExceptionsDetailsToClients = true;
});
}
}
private void ConfigureBundles()
{
Configure<AbpBundlingOptions>(options =>
// 如果要求返回值正确和异常格式一致添加次特性
context.Services.AddMvc(options =>
{
options.StyleBundles.Configure(
BasicThemeBundles.Styles.Global,
bundle => { bundle.AddFiles("/global-styles.css"); }
);
options.Filters.Add(typeof(ResultExceptionFilter));
});
}
private void ConfigureUrls(IConfiguration configuration)
{
Configure<AppUrlOptions>(options =>
@ -231,7 +234,7 @@ namespace CompanyNameProjectName
{
options.ConventionalControllers.Create(typeof(CompanyNameProjectNameApplicationModule).Assembly);
});
}
/// <summary>
@ -362,7 +365,7 @@ namespace CompanyNameProjectName
});
var redisConnectionString = services.GetConfiguration().GetSection("Cache:Redis:ConnectionString").Value;
var redisDatabaseId = Convert.ToInt32(services.GetConfiguration().GetSection("Cache:Redis:DatabaseId").Value);
var redisDatabaseId = services.GetConfiguration().GetValue<int>("Cache:Redis:DatabaseId", 1);
// 启用Hangfire 并使用Redis作为持久化
services.AddHangfire(config =>

2
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/Dockerfile

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/aspnet:5.0
FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim
# ´´½¨Ä¿Â¼
RUN mkdir /apps

129
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/Extensions/Filters/ResultExceptionFilter.cs

@ -0,0 +1,129 @@
using CompanyNameProjectName.Attributes;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http;
using Volo.Abp.Json;
using Volo.Abp.Validation;
namespace CompanyNameProjectName.Extensions.Filters
{
public class ResultExceptionFilter : IFilterMetadata, IAsyncExceptionFilter, ITransientDependency
{
public ILogger<ResultExceptionFilter> Logger { get; set; }
private readonly IExceptionToErrorInfoConverter _errorInfoConverter;
private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder;
private readonly IJsonSerializer _jsonSerializer;
private readonly AbpExceptionHandlingOptions _exceptionHandlingOptions;
public ResultExceptionFilter(
IExceptionToErrorInfoConverter errorInfoConverter,
IHttpExceptionStatusCodeFinder statusCodeFinder,
IJsonSerializer jsonSerializer,
IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions)
{
_errorInfoConverter = errorInfoConverter;
_statusCodeFinder = statusCodeFinder;
_jsonSerializer = jsonSerializer;
_exceptionHandlingOptions = exceptionHandlingOptions.Value;
Logger = NullLogger<ResultExceptionFilter>.Instance;
}
public async Task OnExceptionAsync(ExceptionContext context)
{
if (!ShouldHandleException(context))
{
return;
}
await HandleAndWrapException(context);
}
protected virtual bool ShouldHandleException(ExceptionContext context)
{
if (context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.GetCustomAttributes(typeof(DontWrapResultAttribute), true).Any())
{
return true;
}
if (context.ActionDescriptor.GetMethodInfo().GetCustomAttributes(typeof(DontWrapResultAttribute), true).Any())
{
return true;
}
return false;
}
protected virtual async Task HandleAndWrapException(ExceptionContext context)
{
//TODO: Trigger an AbpExceptionHandled event or something like that.
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
var statusCode = (int)_statusCodeFinder.GetStatusCode(context.HttpContext, context.Exception);
context.HttpContext.Response.StatusCode = 200;
var remoteServiceErrorInfo = _errorInfoConverter.Convert(context.Exception, _exceptionHandlingOptions.SendExceptionsDetailsToClients);
remoteServiceErrorInfo.Code = context.HttpContext.TraceIdentifier;
remoteServiceErrorInfo.Message = SimplifyMessage(context.Exception);
var result = new WrapResult<object>();
result.SetFail(remoteServiceErrorInfo.Message);
// HttpResponse
context.Result = new ObjectResult(result);
// 写日志
var logLevel = context.Exception.GetLogLevel();
var remoteServiceErrorInfoBuilder = new StringBuilder();
remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------");
remoteServiceErrorInfoBuilder.AppendLine(_jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
Logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString());
Logger.LogException(context.Exception, logLevel);
await context.HttpContext
.RequestServices
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(
new ExceptionNotificationContext(context.Exception)
);
context.Exception = null; //Handled!
}
protected string SimplifyMessage(Exception error)
{
string message = string.Empty;
switch (error)
{
case AbpAuthorizationException e:
return message = "Authenticate failure!";
case AbpValidationException e:
return message = "Request param validate failure!";
case EntityNotFoundException e:
return message = "not found the entity!";
case BusinessException e:
return message = $"{e.Message}";
case NotImplementedException e:
return message = "not implement!";
default:
return message = "server internal error!";
}
}
}
}

61
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.Ali.json

@ -1,61 +0,0 @@
{
"App": {
"SelfUrl": "https://localhost:44363",
"CorsOrigins": "https://*.CompanyNameProjectName.com,http://localhost:8083,http://localhost:8081,https://localhost:44307,"
},
"StringEncryption": {
"DefaultPassPhrase": "MY2bTgp2iaHUEp66"
},
"Settings": {
"Abp.Mailing.Smtp.Host": "127.0.0.1",
"Abp.Mailing.Smtp.Port": "25",
"Abp.Mailing.Smtp.UserName": "",
"Abp.Mailing.Smtp.Password": "",
"Abp.Mailing.Smtp.Domain": "",
"Abp.Mailing.Smtp.EnableSsl": "false",
"Abp.Mailing.Smtp.UseDefaultCredentials": "true",
"Abp.Mailing.DefaultFromAddress": "noreply@abp.io",
"Abp.Mailing.DefaultFromDisplayName": "ABP application"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Microsoft.AspNetCore": "Information",
"System": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Routing": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Error"
}
}
},
"ConnectionStrings": {
"Default": "Data Source=mysql.development.cn;Database=CompanyNameProjectName;uid=root;pwd=mysql@;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
},
"Cache": {
"Redis": {
"ConnectionString": "redis.development.yhglobal.cn,password=OD5VbzIuAWsHOZV5ncYx9xaDsAMsKXn7",
"DatabaseId": 231
}
},
"AuthServer": {
"Authority": "https://localhost:44363",
"RequireHttpsMetadata": "false"
},
"Jwt": {
"Audience": "http://localhost:5010", //客户端标识
"SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=",
"Issuer": "CompanyNameProjectName", //签发者
"ExpirationTime": 2 //过期时间 hour
},
"LogToElasticSearch": {
"Enabled": "true",
"ElasticSearch": {
"Url": "http://es.cn",
"IndexFormat": "CompanyNameProjectName.admin.api.dev-{0:yyyy.MM.dd}",
"UserName": "CompanyNameProjectName",
"Password": "TCYobkZxWu0ELYZY"
}
}
}

61
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.Staging.json

@ -1,61 +0,0 @@
{
"App": {
"SelfUrl": "https://localhost:44363",
"CorsOrigins": "https://*.CompanyNameProjectName.com,http://localhost:8083,http://localhost:8081,https://localhost:44307,"
},
"StringEncryption": {
"DefaultPassPhrase": "MY2bTgp2iaHUEp66"
},
"Settings": {
"Abp.Mailing.Smtp.Host": "127.0.0.1",
"Abp.Mailing.Smtp.Port": "25",
"Abp.Mailing.Smtp.UserName": "",
"Abp.Mailing.Smtp.Password": "",
"Abp.Mailing.Smtp.Domain": "",
"Abp.Mailing.Smtp.EnableSsl": "false",
"Abp.Mailing.Smtp.UseDefaultCredentials": "true",
"Abp.Mailing.DefaultFromAddress": "noreply@abp.io",
"Abp.Mailing.DefaultFromDisplayName": "ABP application"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Microsoft.AspNetCore": "Information",
"System": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Routing": "Information",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Error"
}
}
},
"ConnectionStrings": {
"Default": "Data Source=mysql.development.cn;Database=CompanyNameProjectName;uid=root;pwd=mysql@;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
},
"Cache": {
"Redis": {
"ConnectionString": "redis.development.yhglobal.cn,password=OD5VbzIuAWsHOZV5ncYx9xaDsAMsKXn7",
"DatabaseId": 231
}
},
"AuthServer": {
"Authority": "https://localhost:44363",
"RequireHttpsMetadata": "false"
},
"Jwt": {
"Audience": "http://localhost:5010", //客户端标识
"SecurityKey": "dzehzRz9a8asdfasfdadfasdfasdfafsdadfasbasdf=",
"Issuer": "CompanyNameProjectName", //签发者
"ExpirationTime": 2 //过期时间 hour
},
"LogToElasticSearch": {
"Enabled": "true",
"ElasticSearch": {
"Url": "http://es.cn",
"IndexFormat": "CompanyNameProjectName.admin.api.dev-{0:yyyy.MM.dd}",
"UserName": "CompanyNameProjectName",
"Password": "TCYobkZxWu0ELYZY"
}
}
}

2
content/aspnetcore/host/CompanyName.ProjectName.HttpApi.Host/appsettings.json

@ -31,7 +31,7 @@
}
},
"ConnectionStrings": {
"Default": "Data Source=localhost;Database=CompanyNameProjectName;uid=root;pwd=mypassword;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
"Default": "Data Source=localhost;port=3307;Database=CompanyNameProjectName;uid=root;pwd=mypassword;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
},
"Cache": {
"Redis": {

12
content/aspnetcore/src/CompanyName.ProjectName.Application/Attributes/DontWrapResultAttribute.cs

@ -0,0 +1,12 @@
using System;
namespace CompanyNameProjectName.Attributes
{
public class DontWrapResultAttribute : Attribute
{
public DontWrapResultAttribute()
{
}
}
}

31
content/aspnetcore/src/CompanyName.ProjectName.Application/Attributes/WrapResult.cs

@ -0,0 +1,31 @@
namespace CompanyNameProjectName.Attributes
{
public class WrapResult<T>
{
public bool Success { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public WrapResult()
{
Success = true;
Message = "Success";
Data = default;
}
public void SetSucess(T data, string message = "Success")
{
Success = true;
Data = data;
}
public void SetFail(string message = "Fail")
{
Success = false;
Message = message;
}
}
}

1
content/aspnetcore/src/CompanyName.ProjectName.Application/Users/LoginAppService.cs

@ -95,7 +95,6 @@ namespace CompanyNameProjectName.Users
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateToken(tokenDescriptor);
return handler.WriteToken(token);
}
}
}

2
content/aspnetcore/tools/CompanyName.ProjectName.DbMigrator/appsettings.json

@ -1,5 +1,5 @@
{
"ConnectionStrings": {
"Default": "Data Source=localhost;Database=CompanyNameProjectName;uid=root;pwd=mypassword;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
"Default": "Data Source=localhost;port=3307;Database=CompanyNameProjectName;uid=root;pwd=mypassword;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true"
}
}

30
content/vue/.husky/_/husky.sh

@ -0,0 +1,30 @@
#!/bin/sh
if [ -z "$husky_skip_init" ]; then
debug () {
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
}
readonly hook_name="$(basename "$0")"
debug "starting $hook_name..."
if [ "$HUSKY" = "0" ]; then
debug "HUSKY env variable is set to 0, skipping hook"
exit 0
fi
if [ -f ~/.huskyrc ]; then
debug "sourcing ~/.huskyrc"
. ~/.huskyrc
fi
export readonly husky_skip_init=1
sh -e "$0" "$@"
exitCode="$?"
if [ $exitCode != 0 ]; then
echo "husky - $hook_name hook exited with code $exitCode (error)"
exit $exitCode
fi
exit 0
fi
Loading…
Cancel
Save