调用.fail(error)而不抛出异常

本文关键字:抛出异常 error fail 调用 | 更新日期: 2023-09-27 18:06:15

使用SignalR,当集线器方法返回特定值时,是否有可能调用.fail而不是.done ?

也许使用SignalR管道?

public bool Delete(int addressId)
{
    // User should not be able to delete default address
    if(AddressService.IsDefaultAddressOfCustomer(addressId))
        return false; // Should call .fail() on client
    AddressService.Delete(addressId);
    return true; // Should call .done() on client
}

另一种选择是抛出异常,但我希望避免这种情况,因为错误不是真正的服务器故障,而是用户故障。

调用.fail(error)而不抛出异常

假设您真的确信异常不是适合您的工具,您可以使用您将定义的一些自定义属性来标记方法,其中false返回值必须转换为错误,然后拦截来自HubPipelineModuleBuildIncoming传入呼叫:

http://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubs.hubpipelinemodule.buildincoming (v = vs.118) . aspx

从里面,你可以拦截调用你的原始方法,检查如果它被标记与你的属性,如果它返回false,如果是这种情况下,你可以抛出一个异常从那里。底线是,您仍然会抛出异常以使其调用客户端.fail(),但该异常不会使您的业务逻辑膨胀。像这样:

public class FailPipelineModule : HubPipelineModule
{
    public override Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
    {
        return base.BuildIncoming(context =>
        {
            var r = (bool)(invoke(context)).Result;
            if (context.MethodDescriptor.Attributes.Any(a => typeof(FailAttribute) == a.GetType()) && !r)
                throw new ApplicationException("false");
            return Task.FromResult((object)r);
        });
    }
}

你需要定义FailAttribute,用它来标记集线器的方法,并在启动时注册FailPipelineModule

正如Wasp所指出的那样,使用SignalR JavaScript客户端,只有在设置了hubResult.Error时才会拒绝承诺,这只有在处理请求时抛出异常时才会发生。没有办法使用hub管道来修改它。

一般来说,我可能会坚持使用异常在这种情况下,但如果你正在寻找另一种选择,你也可以修改客户端jquery.signalr-*.js代码,特别是hubProxy原型invoke方法。它有一个决定是解决还是拒绝承诺的条件:

if (result.Error) {
   // code to reject ...
} else {
    connection.log("Invoked " + that.hubName + "." + methodName);
    d.resolveWith(that, [result.Result]);
}

你可以修改else代码块:

if (result.Error) {
    // code to reject ...
} else {
    connection.log("Invoked " + that.hubName + "." + methodName);
    if (typeof result.Result === "boolean" && !result.Result) {
        d.rejectWith(that, [result.Result]);
    } else {
        d.resolveWith(that, [result.Result]);
    }
}

那么所有返回false的hub方法将拒绝承诺。与Wasp的解决方案相比,它的优势在于不必创建属性,而且总体上代码更少。缺点是,由于您手动编辑SignalR代码,因此它可能不太容易维护(因此,如果您这样做,它肯定应该在某处记录,并且您必须自己最小化脚本,而不是使用打包的脚本)。

一个更易于维护的客户端替代方案是包装集线器API,并返回你自己的延迟,例如:

var myHub = {
    server: $.connection.myHub.server,
    init: function() {
        for (var methodName in this.server) {
            if (this.server.hasOwnProperty(methodName)) {
                this[methodName] = function() {
                    var deferred = $.Deferred();
                    this.server[methodName].apply(this.server, arguments)
                        .done(function(result) {
                            // reject if server hub method returned false
                            if (result === false) deferred.reject(result);
                            deferred.resolve(result);
                        });
                    return deferred.promise();
                };
            }
        }
    }
};
myHub.init();

然后不调用

$.connection.myHub.server.someMethod("hello", "world")
    .done(function(result) { })
    .fail(function(result) { });

你会调用

myHub.someMethod("hello", "world")
    .done(function(result) { })
    .fail(function(result) { });

这样,您就可以完全控制如何解释返回值。