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

时间:2020-02-03 03:57:54

标签: asp.net-core ef-code-first npgsql

我正在使用PostgreSQL编写Web api,并在验证过程中检查数据库约束,但是我还有一个全局异常过滤器作为后备,以防万一保存时遇到了麻烦。我的问题是,异常似乎没有经过处理就可以呈现给客户端的任何消息。添加的图像来自断点的PostgresException数据。例如,在这种情况下,我想要类似“资产编号x已经存在”或“资产编号必须唯一”的名称。这可以在某处配置吗?最有意义的地方是约束创建代码,但是我找不到这样做的选择。

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)));
    }
}

Exception Properties

1 个答案:

答案 0 :(得分:1)

PostgreSQL提供的与用户可读消息最接近的是PostgresException上显示的消息文本。

但是,一般而言,将数据库错误直接暴露给用户(包括Web API用户)不是一个好主意:这些错误旨在用于直接与数据库进行交互的应用程序(即您的应用程序)。这些消息通常对您的API用户没有多大意义,更重要的是,它们会泄漏有关数据库架构的潜在敏感信息,因此并不安全。像您正在做的那样(使用JsonConvert.SerializeObject)将整个异常转储/序列化给用户,这尤其有问题。

此处的最佳做法是识别用户可能触发的合法数据库异常,拦截这些异常并返回您自己的消息,并用适当的措词(例如,“具有该名称的用户已经存在”)。

作为旁注,要标识PostgresException,而不必获取异常的名称并与之进行比较,您可以简单地使用C#模式匹配:

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