Adding some filters to hub methods (like AcctionFilters in MVC) - signalr

Is there anyway to define and add method filters for hub functions (like ActionFilters in mvc)
I mean something like this :
public class MyHub : Hub
{
[Log]
public string RegisterUser(UserModel model){
...
}
}
where I can do some control inside the LogAttribute implementation.

You should be able to achieve similar functionality to action filters in ASP.NET MVC by using SignalR's Hub pipeline:
public class LoggingPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
Debug.WriteLine("Invoking '{0}.{1}({2})'.",
context.MethodDescriptor.Hub.Name,
context.MethodDescriptor.Name,
string.Join(", ", context.Args));
return base.OnBeforeIncoming(context);
}
protected override object OnAfterIncoming(object result, IHubIncomingInvokerContext context)
{
Debug.WriteLine("Finished Invoking '{0}.{1}'. Returned '{2}'.",
context.MethodDescriptor.Hub.Name,
context.MethodDescriptor.Name,
result);
return base.OnAfterIncoming(result, context);
}
}
If you only want to log for methods with a custom attribute attached, you can check for your custom attribute before logging:
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
if (context.MethodDescriptor.Attributes.OfType<MyAttribute>().Any())
{
// Log here.
}
return base.OnBeforeIncoming(context);
}
You can register your module before your call to MapSignalR:
public void Configuration(IAppBuilder app)
{
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
app.MapSignalR();
}

Related

How to inject service into custom ActionFilterAttribute (Web API)?

I tried this answer: [https://stackoverflow.com/questions/18406506/custom-filter-attributes-inject-dependency][1] to implement ActionFilterAttribute (System.Web.Http.Filters) for Web API project (not MVC). But my custom attribute never called in controller. I would be grateful for any advice.
Custom attribute:
public class MyAttribute : FilterAttribute { }
Filter:
public class MyFilter : ActionFilterAttribute
{
private readonly IMyService _myService;
public MyFilter(IMyService myService)
{
_myService = myService;
}
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
//do some with actionContext
throw new Exception("You can`t go here");
}
}
Controller method:
[My] // Not called
[HttpPost]
[Route("/do-some")]
public async Task DoSome(string myString)
{
//do some
}
Register filter:
public partial class Startup
{
protected void ConfigureApi(IAppBuilder app, IContainer container)
{
var configuration = new HttpConfiguration();
//...
var serviceInstance = container.GetInstance<IMyService>();
configuration.Filters.Add(new MyFilter(serviceInstance));
}
}
Is something wrong here?
Almost everything is fine with the your code, but you should register your filter and service in another way.
In Asp Net Core WebAPI there several ways you can register your filter:
Globally - for all controllers, actions, and Razor Pages. More information in Microsoft documentation
For only one controller/method. More information in Microsoft documentation
Example of global registration:
services.AddControllers(options =>
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});
Example of method registration in Controller:
I want notice - in this case you should use ServiceFilter - this helps DI resolve any dependecines for your filter.
[HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{
}
This is my simple example for this task:
My SimpleService
public interface ISimpleService
{
void Notify(string text);
}
public class SimpleService : ISimpleService
{
public void Notify(string text)
{
Console.WriteLine($"Notify from {nameof(SimpleService)}. {text}");
}
}
ActionFilterAttribute
public class LoggerFilterAttribute : ActionFilterAttribute
{
private readonly ISimpleService _simpleService;
public LoggerFilterAttribute(ISimpleService simpleService)
{
_simpleService = simpleService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
_simpleService.Notify($"Method {nameof(OnActionExecuting)}");
}
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
_simpleService.Notify($"Method {nameof(OnActionExecutionAsync)}");
return base.OnActionExecutionAsync(context, next);
}
}
The main step - you should choose way of registration, because there is main difference between global registration and per controller/method in code.
If you want use this way of registration - you need only register global filter and this is enough. All magic will be do by WebAPI with DI registration.
services.AddControllers(options =>
{
options.Filters.Add(typeof(LoggerFilterAttribute));
});
If you want use registration per controller/method. You need to register your filter in DI. Because without it you will have Exception.
services.AddScoped<LoggerFilterAttribute>();
[HttpGet]
[ServiceFilter(typeof(LoggerFilterAttribute))]
public IEnumerable<WeatherForecast> Get()
{
}
The last step register my service
services.AddTransient<ISimpleService, SimpleService>();
Results

Is there a way to get the current controller instance in ASP.NET 5?

