1. 程式人生 > >MVC身份驗證.MVC過濾器.MVC6關鍵字Task,Async.前端模擬表單驗證,提交.自定義匿名集合.Edge匯出到Excel.BootstrapTree樹狀選單的全選和反選.bootstrap可搜尋可多選可全選下拉框

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後跳轉前執行
        }
    }

}

           接下來介紹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的核心資訊也不一致.所以在做相容性的時候要多考慮一下

       樹狀選單的全選和反選.如果沒有這兩個功能.一個一個點也是非常反人類的

 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" 下拉框允許全選