65.9K
CodeProject 正在变化。 阅读更多。
Home

MVC 的可扩展点:MVC 和 WEB-API 中的自定义操作选择

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (1投票)

2016年11月10日

CPOL

3分钟阅读

viewsIcon

10011

如何在 MVC 和 WebAPI 中创建自定义操作调用者

MVC 中的自定义操作选择

在本节中,我们将讨论 MVC 中的自定义操作选择。我们知道,在选择控制器后,框架通过调用 IHttpActionSelector.SelectAction 方法来选择操作。此方法接收一个 HttpControllerContext 并返回一个 HttpActionDescriptor。默认实现由 ApiControllerActionSelector 类提供。为了选择一个操作,它会查看以下内容:

  • 请求的 HTTP 方法。
  • 如果存在,则在路由模板中的“{action}”占位符。
  • 控制器上操作的参数。

现在,在需要时,我们可以覆盖默认行为并在两者之间插入我们自定义的代码。让我们尝试实现它。首先,我们必须从“ActionMethodSelectorAttribute”类继承我们的自定义类。该类只有一个名为“IsValidForRequest”的方法,该方法本质上是 abstract

 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public abstract class ActionMethodSelectorAttribute : Attribute
    {
        protected ActionMethodSelectorAttribute();
        public abstract bool IsValidForRequest
                  (ControllerContext controllerContext, MethodInfo methodInfo);
    }

由于此方法是 abstract,我们可以在自定义类中定义它。以下是我们在执行此操作的示例代码:

public class ActionDecorationAttribute : ActionMethodSelectorAttribute
    { 
        public override bool IsValidForRequest
           (ControllerContext controllerContext, MethodInfo methodInfo)
        {
            //Check for AJAX request
            if (!controllerContext.HttpContext.Request.IsAjaxRequest())
                throw new Exception("Accept AJAX call only");

            //Check for HTTPS request only
            if (!controllerContext.HttpContext.Request.IsSecureConnection)
                throw new Exception("Support only HTTPS");

            //Check for language
            if (controllerContext.HttpContext.Request.UserLanguages[0] != "en-US")
                throw new Exception("For English user only");

            return true;
        }
    }

从请求上下文中验证一些内容。示例代码非常简单,只是为了帮助演示这一点。您可以在实际问题中实现您的实际逻辑。现在,我们必须将自定义类附加到操作,以便在控制器执行之前注入代码片段。

public class HomeController : Controller
    {
        [ActionDecoration]
        public ActionResult Index()
        {
            return new EmptyResult();
        }
    }

现在,当 Index 操作的请求到达时,在执行 Index 之前,它将触发 ActionDecorationAttribute 类中的 Validation 方法。

您可能有一个问题是,“我们不能在操作筛选器中做同样的事情吗?” 对吧? 好的,但是操作选择器和操作筛选器的目的完全不同。操作选择器的任务是选择操作,而操作筛选器用于在操作执行之前和之后执行代码(在大多数情况下)。

Web API 中的自定义操作调用者

我们知道 Web API 默认支持基于 HTTP 动词的路由。这意味着框架将根据请求类型搜索匹配的操作,然后与参数匹配。要在 Web API 中实现自定义操作选择器,我们必须从以下类继承:

"ApiControllerActionSelector" class. Here is the definition of "ApiControllerActionSelector" class. 
//
    // Summary:
    //     Represents a reflection based action selector.
    public class ApiControllerActionSelector : IHttpActionSelector
    {
        //
        // Summary:
        //     Initializes a new instance of the 
        //     System.Web.Http.Controllers.ApiControllerActionSelector class.
        public ApiControllerActionSelector();

        //
        // Summary:
        //     Gets the action mappings for the 
        //     System.Web.Http.Controllers.ApiControllerActionSelector.        
        // Parameters:
        //   controllerDescriptor:
        //     The information that describes a controller.
        //
        // Returns:
        //     The action mappings.
        public virtual ILookup<string, HttpActionDescriptor> 
        GetActionMapping(HttpControllerDescriptor controllerDescriptor);
        //
        // Summary:
        //     Selects an action for the System.Web.Http.Controllers.ApiControllerActionSelector.
        //
        // Parameters:
        //   controllerContext:
        //     The controller context.
        //
        // Returns:
        //     The selected action.
        public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
    }

在此示例中,我们创建了一个非常简单的类,该类继承自“ApiControllerActionSelector”类。

public class ApiActionSelector : ApiControllerActionSelector
    {
        public  override HttpActionDescriptor SelectAction( HttpControllerContext context)
        {
            HttpMessageContent requestContent = new HttpMessageContent( context.Request);
           
            var actionMethod = context.ControllerDescriptor.ControllerType
                .GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .FirstOrDefault();

            if (actionMethod != null)
            {
                return new ReflectedHttpActionDescriptor(
                                   context.ControllerDescriptor, actionMethod);
            }

            return base.SelectAction(context);
        }
    }

所以我们在这里没有做太多事情。只想检查操作名称。由于 SelectAction 方法将 HttpControllerContext 对象作为参数,因此我们将从那里获得对每个请求对象的可见性。在实际场景中,您可以实现一些有用的逻辑。现在我们必须将代码挂接到操作中。挂接方式与我们在顶部部分看到的 MVC 方式略有不同。我们必须替换默认的操作选择机制。设置代码的好地方是 WebApiConfig 类。以下是显示如何操作的示例代码:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            //Hook up custom action selector
            config.Services.Replace(typeof(IHttpActionSelector), new ApiActionSelector());

        }
    }

现在当我们调用下面的操作时:

public class testController : ApiController
    {
        [System.Web.Http.ActionName("action1")]
        public void Get()
        {
            
        }
    }

我们看到执行序列击中了 ApiActionSelector 类中的 SelectAction 函数。

结论

在本例中,我们学习了如何在 MVC 和 Web API 中实现自定义操作调用者。请不要将操作调用者与操作筛选器混淆。两者的目的完全不同。

© . All rights reserved.