欢迎您来到“名字什么都是浮云”的博客空间!

Asp.Net Mvc 自定义扩展

 

目录:

  1. 自定义模型IModelBinder
  2. 自定义模型验证
  3. 自定义视图引擎
  4. 自定义Html辅助方法
  5. 自定义Razor辅助方法
  6. 自定义Ajax辅助方法
  7. 自定义控制器扩展
  8. 自定义过滤器
  9. 自定义ActionResult

自定义模型IModelBinder

IModelBinder主要解决的问题是,将请求的数据转换成需要的数据这部分逻辑进行了封装。比如说 http://localhost:4742/Person/Person/2 请求的参数Id是2,通过参数2,获得2相关的所有数据。

这样做的好处是:

  1. 使代码变得更加简洁
  2. 帮助我们获取HTTP请求中的数据
  3. 帮助我们完成必要的数据类型转换

Controller部分:

当访问http://localhost:4742/Person/Person?Name=admin&Age=12时,自动转换为setting对象

[HttpPost]
    public ActionResult Person2([ModelBinder(typeof(SettingsBinder))]Settings settings)
    {       
        return View("Person");
    }

IModelBinder部分

SettingsBinder继承了IModelBinder,并实现了BindModel,通过controllerContext.HttpContext.Request获取请求,从请求中获取参数值,然后转换为对象

public class SettingsBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;
        //  从request中获取参数值
        var name = request["Name"];
        var age = request["Age"];
        //  然后将参数值转为对象
        var setting = new { Name = name, Age = age };
        return setting;
    }
}

注:当Person2([ModelBinder(typeof(SettingsBinder))]Settings settings)已存在时,不能再定义Person2方法

自定义验证

通过定义自定义验证特性类,实现View的模型验证,NotEqualTo即自定义验证

public class RegisterModel
    {
        [Required]
        [StringLength(6, MinimumLength = 2)] //
        [Display(Name = "用户名")]
        public string UserName { get; set; }


        [NotEqualTo("UserName", ErrorMessage = "不能与用户名的值相同")]
        public string OtherName { get; set; }

        //  NotEqualTo  是自定义模型验证特性
    }

NotEqualToAttribute继承了ValidationAttribute和IClientValidatable ,并实现了IsValid和GetClientValidationRules方法

通过NotEqualTo构造参数的值UserName,获得该对象对应该参数值的属性值,匹配属性的值和约束的值OtherName是否相等,然后返回结果信息

using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Web.Mvc;

namespace MvcValidation.Extension
{
    //  ValidationAttribute 验证特性
    //  IClientValidatable  客户端验证接口(View视图验证)
    public class NotEqualToAttribute : ValidationAttribute, IClientValidatable
    {
        public string OtherProperty { get; set; }

        //  构造参数
        public NotEqualToAttribute(string otherProperty)
        {
            OtherProperty = otherProperty;
        }

        //  验证方法
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            //从验证上下文中可以获取我们想要的的属性
            var property = validationContext.ObjectType.GetProperty(OtherProperty);
            if (property == null)
            {
                return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "{0} 不存在", OtherProperty));
            }

            //获取属性的值
            var otherValue = property.GetValue(validationContext.ObjectInstance, null);

            //  判断并返回验证结果
            if (object.Equals(value, otherValue))
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
            return null;
        }
        //  客户端验证
        public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            //  设置客户端验证结果信息
            var rule = new ModelClientValidationRule
            {
                ValidationType = "notequalto",
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
            };
            rule.ValidationParameters["other"] = OtherProperty;
            yield return rule;
        }
    }
}


自定义视图引擎

系统提供了视图和视图引擎,我们需要了解它之后继承并重写它的逻辑。

/*  
     *  思路
     *  1、控制器方法返回ActionResult是一个抽象类 
     *  2、ActionResult的其中一个子类ViewResult,正是她使用IView实例最终渲染出视图 
     *  3、需要自定义IView 
     *  4、IViewEngine管理着IView,同时也需要自定义IViewEngine
     *  5、自定义IViewEngine是需要全局注册的
     */
//namespace System.Web.Mvc

    //public interface IView
    //{
    //  第一个参数ViewContext包含了需要被渲染的信息,被传递到前台的强类型Model也包含在其中。 
    //  第二个参数TextWriter可以帮助我们写出想要的html格式。
    //  void Render(ViewContext viewContent, TextWriter textWriter);
    //}
