服务层应该返回什么类型的结果?

发布于 2024-10-09 13:32:10 字数 852 浏览 0 评论 0原文

举例来说,假设我有一个创建帖子的 PostsService

public class PostsService : IPostsService
{
    public bool Create(Post post)
    {
        if(!this.Validate(post))
        {
            return false;
        }

        try
        {
            this.repository.Add(post);
            this.repository.Save();
        }
        catch(Exception e)
        {
            return false;
        }
    }
}

这样做的问题是,如果在存储库操作期间引发异常,该异常就会被吞掉。 Create() 返回 false,消费者只知道 Post 未添加,但不知道为什么

相反,我想到了一个 ServiceResult 类:

public class ServiceResult
{
    public bool Success { get; private set; }
    public Exception Exception { get; private set; }
}

这是一个好的设计吗?或者我是否需要向消费者报告这些错误?说“添加帖子时发生错误”就足够了吗?然后记录服务内部的异常?

任何其他建议表示赞赏。

Say, for example, that I have a PostsService that creates a post:

public class PostsService : IPostsService
{
    public bool Create(Post post)
    {
        if(!this.Validate(post))
        {
            return false;
        }

        try
        {
            this.repository.Add(post);
            this.repository.Save();
        }
        catch(Exception e)
        {
            return false;
        }
    }
}

The problem with this is that if an exception is thrown during the repository actions, it's swallowed. Create() returns false and all that the consumer knows is that the Post wasn't added, but doesn't know why.

Instead, I was think of having a ServiceResult class:

public class ServiceResult
{
    public bool Success { get; private set; }
    public Exception Exception { get; private set; }
}

Would this be a good design? Or do I even need to report these errors to the consumer? Is it sufficient to say "An error occurred while adding the post." and then log the exception inside of the service?

Any other suggestions are appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

泪眸﹌ 2024-10-16 13:32:10

我认为这取决于原因的范围。在我看来,一些失败原因不应该引起消费者的关注,这些应该记录在服务中。另一方面,在某些情况下,应该告知消费者这些原因,因此除了记录它们之外,您还应该以某种方式指出错误的原因。

例如,假设用户注册服务的一个非常常见的用例。您很可能有一个唯一的属性来标识用户(用户名、电子邮件等)。这种唯一性必须由服务强制执行(也可能在数据库级别)。现在,如果消费者尝试注册用户并且由于违反约束而失败,则应通知消费者此原因(以便它可以尝试其他用户句柄)。大多数情况下,失败的验证都属于同一场景。

另一方面,如果存在某种内部数据库问题,则不应向消费者告知详细信息(因为这是服务的专有责任;而且消费者无论如何也无能为力)。

考虑到这一点,我想说在很多情况下返回布尔值是不够的。因此,拥有某种 ServiceResult 类型听起来并不是一个坏主意。不过,我不确定是否会包含 Exception 。但也许您可以创建某种特定于您的服务的 ServiceExpection。有时错误代码也适合这种情况。

I think it depends on the scope of why. Some failure reasons shouldn't concern the consumer, in my opinion and these should be logged in the service. On the other hand, there are cases when the consumer should be informed of these reasons, so in addition to logging them, you should indicate the cause of the error somehow.

For instance, suppose a very common use case of a user registration service. It's quite likely that you'd have a unique attribute that identifies the user (user handle, email, etc). This uniqueness must be enforced by the service (and probably at a database level too). Now, if the consumer is trying to register a user and it fails because the constrain was violated, the consumer should be notified of this reasons (so it could try some other user handle). Failed validations fall in the same scenario, most of the times.

If there's some kind of internal DB problem on the other hand, the consumer should not be informed of the details (because that's exclusive responsibility of the service; and it's not something the consumer can do anything about anyway).

Considering that, I'd say returning a boolean is insufficient in many cases. So having some kind of ServiceResult type doesn't sound like a bad idea. I'm not sure I would include the Exception though. But perhaps you can create some kind of ServiceExpection, specific to your service. Sometimes error codes are just fine for this, too.

坦然微笑 2024-10-16 13:32:10

捕获所有异常确实是个坏主意。你只能抓住那些你可以合理处理的东西。你无法处理所有异常,那么为什么要捕获它呢?为了防止堆栈跟踪?

如果你不希望你的用户看到可怕的堆栈跟踪,我明白,但你确实希望它死掉并且死得很可怕,这样某些东西就不会被损坏。

假设服务抛出了 OutOfMemoryException,您只是捕获了它,并且不知道您的应用程序是否像筛子一样泄漏内存或不正确地分配大对象。

这是一件坏事。

如果您不希望用户看到出了什么问题,那么您应该将捕获更改为:

catch (Exception e)
{
    //send it to yourself, log it. Just don't swallow it.
    LogErrorAndEmailDevTeam(e); 
    throw new SaveFailedException("We're sorry. \n" + 
    "It's not your fault, but something bad happened.\n\n" + 
    "We've been notified and will fix it as soon as possible.");
}

但更好的是让应用程序惨死,而不是捕获该异常,以便在出现问题时快速知道。

您不希望应用程序继续处于损坏状态,但这正是catch (Exception) 的作用。你真的确定你能处理抛出的任何东西吗?

It's a really bad idea to catch all exceptions. You only catch that which you can reasonably handle. You can't handle all exceptions, so why are you catching it? To prevent a stack trace?

If you don't want your users to see a horrible stack trace, I get that, but you do want it to die and die horribly so that something isn't corrupted.

Let's say that service threw an OutOfMemoryException, you just caught it, and won't know whether or not your application is leaking memory like a sieve or is improperly allocating large objects.

That's a bad thing.

If you don't want your user to see what was wrong, then you should change your catch to say:

