(1-1)文件結構的升級(Area和Filter知識總結) - ASP.NET從MVC5升級到MVC6

ASP.NET從MVC5升級到MVC6 總目錄git

MVC5項目結構

帶有Areas和Filter的項目結構

通常來講,小的MVC項目是不考慮領域的,可是,若是是稍微複雜一點的項目,每每是須要領域這個概念的。
一個領域就是一個小型的MVC項目,因此領域Area的目錄結構和普通的目錄結構是同樣的。(具備Controllers和Views目錄,以及一個AreaRegistration文件)
一個MVC項目,Controllers和Views這兩個目錄因爲約定的關係,文件夾的名稱和相對位置是不能變化的。
在默認的配置下,Controllers下面的Controller和Views下面的子文件夾是一一對應的。Controller裏面的方法和View下面的CSHTML視圖也是一一對應的。
Model這個文件夾不是必須的,並且按照趨勢,Model通常被歸爲BussinessLogic,每每存在於業務的工程中。數據模型的問題,這裏不進行討論。github

AreaRegistration文件

一個AreaRegistration文件是這樣的: AdminAreaRegistration.cs 請注意命名規範,MVC這樣的約定氛圍很濃重的框架,最好按照規定命名。安全

using System.Web.Mvc;

namespace WebSite.Areas.Admin
{
    public class AdminAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get { return "Admin"; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Admin_default",
                "Admin/{controller}/{action}/{id}",
                new {controller = "Home", action = "Index", id = UrlParameter.Optional}
                );
        }
    }
}

固然使得這個Area註冊生效的源頭是Global.asax 裏面的 RegisterAllAreas 方法mvc

public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            //MVC
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

RouteConfig.cs(位於App_Start文件夾下面)能夠設定默認的領域。框架

using System.Web.Mvc;
using System.Web.Routing;

namespace WebSite
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute("Default", "{area}/{controller}/{action}/{id}",
                new {area = "Admin", controller = "Home", action = "Index", id = UrlParameter.Optional},
                new[] {"WebSite.Areas.Admin.*"}
                ).DataTokens.Add("area", "Admin");
        }
    }
}

Filter

Filter也不是MVC的標配,可是每每一個複雜的項目會有一些Filter。Filter能夠完成不少不一樣的工做,對於某個環節的輸入和輸出進行一些干預。固然Filter也必須註冊才能使用。FilterConfig.cside

using System.Web.Mvc;

namespace WebSite
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            //默認錯誤處理
            filters.Add(new HandleErrorAttribute());
            //日誌
            filters.Add(new LoggerAttribute());
            //異常記錄
            filters.Add(new ExceptionHandlerAttribute());
            //壓縮
            filters.Add(new CompressAttribute());
        }
    }
}

壓縮

using System.IO.Compression;
using System.Web.Mvc;

namespace WebSite
{
    OnActionExecuting的時候,能夠設定輸出的壓縮
    public class CompressAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var acceptEncoding = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
            if (!string.IsNullOrEmpty(acceptEncoding))
            {
                acceptEncoding = acceptEncoding.ToLower();
                var response = filterContext.HttpContext.Response;
                if (acceptEncoding.Contains("gzip"))
                {
                    response.AppendHeader("Content-encoding", "gzip");
                    response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
                }
                else if (acceptEncoding.Contains("deflate"))
                {
                    response.AppendHeader("Content-encoding", "deflate");
                    response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
                }
            }
        }
    }
}

錯誤處理

OnException 出現錯誤的時候能夠進行一些處理工具

using System.Web.Mvc;
using InfraStructure.Log;
using InfraStructure.Utility;

namespace WebSite
{
    public class ExceptionHandlerAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext actionExecutedContext)
        {
            var actionName = actionExecutedContext.RouteData.Values["action"].ToString();
            var controllerName = actionExecutedContext.RouteData.Values["controller"].ToString();
            var username = string.Empty;
            if (actionExecutedContext.HttpContext.Session[ConstHelper.Username] != null)
            {
                username = actionExecutedContext.HttpContext.Session[ConstHelper.Username].ToString();
            }
            ExceptionLog.Log(username, actionName, controllerName, actionExecutedContext.Exception.StackTrace);
        }
    }
}

日誌

若是但願每一個Action都有執行日誌能夠這樣,OnActionExecuted以後,能夠添加一些動做測試

using System.Web.Mvc;
using InfraStructure.Log;
using InfraStructure.Utility;

namespace WebSite
{
    public class LoggerAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            var actionName = filterContext.ActionDescriptor.ActionName;
            var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            var username = string.Empty;
            if (filterContext.HttpContext.Session[ConstHelper.Username] != null)
            {
                username = filterContext.HttpContext.Session[ConstHelper.Username].ToString();
            }
            InfoLog.Log(username, actionName, controllerName);
        }
    }
}

安全

若是每一個Controller都進行相同的安全檢查,代碼量很龐大,能夠設定一個SecurityController,而後全部的Controller都繼承與SecurityController。ui

