目录
介绍
背景
建议的解决方案:将Logger中的错误日志与发送到应用程序的消息链接起来
可恢复和灾难性错误
管理生产中的灾难性错误
管理开发中的灾难性错误
管理生产中的可恢复错误
管理开发中的可恢复错误
使用代码
兴趣点
- 下载演示 - 16.3 KB
报告错误似乎是一项微不足道的任务,但如果您不仔细分析您的系统,您可能会遇到安全问题,或者不向用户和技术服务提供正确的信息来报告和解决问题。在本文中,我们将介绍实际问题,如何在开发和生产的不同阶段对其进行管理,以及对其进行管理的代码命题。
背景在早期基于服务器代码的WEB API中,向Web发送错误的问题并没有那么复杂。服务器管理对API的请求,当发生错误时,您可以将技术错误信息连同解决该错误的所有信息发送给WEB服务器,而Web服务器仅在浏览器中向用户发送一个友好的信息错误。在这种环境下,你不需要限制你的API向WEB服务器发送的信息量,因为通信只是服务器之间的。假设您的服务位于内网或您使用的是加密协议。
随着基于客户端的Web作为Angular和其他框架的流行,对API服务的调用是直接从浏览器完成的。这意味着,如果您继续以服务器基础WEB类型ASP.NET或NET MVC中使用的相同方式发送错误信息,则错误的技术细节将被潜在黑客以明文形式发现浏览器,无论您使用什么协议来保护信息。这种情况是我们系统中的一个重要安全漏洞。
一种可能的解决方案是创建一个字段,该字段与友好消息一起发送到浏览器,并将相同的值作为记录信息的一部分存储在日志记录器系统中,这样您就可以在日志记录器中找到有关错误的技术信息。将错误与日志记录器中的记录联系起来可以在没有安全风险的情况下解决问题,为技术服务提供必要的信息以纠正生产中的错误情况。
您可以使用时间戳作为字段来链接要使用的错误和日志记录器系统中的日志。让我们在一个例子中看到这一点(见下图)。
假设在微服务“Users”中引发异常,系统会创建一个时间戳并将技术信息发送到日志记录器系统,同时向客户端浏览器发送相同的时间戳和关于错误的友好信息消息,其中还包括失败服务的名称。用户致电支持中心,并给出带有识别错误的时间戳的错误,支持中心向负责生产错误的开发人员发送电子邮件。开发人员转到“user”微服务日志记录器并查找给定日期时间中的条目,其中包含识别问题所需的所有信息。
其他需要解决的问题是我们将向API客户端发送什么类型的HTTP错误。当错误是灾难性的时,逻辑选择是代码500意味着在调用API时发生了一些不可恢复的事情。在这种情况下,可以轻松选择向浏览器发送与记录的技术错误相关联的友好错误。
如果错误可通过用户可以执行的某些操作恢复,则使用错误类型400,该错误类型可以涵盖数据验证、数据缺失或其他类似错误。通常,错误400应该是不会阻止用户继续或纠正服务响应的错误。在这种情况下发送给用户的错误消息应该能够向最终用户解释会发生什么以及可以采取什么措施来避免它。让我们看看生产和开发人员条件中的错误。
生产中未恢复的错误应该只报告发生了灾难性错误,原因有二:一是出于安全考虑,二是用户无法采取任何措施来解决错误。我们可以做的是向用户发送一个带有时间戳的友好错误,并将技术问题发送给具有相同时间戳的日志记录器系统。发送给用户的友好错误应该通过时间戳与日志记录器条目相关联,便于在日志记录器上定位错误的相关技术条目。
在开发的情况下,我们可以简单地将技术错误发送到浏览器,因为只有开发团队和可能的QA团队才能看到信息。此外,我们还需要将错误发送到日志记录器系统,并维护发送到浏览器的错误与日志条目之间的关系。
在可恢复错误的情况下,我们可以将信息发送给允许他/她从错误中恢复的信息。例如,在验证错误的情况下:更改错误名称或填写缺失字段。
在生产中记录此错误的决定可以是可选的,您可以选择记录该类型的错误,在这种情况下,建议将错误级别移至此类型WARN或INFO允许ERROR仅过滤灾难性信息的错误。
在开发环境和验证错误的情况下,我们继续类似于生产向客户端发送所有可能的信息给客户端。但是我们建议在日志记录器系统中记录这些信息。这在开发过程中解决问题很重要,因为对API的请求不好。
在本文中,我们提出了一个对象,它允许我们管理此处讨论的所有不同类型的错误。为此,我们使用以下类:
///
/// Error Message
///
public class ErrorManager : IErrorManager
{
///
/// This link the log record with the error
/// message
///
public DateTime DateCreated { get; set; }
///
/// List of Validations or internal Errors
/// Used to reported error conditions that does
/// not raise exceptions. Normally Error 400 Type
///
public List Errors { get; set; }
///
/// Friendly description, Reserved for
/// Exceptions raised Errors type 500
///
public string Description { get; set; }
///
/// This is the technological Error, should only be
/// used in non production environment.
/// Must be empty in production
///
public Exception DebugException { get; set; }
///
/// Return a initializated error Manager
///
///
public static IErrorManager Factory()
{
IErrorManager manager = new ErrorManager();
manager.Errors = new List();
return manager;
}
}
并保存错误响应列表,以下类:
///
/// Hold error information only for the developer
/// or technical service
///
public class ErrorResponse
{
///
/// Can be a numeric code
///
public string Code { get; set; }
///
/// Description of the error, can be a
/// portion of the stack trace.
///
public string Description { get; set; }
}
正如您在此对象中每个属性的注释中看到的那样,您可以将所有用于错误类型400或500的信息放在您的web.xml文件中。处于生产或开发阶段。您唯一需要考虑的是根据实验的错误使用属性。
此类应与日志记录器系统一起使用,以允许将记录的错误与发送给用户的信息连接起来。
现在让我们看看如何使用此类来管理.NET Core 3.1 应用程序中的一般错误。下一个代码片段解释了如何在startup.cs文件中的应用程序的一般错误句柄中配置要使用的类。
///
/// This method gets called by the runtime.
/// Use this method to configure the HTTP request pipeline.
///
///
///
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// General Manager for Exceptions
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var errorFeature = context.Features.Get();
DateTime errorReportedtime = DateTime.UtcNow;
ErrorManager errorManager = new ErrorManager()
{
DateCreated = errorReportedtime,
};
if (!env.IsProduction())
{
errorManager.DebugException = errorFeature.Error;
errorManager.Description = errorFeature.Error.Message;
}
else
{
// Production
errorManager.Description = $" {errorManager.DateCreated}:
Please call our service that a error happen in NetCoreDemo";
}
// Call the Logger to enter the information
// TODO: Process the information to the logger using the errorManager
// TODO: Enter the information in the Logger included in the errorReportedTime
// Send the response back
var content = JsonConvert.SerializeObject(errorManager);
context.Response.StatusCode = 500; // 500 reserved for exceptions in app.
await context.Response.WriteAsync(content);
});
});
app.UseHttpsRedirection();
app.UseRouting();
// continue the code for other configurations....
}
观察代码管理与其他环境不同的生产,在生产的情况下,只有带有TimeStamp的通用错误被发送到浏览器。在其他环境的情况下,会向用户发送大量错误报告。
在这两种情况下,广泛的技术信息应存储在Logger中,并且日志记录器应使用时间戳与发送到浏览器客户端的消息链接。
此处的其余代码是获取对象并使用上下文响应对象返回它。
在错误400的情况下,您可以使用请求管道中的验证,并将相同的对象,不同的配置发送到客户端浏览器。执行此操作的代码如下:
///
/// This method gets called by the runtime.
/// Use this method to add services to the container.
///
///
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
// Resolving the 400 Validation Error Type
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
IErrorManager errorManager = ErrorManager.Factory();
StringBuilder messageb = new StringBuilder();
foreach (var item in context.ModelState)
{
messageb.Append($"Validation Failure in
{context.ActionDescriptor.DisplayName}
Parameter: {item.Key} Error: ");
foreach (var i in item.Value.Errors)
{
messageb.Append($" {i.ErrorMessage} - ");
}
errorManager.Errors.Add(new ErrorResponse()
{
Code = "400",
Description = messageb.ToString()
});
}
// Enter the data time
DateTime errorTime = DateTime.UtcNow;
errorManager.DateCreated = errorTime;
errorManager.Description =
$"{errorTime}: Validation Error in NetCoreDemo";
// Call the Logger to enter the information
// TODO: Process the information to the logger using the errorManager
// TODO: Enter the information in the Logger
// included in the errorReportedTime
var error = new BadRequestObjectResult(errorManager);
return error;
};
});
// Code continue ....
}
在这段代码中,我们使用Errors属性的数组Errors来列出验证模型状态中存在的验证错误。此外,我们在此处提供了错误的描述以及指向要发送到日志记录器系统的相同错误的链接。请注意,在这里,我们为生产和开发环境发送了相同的信息。
这个例子的完整代码可以从这里下载。
兴趣点- 您不应该将生产中的技术错误发送到基于浏览器的Web风格的Angular,相反,您应该将错误的技术数据存储在日志记录器系统中,并且只向用户发送友好的错误。
- 您应该随友好消息一起发送将友好消息与日志记录器系统中存储的信息链接的可能性。
- 您应该始终使用同一对象来报告所有类型的错误。您应该针对生产和开发环境的情况自定义对象。
Security Concern when reporting Errors in Client base Web Applications - CodeProject