//namespace System.Web.Mvc
    //public interface IViewEngine
    //{
    //    //  FindPartialView:在当前控制器上下文ControllerContext中找到部分视图。 
    //    System.Web.Mvc.ViewEngineResult FindPartialView(System.Web.Mvc.ControllerContext controllerContext, string partialViewName, bool useCache);
    //    //  FindView:在当前控制器上下文ControllerContext中找到视图。 
    //    System.Web.Mvc.ViewEngineResult FindView(System.Web.Mvc.ControllerContext controllerContext, string viewName, string masterName, bool useCache);
    //    //  ReleaseView:释放当前控制器上下文ControllerContext中的视图。
    //    void ReleaseView(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.IView view);
    //}

我们将派生出它们的类,通过自定义视图引擎类实现渲染,并显示。
准备资源:

public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Score { get; set; }
    }

    public class DataAccess
    {
        List<Student> students = new List<Student>();

        public DataAccess()
        {
            for (int i = 0; i < 10; i++)
            {
                students.Add(new Student()
                {
                    Id=i+1,
                    Name="Name"+Convert.ToString(i+1),
                    Score = i+80
                });
            }
        }

        public List<Student> GetStudents()
        {
            return students;
        }
    }

自定义扩展

public class StudentView : IView
    {
        /// <summary>
        /// 渲染
        /// 通过获得视图上下文数据,然后自定义输出格式通过TextWriter输出数据
        /// </summary>
        /// <param name="viewContent"></param>
        /// <param name="writer"></param>
        public void Render(ViewContext viewContent, TextWriter writer)
        {
            //  从视图上下文ViewContext拿到model
            var model = viewContent.ViewData.Model;
            var students=model as List<Student>;
            //  自定义输出视图的html格式
            writer.Write("<table border=1><tr><th>编号</th><th>名称</th><th>分数</th></tr>");
            foreach (Student stu in students)
            {
                writer.Write("<tr><td>" + stu.Id + "</td><td>" + stu.Name + "</td><td>" + stu.Score + "</td></tr>");
            }
            writer.Write("</table>");
        }
    }

    public class StudentViewEngine : IViewEngine
    {
        //呈现部分视图
        public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            throw new NotImplementedException();
        }
        //呈现视图
        public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            if (viewName == "StudentView")
            {
                //  呈现自定义视图
                return new ViewEngineResult(new StudentView(), this);
            }
            else
            {
                return new ViewEngineResult(new string[] { "针对Student的视图还没创建!" });
            }
        }
        //显示视图
        public void ReleaseView(ControllerContext controllerContext, System.Web.Mvc.IView view)
        {
            
        }
    }

至此自定义视图引擎完成。接下来我们进行调用:
我们只需要在View中指定自定义视图的名称即可。

public ActionResult Index()
        {
            var students = new DataAccess().GetStudents();
            ViewData.Model = students;

            return View("StudentView");
        }

设置默认的,全局的视图引擎
只需要添加ViewEngines.Engines.Add(new StudentViewEngine());即可实现。

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);


            ViewEngines.Engines.Add(new StudentViewEngine());
        }

 

自定义HtmlHelper辅助方法

定义扩展方法类和扩展方法

using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

//  设置命名空间为统一的Html命名空间
namespace System.Web.WebPages.Html
{
    //  设置静态类
    public static class HtmlExtensions
    {
        //  为HtmlHelper类提辅助方法Img,参数为src和alt
        public static MvcHtmlString Img(this HtmlHelper html,string src,string alt)
        {
            return MvcHtmlString.Create("<img src=\"" + src + "\" alt=\"" + alt + "\" />");
        }
} }

在View页面中调用该扩展方法

@* 调用Img方法,前者为src,后者为alt,生成的结果是:<img src="C:\images\btn.ico\" alt="" /> *@
@Html.Img("C:\\images\\btn.ico","")

 

自定义Razor辅助方法

在MVC项目根目录下新建一个文件夹名为:App_Code,用于存放MyHelpers.cshtml

编写MyHelpers.cshtml内容如下:

相当于定义了MyHelpers.li(List<string> arrays)方法,通过关键字helper进行声明,这样其他的cshtml页面都可以访问该方法

@helper li(List<string> arrays) { 
    <ul>
        @foreach (var item in arrays)
        {
            <li>@(item)</li>
        }
    </ul>
}

cshtml页面访问li方法

Index.cshtml页面调用:

@MyHelpers.li(new List<string>() { "甲","乙","丙","丁"})