using InfraStructure.Helper;
using InfraStructure.Log;
using InfraStructure.Table;
using InfraStructure.Utility;

namespace WebSite.Areas.Admin.Controllers
{
    public class DataViewSetController : SecurityController
    {

        // GET: Develop/DataViewSet
        public ActionResult Index()
        {

            var list = OwnerTableOperator.GetRecListByOwnerId<DataViewSet>(DataViewSet.CollectionName, OwnerId);
            //MasterTable Sort Function
            //list.Sort((x, y) => { return x.Rank - y.Rank; });
            ViewData.Model = list;
            return View();
        }

本質上仍是在運行Action的時候(OnActionExecuting),進行一些搶先過濾。this

using System.Web.Mvc;
using BussinessLogic.Security;
using InfraStructure.Utility;

namespace WebSite
{
    public class SecurityController : Controller
    {
        /// <summary>
        ///     驗證
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (Session[ConstHelper.OwnerId] == null)
            {
                filterContext.Result = RedirectToAction("Index", "Home", new { area = "Admin" });
                return;
            }
            OwnerId = Session[ConstHelper.OwnerId].ToString();
            EmployeeInfoType = Session[ConstHelper.EmployeeInfoType].ToString();
            Username = Session[ConstHelper.Username].ToString();
            AccountCode = Session[ConstHelper.Account].ToString();
            Privilege = Session[ConstHelper.Privilege].ToString().GetEnum(PrivilegeType.None);
            ViewBag.Privilege = Privilege;
            ViewBag.OwnerId = OwnerId;
        }
    }
}

MVC6

Area

若是你上網檢索關於Area的信息,下面的文章大概會引發你的關注,惋惜裏面的Sample已經沒有了。
using areas in asp-net-5

若是你想完整的看一個MVC6帶有Area的例子,MusicStore則應該能夠知足你的需求。
MusicStore示例

Area的目錄結構仍是和MVC5同樣:MusicStore/Areas/Admin/
這個也沒有什麼好修改的。至於Area的路由問題,將在路由裏面進一步討論。

Filter

下面這篇文章很好的介紹了Filter的問題,目錄結構仍是和MVC5同樣(原做者已經更新到RC2了)

asp-net-5-action-filters

Because the filters will be used as a ServiceType, the different custom filters need to be registered with the framework IoC. If the action filters were used directly, this would not be required.

這裏也是須要爲Filter進行註冊了,只是註冊的方式變成下面的方式:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddScoped<ConsoleLogActionOneFilter>();
    services.AddScoped<ConsoleLogActionTwoFilter>();
    services.AddScoped<ClassConsoleLogActionOneFilter>();
}

工具製做(計劃中)

界面和總體流程

我在考慮是否要作這樣一個工具:
工具的界面以下所示,兩個文本框,一個是MVC5目錄,一個是MVC6目錄。一個升級按鈕。
而後一鍵能夠將MVC5 儘量 得升級到MVC6。

總體工具的框架也很簡單

/// <summary>
        ///     Start To Upgrade MVC5 to MVC6
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnUpgrade_Click(object sender, EventArgs e)
        {
            //測試用開始
            txtMVC5Root.Text = @"E:\WorkSpace\DominoHR\WebSite";
            txtMVC6Root.Text = @"E:\WorkSpace\MVCMigratiorLib";
            //測試用結束

            SystemManager.mvc5Root = txtMVC5Root.Text;
            SystemManager.mvc6Root = txtMVC6Root.Text;
            //Init(Log準備)
            SystemManager.Init();
            //Analyze The Folder
            StructureAnalyze.Analyze();
            //Upgrade
            MainUpgrade.Upgrade();
            //Terminte(Log關閉)
            SystemManager.Terminate();
        }

這裏的代碼省略LOG輸出等次要可是必須的功能介紹,一個好的工具必須有LOG。同時,這個工具不能對原來的MVC5文件進行任何的修改,這個是大原則,全部的文件都必須複製到新的目錄下面進行修改

在考慮MVC6的目錄以前,咱們先來看看如何分析MVC5的目錄結構。
這裏很簡單,就是把頂層目錄都遍歷一遍便可,沒有什麼技術含量。固然,因爲目錄信息都保存起來了,很容易作出HasArea,HasFilter這樣的屬性方法。

/// <summary>
        /// Has Areas
        /// </summary>
        public static bool HasAreas {
            get {
                return RootFolder.ContainsKey(strAreas);
            }
        }

        /// <summary>
        /// Analyze 
        /// </summary>
        public static void Analyze() {
            //Get Top Level Folder List
            foreach (var topLevelFolder in Directory.GetDirectories(SystemManager.mvc5Root))
            {
                string folderName = new FileInfo(topLevelFolder).Name;
                RootFolder.Add(folderName, topLevelFolder);
                SystemManager.Log("topLevelFolder:" + folderName);
            }
            AppendReport();
        }

本文已經同步到 http://www.codesnippet.info/Article/Index?ArticleId=00000024
ASP.NET從MVC5升級到MVC6 總目錄

相關文章
相關標籤/搜索