从 Npgsql.PostgresException 获取客户端可读消息

Getting a client readable message from an Npgsql.PostgresException

我正在使用 PostgreSQL 编写 Web api 并将检查数据库约束作为验证过程的一部分,但我也有一个全局异常过滤器作为备用,以防在保存时出现问题。我的问题是异常似乎没有任何我可以在不进行某些处理的情况下呈现给客户端的消息。添加的图像是来自断点的 PostgresException 数据。例如,在这种情况下,我想要类似于 "Asset Number x already exists" 或 "Asset Number must be unique" 的东西。这是可以在某处配置的东西吗?最有意义的地方是约束创建代码,但我找不到这样做的选项。

modelBuilder.Entity<AssetItem>().HasIndex(item => new { item.AssetNumber }).IsUnique();

public class DbExceptionFilter : IExceptionFilter 
{
    private const string UNIQUE_EXCEPTION = "23505";
    public async void OnException(ExceptionContext context)
    {
        var exceptionType = context.Exception.InnerException.GetType().FullName;

        if (exceptionType == "Npgsql.PostgresException")
        {
            var pgException = (PostgresException) context.Exception.InnerException;

            switch(pgException.SqlState)
            {
                case UNIQUE_EXCEPTION:
                    var error = new {error = "Unique Error Here"};
                    await WriteJsonErrorResponse(context.HttpContext.Response, HttpStatusCode.BadRequest, error);
                    return;
            }
        } 
        else 
        {
            var error = new { error = "Unexpected Server Error"};
            await WriteJsonErrorResponse(context.HttpContext.Response, HttpStatusCode.InternalServerError, error);
            return;
        }
    }

    private async Task WriteJsonErrorResponse(HttpResponse response, HttpStatusCode statusCode, dynamic error)
    {
        response.ContentType = "application/json";
        response.StatusCode = (int) statusCode;
        await response.Body.WriteAsync(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(error)));
    }
}

PostgreSQL 提供的最接近用户可读消息的是在 PostgresException 上公开的消息文本。

但是,作为一般规则,将数据库错误直接暴露给用户(包括网络 API 用户)并不是一个好主意:这些错误旨在直接与数据库交互的应用程序(即您的应用程序).这些消息通常对 API 的用户没有多大意义,更重要的是,它们会泄露有关数据库架构的潜在敏感信息,因此不安全。正如您似乎正在做的那样 dump/serialize 对用户的整个异常尤其成问题(使用 JsonConvert.SerializeObject)。

这里的最佳做法是识别用户可能触发的合法数据库异常,拦截这些异常和 return 以及您自己的措辞适当的消息(例如 "A user with that name already exists")。

附带说明一下,要识别 PostgresException,您可以简单地使用 C# 模式匹配,而不是获取异常的名称并与之进行比较:

if (context.Exception.InnerException is PostgresException postgresException)
{
    // ...
}