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

时间:2014-06-02 09:25:05

标签: c# exception signalr signalr.client

使用SignalR,当hub方法返回特定值时,是否有可能调用.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
}

替代方法是抛出异常,但我想避免这种情况,因为错误实际上不是服务器故障,而是用户故障。

2 个答案:

答案 0 :(得分:2)

假设您确信异常不是适合您的工具,您可以使用一些自定义属性来标记必须将false返回值转换为错误的方法,然后拦截任何来自BuildIncoming的{​​{1}}来电:

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

从里面你可以拦截对原始方法的调用,检查它是否标记了你的属性以及它是否返回HubPipelineModule,如果是这种情况你可以从那里抛出异常。最重要的是,您仍然会抛出异常使其调用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

答案 1 :(得分:0)

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

一般情况下,我可能会坚持使用例外情况,但如果您正在寻找其他替代方案,您还可以修改客户端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方法都会拒绝promise。 与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) { });

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