我开始使用SignalR,我有一个情况,我将有一个SignalR站点,它将向客户端广播消息,但我还需要一个实际触发这些消息的管理界面。管理页面将调用服务器端方法,这些方法将为常规用户调用客户端Javascript方法。所以我想我可以设置两个独立的集线器(一个用于管理员,一个用于其他人)或者我可以在单个集线器中使用方法,只能由管理员调用以检查授权。
但是除了授权之外,我想让SignalR在生成的Javascript代理类中不包含管理方法或管理中心,这样我就不会宣传它们的存在(再次 - 这不是唯一的安全性,我将检查授权)。我是否可以在各个集线器或集线器中的方法上设置属性或属性,以阻止它们包含在代理中(但仍然可以从Javascript调用它们)?我知道您可以在EnableJavaScriptProxies
中将false
设置为HubConfiguration
,但这似乎是全局性的,我想保留代理,因为我希望常规客户端成为使用。
答案 0 :(得分:7)
使用接口有一个技巧。由于代理只会在代理中生成公共方法,因此您可以使用以下界面创建集线器:
public class MyHub : Hub, IMyHub
{
void IMyHub.NotGeneratedOnClient()
{
}
public void GeneratedOnClient()
{
}
}
如果使用MyHub类型的对象,NotGeneratedOnClient方法将不可见,您只能使用interface访问它。由于方法不是公共代理生成器,因此不会将其添加到客户端代理
答案 1 :(得分:2)
我们今天无法从代理中排除特定方法。您必须重新实现自己的代理生成器,它基本上执行我们在默认impl中所做的操作,但具有某些属性的知识,可以跳过特定方法的生成。
我们可以想象在SignalR的未来版本中添加它。如果您对此感到强烈,请在github上提出问题。
这是默认实现(如果我们将更多方法设置为虚拟和非静态,那会更容易。)
答案 2 :(得分:0)
这是经过修改的 DefaultJavaScriptProxyGenerator ,具有以下更改:
首先,您需要更新依赖关系解析程序,以将新的 CustomJavaScriptProxyGenerator 用于IJavaScriptProxyGenerator接口。如果使用的是默认解析器,则可以这样设置自定义解析器:
map.RunSignalR( new HubConfiguration() { Resolver = new CustomDependencyResolver() } );
这是从DefaultDependecyResolver派生的自定义解析器:
namespace Microsoft.AspNet.SignalR { public class CustomDependencyResolver : DefaultDependencyResolver { MyDependencyResolver() : base() { var proxyGenerator = new Lazy(() => new CustomJavaScriptProxyGenerator(this)); Register(typeof(IJavaScriptProxyGenerator), () => proxyGenerator.Value); } } }
最后,这是新的 CustomJavaScriptProxyGenerator.cs 文件( HubMethodExcludeFromProxyAttribute 类在底部):
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Mods by Brain2000 using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using Microsoft.AspNet.SignalR.Json; using Microsoft.AspNet.SignalR.Hubs; using Newtonsoft.Json; namespace Microsoft.AspNet.SignalR.Hubs { public class CustomJavaScriptProxyGenerator : IJavaScriptProxyGenerator { protected static readonly Lazy _templateFromResource = new Lazy(GetTemplateFromResource); protected static readonly Type[] _numberTypes = new[] { typeof(byte), typeof(short), typeof(int), typeof(long), typeof(float), typeof(decimal), typeof(double) }; protected static readonly Type[] _dateTypes = new[] { typeof(DateTime), typeof(DateTimeOffset) }; protected const string ScriptResource = "Microsoft.AspNet.SignalR.Scripts.hubs.js"; protected readonly IHubManager _manager; protected readonly IJavaScriptMinifier _javaScriptMinifier; protected readonly Lazy _generatedTemplate; protected readonly Lazy _generatedTemplateWithComments; public CustomJavaScriptProxyGenerator(IDependencyResolver resolver) : this(resolver.Resolve(), resolver.Resolve()) { } public CustomJavaScriptProxyGenerator(IHubManager manager, IJavaScriptMinifier javaScriptMinifier) { _manager = manager; _javaScriptMinifier = javaScriptMinifier ?? NullJavaScriptMinifier.Instance; _generatedTemplate = new Lazy(() => GenerateProxy(_manager, _javaScriptMinifier, includeDocComments: false)); _generatedTemplateWithComments = new Lazy(() => GenerateProxy(_manager, _javaScriptMinifier, includeDocComments: true)); } public string GenerateProxy(string serviceUrl) { serviceUrl = JavaScriptEncode(serviceUrl); return _generatedTemplate.Value.Replace("{serviceUrl}", serviceUrl); } public string GenerateProxy(string serviceUrl, bool includeDocComments) { if (!includeDocComments) return GenerateProxy(serviceUrl); //use the includeDocComments: false cached version serviceUrl = JavaScriptEncode(serviceUrl); return _generatedTemplateWithComments.Value.Replace("{serviceUrl}", serviceUrl); } protected virtual string GenerateProxy(IHubManager hubManager, IJavaScriptMinifier javaScriptMinifier, bool includeDocComments) { string script = _templateFromResource.Value; var hubs = new StringBuilder(); var first = true; foreach (var descriptor in hubManager.GetHubs().OrderBy(h => h.Name)) { if (!first) { hubs.AppendLine(";"); hubs.AppendLine(); hubs.Append(" "); } GenerateType(hubManager, hubs, descriptor, includeDocComments); first = false; } if (hubs.Length > 0) { hubs.Append(";"); } script = script.Replace("/*hubs*/", hubs.ToString()); return javaScriptMinifier.Minify(script); } protected virtual void GenerateType(IHubManager hubManager, StringBuilder sb, HubDescriptor descriptor, bool includeDocComments) { // Get only actions with minimum number of parameters. var methods = GetMethods(hubManager, descriptor); var hubName = GetDescriptorName(descriptor); sb.AppendFormat(" proxies['{0}'] = this.createHubProxy('{1}'); ", hubName, hubName).AppendLine(); sb.AppendFormat(" proxies['{0}'].client = {{ }};", hubName).AppendLine(); sb.AppendFormat(" proxies['{0}'].server = {{", hubName); bool first = true; foreach (var method in methods) { if (!first) { sb.Append(",").AppendLine(); } GenerateMethod(sb, method, includeDocComments, hubName); first = false; } sb.AppendLine(); sb.Append(" }"); } protected virtual string GetDescriptorName(Descriptor descriptor) { if (descriptor == null) { throw new ArgumentNullException("descriptor"); } string name = descriptor.Name; // If the name was not specified then do not camel case if (!descriptor.NameSpecified) { name = JsonUtility.CamelCase(name); } return name; } protected virtual IEnumerable GetMethods(IHubManager manager, HubDescriptor descriptor) { return from method in manager.GetHubMethods(descriptor.Name).Where(md => md.Attributes.FirstOrDefault(a => (a.GetType() == typeof(HubMethodExcludeFromProxyAttribute))) == null) group method by method.Name into overloads let oload = (from overload in overloads orderby overload.Parameters.Count select overload).FirstOrDefault() orderby oload.Name select oload; } protected virtual void GenerateMethod(StringBuilder sb, MethodDescriptor method, bool includeDocComments, string hubName) { var parameterNames = method.Parameters.Select(p => p.Name).ToList(); sb.AppendLine(); sb.AppendFormat(" {0}: function ({1}) {{", GetDescriptorName(method), Commas(parameterNames)).AppendLine(); if (includeDocComments) { sb.AppendFormat(" /// Calls the {0} method on the server-side {1} hub.\nReturns a jQuery.Deferred() promise.", method.Name, method.Hub.Name).AppendLine(); var parameterDoc = method.Parameters.Select(p => String.Format(CultureInfo.CurrentCulture, " /// Server side type is {2}", p.Name, MapToJavaScriptType(p.ParameterType), p.ParameterType)).ToList(); if (parameterDoc.Any()) { sb.AppendLine(String.Join(Environment.NewLine, parameterDoc)); } } sb.AppendFormat(" return proxies['{0}'].invoke.apply(proxies['{0}'], $.merge([\"{1}\"], $.makeArray(arguments)));", hubName, method.Name).AppendLine(); sb.Append(" }"); } protected virtual string MapToJavaScriptType(Type type) { if (!type.IsPrimitive && !(type == typeof(string))) { return "Object"; } if (type == typeof(string)) { return "String"; } if (_numberTypes.Contains(type)) { return "Number"; } if (typeof(IEnumerable).IsAssignableFrom(type)) { return "Array"; } if (_dateTypes.Contains(type)) { return "Date"; } return String.Empty; } protected virtual string Commas(IEnumerable values) { return Commas(values, v => v); } protected virtual string Commas(IEnumerable values, Func selector) { return String.Join(", ", values.Select(selector)); } protected static string GetTemplateFromResource() { //this must remain "DefaultJavaScriptProxyGenerator" because the resource "Microsoft.AspNet.SignalR.Scripts.hubs.js" lives there using (Stream resourceStream = typeof(DefaultJavaScriptProxyGenerator).Assembly.GetManifestResourceStream(ScriptResource)) { var reader = new StreamReader(resourceStream); return reader.ReadToEnd(); } } protected virtual string JavaScriptEncode(string value) { value = JsonConvert.SerializeObject(value); // Remove the quotes return value.Substring(1, value.Length - 2); } } [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public sealed class HubMethodExcludeFromProxyAttribute : Attribute { } }
现在,您所需要做的就是装饰所有集线器方法,例如:
public class MyHub : Hub { [HubMethodExcludeFromProxy] public void NotGeneratedOnClient() { } public void GeneratedOnClient() { } }
EDIT :依赖项注入存在一个问题,如果您有两个不同的解析器实例,一个在GlobalHost.DependencyResolver中,一个在Signalr配置中,则有时会导致远程方法不行。解决方法如下:
//use only !ONE! instance of the resolver, or remote SignalR functions may not run! var resolver = new CustomDependencyResolver(); GlobalHost.Configuration.DependencyResolver = resolver; map.RunSignalR( new HubConfiguration() { Resolver = resolver; } );