存储过程API的最佳实践是什么?

4
我们正在向产品中添加一些存储过程,可以被第三方客户端调用。有没有关于参数验证、返回值、RAISERROR等方面的最佳实践?
第三方客户端将无法直接访问表格,只能访问某些存储过程。受存储过程影响的表格已经很好地限制了,但是我们希望在存储过程被错误调用时提供尽可能详细的错误信息,以便用户更加友好。
2个回答

3
  • 使用TRY/CATCH块
  • 抛出有意义的消息(我使用前缀,如“INFO:”来区分我的错误和数据库错误)

示例:

SET NOCOUNT, XACT_ABORT ON
...
BEGIN TRY
    IF @parameter1 IS NULL
        RAISERROR ('INFO: "parameter1" should not be blank', 16, 1)

    IF @parameter2 < 0
        RAISERROR ('INFO: "parameter2" must be greate then zero', 16, 1)

    ...

END TRY
BEGIN CATCH
    DECLARE @MyError nvarchar(2048)
    SELECT @MyError = ERROR_MESSAGE() -- + other stuff, like stored proc name perhaps
    RAISERROR (@MyError, 16, 1)
    ...
END CATCH

1
每次使用 RAISERROR 时,应该使用不同的状态码:(...,16,1),(...,16,2),(...,16,3)。这样当客户调用支持并提供错误信息(消息、代码、严重性、状态)时,支持人员可以快速定位引发错误的确切位置,这正是状态的目的。 - Remus Rusanu
我们会稍微复杂些,包括记录每个错误并将ERROR与INFO分离。记录对象、错误对象、行号等信息。但是:我们将真正的错误与"哎呀,重复值"分开,因为后者只是提供信息,而不是真正的错误。 - gbn

2
提供人类可以理解的信息性错误消息并不难。只需使用具有描述性文本的RAISERROR即可。稍微更困难的是提高本地化文本,这意味着正确使用sp_addmessage及其相关内容。真正困难的问题是引发程序可以响应的错误。这意味着必须有适当记录的错误代码(严重性状态),并且在API中使用它们时需要遵循严格的代码规范。
而且,不要忘记正确的事务嵌套。我在我的博客上有一个示例,介绍如何正确处理与T-SQL异常相结合的事务:异常处理和嵌套事务
不幸的是,就整个客户端/T-SQL堆栈而言,异常处理存在一些问题。最值得注意的是,如果捕获了T-SQL异常,就无法重新抛出它,因此客户端不能期望典型的系统错误编号。请参见SQL Server:使用原始异常编号重新抛出异常。这样会使你几乎没有办法传达正确的错误信息,除非你使用自己的错误编号超过50000范围,但随着“翻译”错误代码数量的增加,这种方法非常麻烦,并且只能使用错误消息字符串作为异常信息。

非Windows客户端,例如使用FreeTDS的*nix客户端是否存在互操作性问题?在他们的文档中很少提到RAISERROR处理。 - Aidan Ryan
据我所知,没有这样的情况。RAISERROR引发与系统错误相同类型的错误,而FreeTDS知道如何处理这些错误。 - Remus Rusanu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接