MVC身份验证.MVC过滤器.MVC6关键字Task,Async.前端模拟表单验证,提交.自定义匿名集合.Edge导出到Excel.BootstrapTree树状菜单的全选和反选.bootstrap可搜索可多选可全选下拉框
1.MVC身份验证.
有两种方式.一个是传统的所有控制器继承自定义Control,然后再里面用MVC的过滤器拦截.所以每次网站的后台被访问时.就会先走入拦截器.进行前端和后端的验证
一个是利用(MVC4及以上版本)自动生成的Global.asax.cs中的 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters),这个file会加载所有的过滤器.第一种方式适用目前任何版本,第二种支持MVC4以及以上,下面对两种方式一一细讲
1.
public class CountController : BaseController
CountController是自定义Controller,BaseController是需要继承的Control
Base里两行是精华.其他自定义
public class BaseController : Controller protected override void OnActionExecuting(ActionExecutingContext filterContext)
第一行是Control原本继承的父类.所以目前的控制器多继承了一个父类.第二行代码就是要多继承一次的原因.OnActionExecuting是每个Action被调用前.不论是Actionresult还是Jsonresult.都会被拦截.然后我们可以拿到Request.Cookie和相关的请求地址.
就像这杨 url = $"/{filterContext.ActionDescriptor.ControllerDescriptor.ControllerName}/{filterContext.ActionDescriptor.ActionName}"; 还有这杨cookieName = Request.Cookies["userName"].Value.ToString();
一般cookie中会包含请求人信息.然后我们根据数据库就能检测出此人是否能访问这个前端页面或者后端接口.如果通过Return true ,反之
Response.RedirectToRoute(new { controller = "Error", action = "NotFound", Content = "权限不足,请联系管理员" }); return;
这杨就会跳到异常页面了.
如果通过了身份验证.就可以设定一个全局通用身份.HttpContext.Current.User.Identity.Name.方便后面接口使用.但这个属性是只读的.所以要往上重写IPrincipal
public class MyPrincipal : System.Security.Principal.IPrincipal { public MyPrincipal(string userID) { Identity = new MyIdentity(userID); } public System.Security.Principal.IIdentity Identity { get; set; } public bool IsInRole(string role) { return true; } } public class MyIdentity : System.Security.Principal.IIdentity { public MyIdentity(string currentUserID) { Name = currentUserID; } public bool IsAuthenticated { get { return true; } } public string Name { get; } public string AuthenticationType { get { return null; } } }
最后赋值的方式: MyPrincipal principal = new MyPrincipal(userName); HttpContext.User = principal;
2.(MVC4及以上版本)自动生成的Global.asax.cs中的 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters),提供三种拦截器.Action,Result,Exption,分别是方法拦截.结果拦截.异常拦截,
在fileconfig中添加三个过滤器
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //filters.Add(new HandleErrorAttribute()); filters.Add(new ActionFillters()); filters.Add(new ExceptionFillters()); filters.Add(new ResultFillters()); } }
为了方便我将三个过滤器写在一个类中
namespace MvcApplication2.App_Start { public class ActionFillters : FilterAttribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) { //执行action后执行这个方法 比如做操作日志 } public void OnActionExecuting(ActionExecutingContext filterContext) { //执行action前执行这个方法,比如做身份验证 } } public class ExceptionFillters : FilterAttribute, IExceptionFilter { //发生异常时会执行这段代码 public void OnException(ExceptionContext filterContext) { //在这里你可以记录发生异常时你要干什么,比例写日志 //这一行告诉系统,这个异常已经处理了,不用再处理 filterContext.ExceptionHandled = true; } } public class ResultFillters : FilterAttribute, IResultFilter { public void OnResultExecuted(ResultExecutedContext filterContext) { //执行完action后跳转后执行 } public void OnResultExecuting(ResultExecutingContext filterContext) { //执行完action后跳转前执行 } } }
3.接下来介绍MVC6新关键字.Task,Async(异步)
对线程进行了封装.变成了属性.简单举个例子
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { new Task(StartCode, 2).Start(); Console.WriteLine("主线程运行到此"); Thread.Sleep(1000); Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 100); t.Start(); t.Wait(); Console.WriteLine("The Sum is:" + t.Result); } private static void StartCode(object i) { Console.WriteLine("开始执行子线程...{0}", i); Thread.Sleep(1000);//模拟代码操作 } private static Int32 Sum(Int32 i) { Int32 sum = i; return sum-1; } } }
在这段代码中.Task是单独启动了一个子线程运行方法.和主线程互不干涉.但是有一点Task接受到的返回值要加上Result.不然返回的是整个线程属性
接下来讲下Task和MVC6的结合
public async Task<JsonResult> Test() { var jsonresult = await DATA(); return Json(jsonresult); } private Task<string> DATA() { return Task.FromResult("HELLO"); }
根据约定Async 和await 永远是成双成对
Async代表异步 Task代表线程
接受时 用await
返回值用 Task.FromResult封装
好吧。真的很简单的新属性
前端模拟表单验证.
大家都用过MVC的表单吧.大致是view模型绑定.from 中submit 提交. controll同名同参方法接受
但是在实际运用中略限僵硬.如果一个实体类要多次提交呢?MVC默认以实体类的名字作为控件ID,一个页面出现两个同名控件只会拿到第一个的值.所以需要自定义部分表单提交.那么从头讲起吧
引入以下文件
<link rel="stylesheet" href="~/Scripts/bootstrapValidator.css" /> <script src="~/Scripts/bootstrapValidator.js"></script>
HTML表单 <form id="formEditAPP" method="post" action=""> HTML控件在表单中实际取的NAME <input class="form-control" id="EditAPPSysId" type="text" name="EditAPPSysId" readonly="readonly">
在JQUERY中
$("#formEditAPP").bootstrapValidator({ live: 'enabled',//验证时机,enabled是内容有变化就验证(默认),disabled和submitted是提交再验证 message: '通用的验证失败消息', feedbackIcons: { valid: 'glyphicon glyphicon-ok', invalid: 'glyphicon glyphicon-remove', validating: 'glyphicon glyphicon-refresh' }, fields: { EditappAccount: { validators: { notEmpty: { message: '通道账号必填' } } }, EditappPassword: { validators: { notEmpty: { message: '通道密码必填' } } }, } });
当没有通过验证的时候
模拟表单的提交
$("#EditAPPSubmit").click(function () { $("#formEditAPP").bootstrapValidator('validate');//提交验证 if ($("#formEditAPP").data('bootstrapValidator').isValid()) {//获取验证结果,如果成功,执行下面代码 $("#formEditAPP").data('bootstrapValidator').resetForm(); var array = new Object(); array.sysId = $("#EditAPPSysId").val(); array.account = $("#EditappAccount").val(); array.password = $("#EditappPassword").val(); array.appEnable = $("#EditappEnable option:selected").val(); array.appChannel = $("#EditAppChannel option:selected").val();
数组的值和服务端实体类对应,ajax 设置参数如下
$.ajax({ url: '/Business/UpdateSystemSms', type: "post", data: JSON.stringify(array), contentType: "application/json; charset=utf-8",
服务端设置参数如下
UpdateSystemSms([System.Web.Http.FromBody]MsgSysAppConfigEntity sysConfig)
自定义匿名集合
很多时候我们不想写实体类.如何偷懒呢
var trackList = new[]{ new { ID = string.Empty, ChannelKey=string.Empty, ToMobilStatus=0, CreateTime = string.Empty, Type=string.Empty }}.ToList(); trackList.Clear();
这杨数组中NEW匿名集合.类型是List<a> {string xxx,,string xx1.....}
如果是非对应数据库实体.用这个方式.快感略强烈
Edge导出到Excel
在WIN10中推出了Edge替换IE,但是Edge的内核信息和IE完全不一致.所以需要更新Edge导出EXCEL的功能.至于IE,Google等其他浏览器利用TABLE导出excel可以参考之前的博客
function EdgeToExcel(obj) { var oHtml = document.getElementsByClassName(obj)[0].outerHTML; var excelHtml = ` <html> <head> <meta charset='utf-8' /> </head> <body> ${oHtml} </body> </html> `; var excelBlob = new Blob([excelHtml], { type: 'application/vnd.ms-excel' }) // 创建一个a标签 var oA = document.createElement('a'); // 利用URL.createObjectURL()方法为a元素生成blob URL oA.href = URL.createObjectURL(excelBlob); // 给文件命名 oA.download = '下载.xls'; // 模拟点击 oA.click(); // 移除 oA.remove(); }
如何区分Edge呢
function getExplorer() { var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串 var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器 var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器 if (isEdge) { return 'Edge'; } else if (userAgent.indexOf("Chrome") >= 0) { return 'Chrome'; } }
大家要注意WIN7 的IE 9,10,11
和WIN10 IE 9,10,11内核信息完全不一致
edge和IE的内核信息也不一致.所以在做兼容性的时候要多考虑一下
BootstrapTree树状菜单的全选和反选.
如果没有这两个功能.一个一个点也是非常反人类的
var nodeCheckedSilent = false; function nodeChecked(event, node) { if (nodeCheckedSilent) { return; } nodeCheckedSilent = true; checkAllParent(node); checkAllSon(node); nodeCheckedSilent = false; } var nodeUncheckedSilent = false; function nodeUnchecked(event, node) { if (nodeUncheckedSilent) return; nodeUncheckedSilent = true; uncheckAllParent(node); uncheckAllSon(node); nodeUncheckedSilent = false; } //选中全部父节点 function checkAllParent(node) { $('#tree').treeview('checkNode', node.nodeId, { silent: true }); var parentNode = $('#tree').treeview('getParent', node.nodeId); if (!("nodeId" in parentNode)) { return; } else { checkAllParent(parentNode); } } //取消全部父节点 function uncheckAllParent(node) { $('#tree').treeview('uncheckNode', node.nodeId, { silent: true }); var siblings = $('#tree').treeview('getSiblings', node.nodeId); var parentNode = $('#tree').treeview('getParent', node.nodeId); if (!("nodeId" in parentNode)) { return; } var isAllUnchecked = true; //是否全部没选中 for (var i in siblings) { if (siblings[i].state.checked) { isAllUnchecked = false; break; } } if (isAllUnchecked) { uncheckAllParent(parentNode); } } //级联选中所有子节点 function checkAllSon(node) { $('#tree').treeview('checkNode', node.nodeId, { silent: true }); if (node.nodes != null && node.nodes.length > 0) { for (var i in node.nodes) { checkAllSon(node.nodes[i]); } } } //级联取消所有子节点 function uncheckAllSon(node) { $('#tree').treeview('uncheckNode', node.nodeId, { silent: true }); if (node.nodes != null && node.nodes.length > 0) { for (var i in node.nodes) { uncheckAllSon(node.nodes[i]); } } }
如何使用呢
$('#tree').treeview({ data: data, showCheckbox: true, onNodeChecked: nodeChecked, onNodeUnchecked: nodeUnchecked });
已经集成的相当好啦.用的时候注意一下控件ID
今天刚折腾了一个BOOTSTRAP可搜索下拉框.就顺带讲一下吧
<script src="~/Scripts/Bootstrap/bootstrap-select.js" defer></script> <link rel="stylesheet" href="~/Content/Bootstrap/bootstrap-select.css">
<select class="form-control selectpicker show-tick" data-live-search="true" id="FromSys" name="FromSys" data-live-search="true" multiple data-actions-box="true"> <option value="@item.Value">@item.Text</option> </select>
selectpicker show-tick 样式是可搜索下拉框必备的样式
data-live-search="true" 允许搜索
multiple 下拉框允许多选
data-actions-box="true" 下拉框允许全选