Skip to content

Commit

Permalink
#282 Add filter attribute for publishing ModelReceived and ModelPrep…
Browse files Browse the repository at this point in the history
…ared events
  • Loading branch information
RomanovM committed Sep 27, 2017
1 parent f010741 commit 54ac9b3
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 14 deletions.
Expand Up @@ -28,6 +28,7 @@ namespace Nop.Web.Framework.Controllers
/// <summary>
/// Base controller
/// </summary>
[PublishModelEvents]
[SignOutFromExternalAuthentication]
[ValidatePassword]
[SaveIpAddress]
Expand Down
Expand Up @@ -4,15 +4,30 @@

namespace Nop.Web.Framework.Events
{
/// <summary>
/// Represents event publisher extensions
/// </summary>
public static class EventPublisherExtensions
{
/// <summary>
/// Publish ModelPrepared event
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
/// <param name="eventPublisher">Event publisher</param>
/// <param name="model">Model</param>
public static void ModelPrepared<T>(this IEventPublisher eventPublisher, T model) where T : BaseNopModel
{
eventPublisher.Publish(new ModelPrepared<T>(model));
}

public static void ModelReceived<T>(this IEventPublisher eventPublisher, T model,
ModelStateDictionary modelState) where T : BaseNopModel
/// <summary>
/// Publish ModelReceived event
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
/// <param name="eventPublisher">Event publisher</param>
/// <param name="model">Model</param>
/// <param name="modelState">Model state</param>
public static void ModelReceived<T>(this IEventPublisher eventPublisher, T model, ModelStateDictionary modelState) where T : BaseNopModel
{
eventPublisher.Publish(new ModelReceived<T>(model, modelState));
}
Expand Down
23 changes: 17 additions & 6 deletions src/Presentation/Nop.Web.Framework/Events/ModelPrepared.cs
@@ -1,18 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nop.Web.Framework.Mvc.Models;
using Nop.Web.Framework.Mvc.Models;

namespace Nop.Web.Framework.Events
{
/// <summary>
/// Represents an event that occurs after the model is prepared for view
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
public class ModelPrepared<T> where T : BaseNopModel
{
#region Ctor

public ModelPrepared(T model)
{
this.Model = model;
}

#endregion

#region Properties

/// <summary>
/// Gets a model
/// </summary>
public T Model { get; private set; }

#endregion
}
}
26 changes: 20 additions & 6 deletions src/Presentation/Nop.Web.Framework/Events/ModelReceived.cs
@@ -1,22 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Nop.Web.Framework.Mvc.Models;

namespace Nop.Web.Framework.Events
{
/// <summary>
/// Represents an event that occurs after the model is receved from the view
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
public class ModelReceived<T> where T : BaseNopModel
{
#region Ctor

public ModelReceived(T model, ModelStateDictionary modelState)
{
this.Model = model;
this.ModelState = modelState;
}

#endregion

#region Properties

/// <summary>
/// Gets a model
/// </summary>
public T Model { get; private set; }

/// <summary>
/// Gets a model state
/// </summary>
public ModelStateDictionary ModelState { get; private set; }

#endregion
}
}
@@ -0,0 +1,148 @@
using System;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Nop.Services.Events;
using Nop.Web.Framework.Events;
using Nop.Web.Framework.Mvc.Models;

namespace Nop.Web.Framework.Mvc.Filters
{
/// <summary>
/// Represents filter attribute that publish ModelReceived event before the action executes, after model binding is complete
/// and publish ModelPrepared event after the action executes, before the action result
/// </summary>
public class PublishModelEventsAttribute : TypeFilterAttribute
{
#region Fields

private readonly bool _ignoreFilter;

#endregion

#region Ctor

/// <summary>
/// Create instance of the filter attribute
/// </summary>
/// <param name="ignore">Whether to ignore the execution of filter actions</param>
public PublishModelEventsAttribute(bool ignore = false) : base(typeof(PublishModelEventsFilter))
{
this._ignoreFilter = ignore;
this.Arguments = new object[] { ignore };
}

#endregion

#region Properties

/// <summary>
/// Gets a value indicating whether to ignore the execution of filter actions
/// </summary>
public bool IgnoreFilter => _ignoreFilter;

#endregion

#region Nested filter

/// <summary>
/// Represents filter that publish ModelReceived event before the action executes, after model binding is complete
/// and publish ModelPrepared event after the action executes, before the action result
/// </summary>
private class PublishModelEventsFilter : IActionFilter
{
#region Fields

private readonly bool _ignoreFilter;
private readonly IEventPublisher _eventPublisher;

#endregion

#region Ctor

public PublishModelEventsFilter(bool ignoreFilter,
IEventPublisher eventPublisher)
{
this._ignoreFilter = ignoreFilter;
this._eventPublisher = eventPublisher;
}

#endregion

#region Methods

/// <summary>
/// Called before the action executes, after model binding is complete
/// </summary>
/// <param name="context">A context for action filters</param>
public void OnActionExecuting(ActionExecutingContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));

//check whether this filter has been overridden for the Action
var actionFilter = context.ActionDescriptor.FilterDescriptors
.Where(filterDescriptor => filterDescriptor.Scope == FilterScope.Action)
.Select(filterDescriptor => filterDescriptor.Filter).OfType<PublishModelEventsAttribute>().FirstOrDefault();

//whether to ignore this filter
if (actionFilter?.IgnoreFilter ?? _ignoreFilter)
return;

if (context.HttpContext.Request == null)
return;

//only in POST requests
if (!context.HttpContext.Request.Method.Equals(WebRequestMethods.Http.Post, StringComparison.InvariantCultureIgnoreCase))
return;

//model received event
foreach (var model in context.ActionArguments.Values.OfType<BaseNopModel>())
{
//we publish the ModelReceived event for all models as the BaseNopModel,
//so you need to implement IConsumer<ModelReceived<BaseNopModel>> interface to handle this event
_eventPublisher.ModelReceived(model, context.ModelState);
}
}

/// <summary>
/// Called after the action executes, before the action result
/// </summary>
/// <param name="context">A context for action filters</param>
public void OnActionExecuted(ActionExecutedContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));

//check whether this filter has been overridden for the Action
var actionFilter = context.ActionDescriptor.FilterDescriptors
.Where(filterDescriptor => filterDescriptor.Scope == FilterScope.Action)
.Select(filterDescriptor => filterDescriptor.Filter).OfType<PublishModelEventsAttribute>().FirstOrDefault();

//whether to ignore this filter
if (actionFilter?.IgnoreFilter ?? _ignoreFilter)
return;

if (context.HttpContext.Request == null)
return;

//only in GET requests
if (!context.HttpContext.Request.Method.Equals(WebRequestMethods.Http.Get, StringComparison.InvariantCultureIgnoreCase))
return;

//model prepared event
if (context.Controller is Controller controller && controller.ViewData.Model is BaseNopModel model)
{
//we publish the ModelPrepared event for all models as the BaseNopModel,
//so you need to implement IConsumer<ModelPrepared<BaseNopModel>> interface to handle this event
_eventPublisher.ModelPrepared(model);
}
}

#endregion
}

#endregion
}
}

0 comments on commit 54ac9b3

Please sign in to comment.