页面输出结果:

自定义AjaxHelper辅助方法

与HtmlHelper扩展一样,为AjaxHelper扩展一个方法Textbox,并设置相应的属性

public static class HtmlExtensions
    {
        //  为HtmlHelper类提辅助方法Img,参数为src和alt
        public static MvcHtmlString Img(this HtmlHelper html, string src, string alt)
        {
            return MvcHtmlString.Create("<img src=\"" + src + "\" alt=\"" + alt + "\" />");
        }

        public static MvcHtmlString Textbox(this AjaxHelper ajaxHelper, string name,  AjaxOptions ajaxOptions, object htmlAttributes)
        {
            //  设置标签名
            var tag = new TagBuilder("input");
            //  设置属性值
            tag.MergeAttribute("name", name);
            tag.MergeAttribute("type", "text");

            tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
            tag.MergeAttributes((ajaxOptions ?? new AjaxOptions()).ToUnobtrusiveHtmlAttributes());
            tag.MergeAttribute("value", "自定义Ajax扩展");
            //  输出Html
            return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
        }
    }

View调用:

@Ajax.Textbox("search",
    new AjaxOptions
    {
        Url = @Url.Action("GetTime"),
        UpdateTargetId = "divTime",
        InsertionMode = InsertionMode.Replace
    },
new { size = 50 })

输出结果:
输出结果的源码:

<input data-ajax="true" data-ajax-mode="replace" data-ajax-update="#divTime" data-ajax-url="/AjaxHelperExt/GetTime" name="search" size="50" type="text" value="自定义Ajax扩展"></input>

自定义UrlHelper辅助方法

略.

自定义控制器扩展BaseController

//  BaseController  针对Controller进行重写
    //  并且提供了一些公用的方法如权限校验,Action跳转、日志记录等
    public class BaseController : Controller
    {

        protected override void OnException(ExceptionContext filterContext)
        {
            //  处理异常
            base.OnException(filterContext);
        }

        protected override void Initialize(RequestContext requestContext)
        {
            //  处理初始化信息,如Cookie,Session等缓存信息
            base.Initialize(requestContext);
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //  在调用操作方法前调用
            base.OnActionExecuting(filterContext);
        }

        protected override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            //  在调用操作方法后调用
            base.OnActionExecuted(filterContext);
        }

        
    }

自定义过滤器

原文

路由访问过滤器

/// <summary>
    /// 路由访问过滤器
    /// </summary>
    public class SystemIActionFilter : IActionFilter
    {
        //  
        // Summary:  
        //     Called after the action method is invoked.  
        //      在Action返回之后  
        // Parameters:  
        //   filterContext:  
        //     Information about the current request and action.  
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
        }
        //  
        // Summary:  
        //     Called before the action method is invoked.  
        //      在进入Action之前  
        //      说明:使用RedirectToRouteResult进行路由值进行重定向时  
        //      RouteName 路由名称   
        //      RouteValues 路由值  特别注意第三个值 Permanent 获取一个值  
        //      该值指示重定向是否应为永久重定向 如果为true 在本程序会出现问题  
        // Parameters:  
        //   filterContext:  
        //     Information about the current request and action.  
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //验证 控制器 视图   
            string tempAction = filterContext.RouteData.Values["action"].ToString();
            string tempController = filterContext.RouteData.Values["controller"].ToString();
            string tempLoginAction = filterContext.RouteData.Values["action"].ToString();

            if (tempAction == "HomeLogin" && tempController == "Home" || tempLoginAction == "UserLogin" ? false : true)
            {
                //请求登录时  
                if (tempAction == "UserLogin" && tempController == "Home" ? false : true)
                {
                    //Cookie  
                    HttpCookie tempToken = filterContext.HttpContext.Request.Cookies["exclusiveuser_token"];
                    if (tempToken == null)
                    {
                        filterContext.Result = new RedirectToRouteResult("HomeLogin", new RouteValueDictionary(new { controller = "Home", action = "HomeLogin" }), false);
                    }
                    //登录token不为null时  进行合法性验证token 头部,载荷,签名,cookie过期时间  
                    if (tempToken == null ? false : true)
                    {
                        //UserToken 方法 将验证 token 合法性 包括token 签名 ,token载荷,cookie 过期时间等  
                        //string SystemToken = new SecondTrackToken().UserToken();
                        //if (SystemToken == null)
                        //{
                        //    filterContext.Result = new RedirectToRouteResult("HomeLogin", new RouteValueDictionary(new { controller = "Home", action = "HomeLogin" }), false);
                        //};
                    }
                }
            }
        }
    }