Is there a way to do this using DI? I tried IScopedInstance<Controller> but this gives me null. Poked around aspnet's source code but didn't win. Any ideas?
I have a controller that accepts different IPaymentMethods. The IPaymentMethod can be a ViewComponent that can render Views. If the IPaymentMethod is a ViewComponent, I want it to use MVC's built-in model binding on post back.
public class XController : Controller
{
// ctor, props, ...
public IActionResult Checkout()
{
return View(new Model
{
PaymentMethodId = 1,
PaymentMethodType = typeof(MyPaymentMethod) // The razor file will use this type to render it as a ViewComponent
});
}
[HttpPost]
public IActionResult Checkout(Model model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
paymentMethod.ProcessPayment();
// ..
}
}
This is where I need the controller to be injected. I wanted to make use of the built-in MVC validation and model binding.
public class MyPaymentMethod : IPaymentMethod
{
private Controller _currentController;
public MyPaymentMethod(IScopedInstance<Controller> controller)
{
_currentController = controller.Value;
}
public void ProcessPayment()
{
var model = new PaymentModel();
_currentController.TryUpdateModel(model, typeof(PaymentModel), null);
if (!_currentController.ModelState.IsValid)
{
return; // or exception
}
// Process Payment using model
}
public Task<IViewComponentResult> InvokeAsync()
{
// returns View
}
}
public interface IPaymentMethod
{
void ProcessPayment();
}
Since the model instance is required in the ProcessPayment method, why not simply pass it as a parameter?
[HttpPost]
public IActionResult Checkout(PaymentModel model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
if (!ModelState.IsValid)
{
return; // or exception
}
paymentMethod.ProcessPayment(model);
// ..
}
public void ProcessPayment(PaymentModel model)
{
// Process Payment using model
}
Your service is taking on responsibilities that belong to the controller - namely checking ModelState.IsValid.
public interface IPaymentMethod
{
void ProcessPayment(PaymentModel model);
}
You may wish to also pass just the properties that are needed from the payment model, or you may wish to make an IPaymentModel interface to decouple your model from your PaymentService. In that case, your IPaymentModel would go into a shared layer.
public interface IPaymentMethod
{
void ProcessPayment(IPaymentModel model);
}
This no longer works with beta7
At this time of writing (beta6), this probably isn't supported and there is a good reason for it: Controllers in ASP.NET 5 does not need to inherit from the Controller class. I have, however, found a way for this to work using ActionFilters.
public class ScopeControllerActionFilterAttribute : ActionFilterAttribute
{
private readonly IScopedInstance<Controller> _controller;
public ScopeControllerActionFilterAttribute(IScopedInstance<Controller> controller)
{
_controller = controller;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (_controller.Value == null)
{
_controller.Value = context.Controller as Controller;
}
}
}
Note that depending on the stage of the http request lifecycle, the Value of IScopedInstance<Controller> may still be empty.

MVC custom filter, invoke ASP.NET pipeline event manually for unit test

public abstract class MyControllerBase : Controller
{
protected override void OnActionExecuting(ActionExecutingContext context)
{
// do some magic
}
}
All of my controllers inherit from MyControllerBase. The problem is that now I can't unit test certain methods because the filter sets some authorisation/logic flags which influence code path.
Is there any way to manually trigger OnActionExecuting? How does the pipeline trigger these events?
EDIT: to show a little more the idea behind this design in response to comments. I basically have something like this:
public abstract class MyControllerBase : Controller
{
protected override void OnActionExecuting(ActionExecutingContext context)
{
UserProperties =
_userService
.GetUserProperties(filterContext.HttpContext.User.Identity.Name);
ViewBag.UserProperties = UserProperties;
}
public UserProperties { get; private set; }
public bool CheckSomethingAboutUser()
{
return UserProperties != null
&& UserProperties.IsAuthorisedToPerformThisAction;
}
// ... etc, other methods for querying UserProperties
}
So now anywhere in View or Controller I can get details of the current user, what is their email, what authorisation they have, which department they work for etc.
Example:
public class PurchasingController : MyControllerBase
{
public ActionResult RaisePurchaseOrder(Item item)
{
// can use UserProperties from base class to determine correct action...
if (UserProperties.CanRaiseOrders)
if (UserProperties.Department == item.AllocatedDepartment)
}
}
So this design works really nice, but as you can see testing the above action is difficult as I can't directly manipulate the UserProperties in the test set up.
I'm not sure you're suppose to override OnActionExecuting like that in MCV, normally I make an ActionFilterAttribute
public class SomeMagicAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
Then your class:
[SomeMagic]
public abstract class MyControllerBase : Controller
{
}
Then in your unit test you can just do
var magic = new SomeMagicAttribute();
var simulatedContext = new ActionExecutingContext();
magic.OnActionExecuting(simulatedContext);

Using castle windsor with interceptors and asp.net