catch (Exception e)
{
    //send it to yourself, log it. Just don't swallow it.
    LogErrorAndEmailDevTeam(e); 
    throw new SaveFailedException("We're sorry. \n" + 
    "It's not your fault, but something bad happened.\n\n" + 
    "We've been notified and will fix it as soon as possible.");
}

But even better would be to let the application die a horrible death instead of catching that exception so that you know quickly when something goes wrong.

You don't want your application to continue in a corrupted state, but that's what catch (Exception) does. Are you really sure you can handle whatever is thrown?

好久不见√ 2024-10-16 13:32:10

(免责声明:根据我的经验,我在环境之间公开/使用服务方面做了很多工作,其中 WCF 之类的内置功能并不总是有用,而且人们并不总是能够控制使用该服务的客户端.)

在我的服务设计中,我越来越转向请求/响应系统。 (实际上,在这方面我大量使用了 agatha-rrsl 库.) 在该设计中,我有一个 BaseRequest 对象和一个 BaseResponse 对象,所有请求和响应类型都继承自它们。

继续您的想法,ServiceResult 类可以很好地作为这样一个 BaseResponse 的开始。它不一定需要实际的异常,但我倾向于包含以下一些内容:

  1. 一个 bool 指示任何请求的成功或失败,甚至是在成功时返回实际结果的请求。
  2. 自定义 Message 对象的 IList,它们本身包含某种消息类型(信息、警告、错误等)以及消息文本(也许还有一个额外的用户友好消息文本(用于 UI 显示需求)、堆栈跟踪(如果相关)等。

更进一步,我还喜欢在 BaseRequest 中包含一些内容,例如原始用户、主机等等。

这个想法是服务本身永远不应该抛出原始异常。无论谁使用该服务,都应该始终期望获得有效的响应,即使该响应表明某种失败。它应该期望每次都会返回一些基本信息,并知道如何处理这些信息。

(Disclaimer: In my experience I've worked a lot more with exposing/consuming services between environments, where the built-in functionality in something like WCF isn't always useful and one doesn't always have control over the client consuming the service.)

More and more in my service designs I've moved towards a request/response system. (Actually, I've made heavy use of the agatha-rrsl library in this regard.) In that design, I have a BaseRequest object and a BaseResponse object, from which all request and response types inherit.

Continuing with what you were thinking, that ServiceResult class would serve well as the start of such a BaseResponse. It doesn't necessarily need the actual exception, but a few things I tend to include are:

  1. A bool indicating success or failure for any request, even one which returns actual results on success.
  2. An IList<> of custom Message objects, which themselves contain a message type of some sort (Info, Warn, Error, etc.) as well as the message text (and perhaps an additional user-friendly message text for UI display needs), stack trace if relevant, etc.

Going further, I also like to include a few things in the BaseRequest such as the originating user, host, etc.

The idea is that the service itself should never throw a raw exception. Whoever is consuming the service should always expect to get a valid response, even if that response indicates a failure of some sort. It should expect some basic information to come back every time and know how to handle that information.

归属感 2024-10-16 13:32:10

是的,ServiceResult 类是个好主意,但我不会包含异常,有时您想报告错误,但您不想/不需要创建/抛出/捕获异常,相反,我会包含错误消息的集合或指示出了什么问题的枚举类型,例如 会员创建状态

另外,不要捕捉异常,它会使错误难以发现。

Yes, the ServiceResult class is a good idea, but I wouldn't include an exception, there are times you want to report errors but you don't want/need to create/throw/catch an exception, instead I would include a collection of error messages or an enum type indicating what went wrong, something like MembershipCreateStatus.

Also, don't catch Exception, it can make bugs hard to find.

南烟 2024-10-16 13:32:10

请记住,面向服务的体系结构的主要优点之一是解耦系统的组件。调用层对服务了解得越多,各层之间的耦合就越紧密。按照这个逻辑,最好让调用层尽可能少地了解错误,它需要知道的是服务调用失败。

调用层是否真的需要知道服务失败的原因?它到底能做什么呢?仅仅是为了向用户展示更好的信息吗?在这种情况下,用户真的关心细节吗?人们很容易认为用户想要了解更多信息,只是因为您作为开发人员这样做了。但开发人员应该从日志而不是最终用户消息中获取有关错误消息的信息。

Keep in mind that one of the key benefits of a service oriented architecture is decoupling components of the system. The more the calling layers have to know about the service the more tightly coupled the layers become. Following this logic it is better to have the calling layer know as little as possible about the error, all it needs to know is that the service call failed.

Does the calling layer really need to know why the service failed? What can it really do about it? Is it just so you can show a better message to the user? In this case does the user really care about the detail? It's easy to think the user wants to know more, just because you as the developer does. But developers should be getting information about error messages from logs, not from end user messages.

长梦不多时 2024-10-16 13:32:10

如果您使用 WCF,我建议不要使用 ServiceResult 或任何类似的内容来处理 SOA 层中的异常。

您应该为您的方法定义一个FaultContract。这将允许使用代码了解可以抛出的异常类型,并使用传统的 try/catch 块相应地处理它们。

这个StackOverflow问题具有故障契约的完整实现

您仍然可以让前端简单地说“发生了错误”,但是使用错误契约可以在将来需要时在代码的 UI 层上进行更好的错误处理。

If you're using WCF, I'd recommend against a ServiceResult, or anything similar for dealing with exceptions in a SOA layer.

You should define a FaultContract for your method. This would allow consuming code to know about the types of exceptions that can be thrown and handle them accordingly using traditional try/catch blocks.

This StackOverflow question has a full implementation of Fault Contracts.

You could still have your front-end simply say "An error has occurred", but using a fault contract would allow for better error handling on the UI layer of your code if it becomes needed in the future.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文