路由访问过滤器

异常处理过滤器

/// <summary>
    /// 异常处理过滤器 
    /// </summary>
    public class SystemIExceptionFilter : IExceptionFilter
    {
        void IExceptionFilter.OnException(ExceptionContext filterContext)
        {
            Exception exception = filterContext.Exception;
            if (filterContext.ExceptionHandled)
            {
                return;
            }
            HttpException http = new HttpException(null, exception);
            /*  
             * filterContext.Exception.Message 错误信息 
              */
            string messager = filterContext.Exception.Message;

            /*  
             * 错误日志 
              */
            //Log4NetHelp help = new Log4NetHelp();
            //help.ErrorString(filterContext.Exception.Message);
            /*  
             * 设置自定义异常已经处理,避免其他过滤器异常覆盖 
              */
            filterContext.ExceptionHandled = true;

            /*  
             * 在派生类重写时,设置或者重写一个值该值指定是否禁用ISS7.0中自定义错误 
              */
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
    }
异常处理过滤器

授权处理(获取客户端信息)

public class SystemIAuthorizationFilter : IAuthorizationFilter
    {
        void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext)
        {
            //当前操作计算机用户   
            string pcName = ((System.Web.HttpServerUtilityWrapper)((System.Web.HttpContextWrapper)filterContext.RequestContext.HttpContext).Server).MachineName;
            //视图  
            string action = ((System.Web.Mvc.ReflectedActionDescriptor)filterContext.ActionDescriptor).ActionName;
            //控制器  
            string controller = ((System.Web.Mvc.ReflectedActionDescriptor)filterContext.ActionDescriptor).ControllerDescriptor.ControllerName;
            //请求时间  
            string time = filterContext.RequestContext.HttpContext.Timestamp.ToString();
            //请求相对路径  
            string absturl = ((System.Web.UnvalidatedRequestValuesWrapper)((System.Web.HttpRequestWrapper)((System.Web.HttpContextWrapper)filterContext.RequestContext.HttpContext).Request).Unvalidated).Url.AbsoluteUri;
            //状态  
            string code = ((System.Web.HttpResponseWrapper)((System.Web.HttpContextWrapper)filterContext.RequestContext.HttpContext).Response).Status;
            // 浏览器版本  
            string browser = ((System.Web.HttpBrowserCapabilitiesWrapper)((System.Web.HttpRequestWrapper)((System.Web.HttpContextWrapper)filterContext.RequestContext.HttpContext).Request).Browser).Type;
            //请求方式  
            string gepPost = ((System.Web.HttpRequestWrapper)((System.Web.Mvc.Controller)filterContext.Controller).Request).RequestType;
            //本地主机名称解析DNS本身处理。  
            string server = ((System.Web.HttpRequestWrapper)((System.Web.HttpContextWrapper)filterContext.HttpContext).Request).UserHostAddress;
            #region  server 说明  
            /* 
              * 版权(c)1993 - 2009微软(msft . o:行情)。 
              * 
              * 这是一个示例所使用的主机文件微软为Windows TCP / IP。 
              * 
              * 这个文件包含IP地址到主机名的映射。 
                          每一个 
              * 条目应该保存在单个行。 
              IP地址应 
              *被放置在第一列对应的主机名。 
              *的IP地址和主机名应该由至少一个 
              *空间。 
              * 
              *此外,评论(这样的)可能是插入的个人 
              *线或后机器名称用“*”符号。 
              * 
              例如: 
              * 
              * 102.54.94.97 rhino.acme.com源服务器 
              * 38.25.63.10 x.acme.com x客户机主机 

              *本地主机名称解析DNS本身处理。 
              * 127.0.0.1 localhost 
              *::1 localhost 
              */
            #endregion
            //用户  
            //部门  
            //职位  

        }
    }
授权处理过滤器

自定义属性过滤器

public class CheckLogin: ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            HttpCookieCollection CookieCollect = System.Web.HttpContext.Current.Request.Cookies;
            if (CookieCollect["username"] == null || CookieCollect["password"] == null)
            {
                filterContext.Result = new RedirectResult("/Home/Login");
            }
            else
            {
                if (CookieCollect["username"].Value != "admin" && CookieCollect["password"].Value != "123456")
                {
                    filterContext.Result = new RedirectResult("/Home/Login");
                }
            }
        }
    }
