Asp.Net Mvc 自定义扩展

时间:2023-12-14 13:15:20

目录:

  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(, MinimumLength = )] //加
[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 = ; i < ; i++)
{
students.Add(new Student()
{
Id=i+,
Name="Name"+Convert.ToString(i+),
Score = i+
});
}
} 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

Asp.Net Mvc 自定义扩展

编写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>() { "甲","乙","丙","丁"})

页面输出结果:
Asp.Net Mvc 自定义扩展

自定义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 = })

输出结果:
输出结果的源码:Asp.Net Mvc 自定义扩展

<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 != "")
{
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(), );
        //  自定义属性过滤器
            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", );
obj.Add("msg", "Success!");
}
else
{
obj.Add("code", );
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);
}

下载地址

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