I'm trying to add logging with aspect orientated programming using castle windsor in plain asp.net, i.e. not MVC
I've added a class that implements the IInterceptor interface and an attribute that inherits from Attribute.
public class LogAttribute : Attribute
{
public Level LogLevel { get; set; }
public LogAttribute(Level level)
{
LogLevel = level;
}
}
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
MethodInfo mi = invocation.Method;
LogAttribute[] atts = (LogAttribute[])mi.GetCustomAttributes(typeof(LogAttribute), true);
// if method not marked with InternalUseRestricted attribute, then pass on call
if (atts.Length == 0)
{
invocation.Proceed();
}
else
{
ISeiLogger log = LoggerFactory.GetLogger(mi.DeclaringType.ToString());
//assume only one logging attribute
//log on entry
log.LogEnter(atts[0].LogLevel);
//allow code to continue
invocation.Proceed();
//log on exit
log.LogExit(atts[0].LogLevel);
}
}
}
Now in the global.asax.cs I've added the following:
public partial class Global : System.Web.HttpApplication, IoCProvider
{
private void InitializeIoC()
{
container = new WindsorContainer();
container.Install(new Sei.Aspect.AspectInstaller());
}
public IWindsorContainer Container
{
get { return container; }
}
private static Sei.Logging.ISeiLogger log;
private IWindsorContainer container;
public override void Init()
{
base.Init();
InitializeIoC();
}
and I've created an installer class:
public class AspectInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
//container.Register(AllTypes.FromAssembly(Assembly.GetExecutingAssembly()).BasedOn<IInterceptor>().Configure(component => component.LifeStyle.PerWebRequest));
container.Register(Component.For<IInterceptor>().ImplementedBy<LoggingInterceptor>().LifeStyle.PerWebRequest);
container.Register(Component.For<IInterceptor>().ImplementedBy<InternalUseRestrictedInterceptor>().LifeStyle.PerWebRequest);
container.Register(Component.For<IInterceptor>().ImplementedBy<CachingInterceptor>().LifeStyle.PerWebRequest);
}
}
I now want to add the attribute to some arbitary page's code behind class and some arbitary virtual method, as in
[Log(Level.Info)]
protected string Login(string username, string password)
{
DoSomething();
}
This obviously doesn't work. Do I need to change the way I'm instantiating the page (its a page's code-behind class) to use a container? Or is it the way I'm registering the interceptors? I want to be able to use the interceptors on any class going forward and not have to tell the container about each and every class that I have in my application.
Short answer: it's not possible.
Long answer: due to the way ASP.NET Web Forms works, it doesn't let anyone interfere with the page instantiation. Some claim that using a custom PageHandlerFactory lets you do IoC, but this only lets you set properties after the page has been instantiated, which is simply not enough for proxying.
So runtime proxy libraries such as DynamicProxy or LinFu can't do anything about this. But you may be able to use compile-time aspect weavers, such as PostSharp.
Alternatively, make your code-behind as slim as possible, deferring actual logic to Windsor-managed components.

IoC and dataContext disposing in asp.net mvc 2 application

I have the Global.asax like the code below:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
// ....
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(IOCControllerFactory));
}
}
public class IOCControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public IOCControllerFactory()
{
kernel = new StandardKernel(new NanocrmContainer());
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
var controller = kernel.TryGet(controllerType) as IController;
if (controller == null)
return base.GetControllerInstance(requestContext, controllerType);
var standartController = controller as Controller;
if (standartController is IIoCController)
((IIoCController)standartController).SetIoc(kernel);
return standartController;
}
class NanocrmContainer : Ninject.Modules.NinjectModule
{
public override void Load()
{
// ...
Bind<DomainModel.Entities.db>().ToSelf().InRequestScope().WithConstructorArgument("connection", "Data Source=lims;Initial Catalog=nanocrm;Persist Security Info=True;User ID=***;Password=***");
}
}
}
In this case if somewhere it is the class, defined like:
public class UserRepository : IUserRepository
{
private db dataContext;
private IUserGroupRepository userGroupRepository;
public UserRepository(db dataContext, IUserGroupRepository userGroupRepository)
{
this.dataContext = dataContext;
this.userGroupRepository = userGroupRepository;
}
}
then the dataContext instance is created (if no one was created in this request scope) by Ninject.
So the trouble now is - where to invoke dataContext method .Dispose()?
UPD:
so i followed the advice from KeeperOfTheSoul and solved the issue in such way:
public override void ReleaseController(IController controller)
{
base.ReleaseController(controller);
var db = kernel.Get<DomainModel.Entities.db>();
db.Dispose();
}
A good place to handle this is in IControllerFactory.ReleaseController, eg
public override void ReleaseController() {
base.ReleaseController();
//Do whatever you need to clean up the IoC container here
}
In NInject this could be handled by scoping using an activation block, at the start of the request when creating the controller you can store the activation block in the HttpContext's current items, during ReleaseController you can retrieve the previously created activation block and dispose it.
You could also consider using InScope and having the custom scope implement INotifyWhenDisposed. After that the usage is the same as with an activation block, except now you store the scope in the HttpContext's current items.
A pattern that is sometimes used to dispose db connections is to call Dispose from the finaliser.
public class db : IDisposable {
//called by the garbage collector
~db() {
//Call dispose to make sure the resources are cleaned up
Dispose(false);
}
//IDisposable implementation
public void Dispose() {
Dispose(true);
}
//subclasses of db can override Dispose(bool) and clean up their own fields
protected virtual void Dispose (bool disposing) {
if (disposing) {
//Supress finalization as all resources are released by this method
//Calling Dispose on IDisposable members should be done here
GC.SupressFinalize();
}
//Clean up unmanaged resources
//Do not call other objects as they might be already collected if called from the finalizer
}
}
You could hook it into Application_EndRequest.

Resources