检查登录的过滤器

 

过滤器定义好后,需要在过滤器配置类FilterConfig中添加

public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());

            //将自定义异常过滤器的优先级提高,防止异常被默认的HandleError处理(也可以自定义类重写HandleErrorAttribute 实现错误处理)  
            filters.Add(new SystemIExceptionFilter(), 1);
            //控制器过滤器  
            filters.Add(new SystemIActionFilter(), 2);
            //授权过滤器  
            filters.Add(new SystemIAuthorizationFilter(), 3);
        //  自定义属性过滤器
            filters.Add(new CheckLogin());
} }

自定义属性过滤器在控制器中调用:
在方法的上面加上特性:CheckLogin,当调用该方法时会先进行过滤再执行下面的逻辑。

 [CheckLogin]
        public ActionResult About()
        {
            
            ViewBag.Message = "Your application description page.";

            return View();
        }

 

自定义ActionResult

扩展的ActionResult,继承自ActionResult,需要重写ExecuteResult方法,通过构造函数传入参数值,

使用ExecuteResult方法的ControllerContext上下文获得HttpResponse,HttpResponse使用输出参数值结果。

 /// <summary>
    /// 自定义JObject返回结果
    /// </summary>
    public class JObjectActionResult : ActionResult
    {
        /// <summary>
        /// 结果集
        /// </summary>
        public JObject JObject
        {
            get;
            set;
        }

        public Encoding ContentEncoding
        {
            get;
            set;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "application/json";
            if (ContentEncoding != null)
            {
                response.ContentEncoding = ContentEncoding;
            }
            if (JObject != null)
            {
                response.Write(JObject.ToString());
            }
        }
    }
JObjectActionResult

通过response.Write(JObject.ToString());最后输出结果。
接下来我们看看调用:

public ActionResult Index()
        {       
            return View();
        }

默认的View()是Controller.View()的方法,因此我们为Controller类扩展一个方法。定义扩展内容如下:

public static class JObjectActionResultExtensions
    {
        public static JObjectActionResult JObjectResult(this Controller controller, JObject obj)
        {
            return new JObjectActionResult { JObject = obj };
        }

        public static JObjectActionResult JObjectResult(this Controller controller, bool success)
        {
            JObject obj = new JObject();
            obj.Add("success", success);
            if (success)
            {
                obj.Add("code", 200);
                obj.Add("msg", "Success!");
            }
            else
            {
                obj.Add("code", 500);
                obj.Add("msg", "Error!");
            }
            return JObjectResult(controller, obj);
        }

        public static JObjectActionResult JObjectResult(this Controller controller, bool success, int code, string msg)
        {
            JObject obj = new JObject();
            obj.Add("success", success);
            obj.Add("code", code);
            obj.Add("msg", msg);
            return JObjectResult(controller, obj);
        }
    }
JObjectActionResult的扩展

控制器中调用输出:
使用this关键字调用JObjectResult即可输出结果。

public ActionResult About()
        {            
            ViewBag.Message = "Your application description page.";
            return this.JObjectResult(true);
        }

接下来我们再扩展一个序列化的Result.

/// <summary>
    /// 泛型的序列化结果
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    public class CustomView<TData>  : ActionResult where TData:class,new()
    {
        /// <summary>
        /// 构造函数传入参数
        /// </summary>
        /// <param name="t"></param>
        public CustomView(TData t) { data = t; }
        public TData data;

        protected JsonSerializerSettings SerializerSettings;

        protected void InitSerialization(ControllerContext context)
        {
            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "text/html";
            if (SerializerSettings == null)
            {
                SetSerializerSettings();
            }
            response.Write(JsonConvert.SerializeObject(data, Formatting.None, SerializerSettings));
        }

        protected virtual void SetSerializerSettings()
        {
            SerializerSettings = new JsonSerializerSettings
            {
                Converters = new List<JsonConverter>
                {
                    new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd hh:mm" }
                }
            };
        }

        public override void ExecuteResult(ControllerContext context)
        {
            InitSerialization(context);
        }
    }
CustomView

调用它,只需要实例化一下就行了。

public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";
            User u = new Models.User();
            return  new CustomView<User>(u);
        }

 

 

 下载地址

大家还有什么好的扩展方法,可以回复该帖哦。

 

posted @ 2018-02-22 18:20  名字什么都是浮云  阅读(1211)  评论(0编辑  收藏  举报