今天幫同事找問題時碰到 Mvc 序列化 Json 的地雷,問題出在 Controller.Json()
是使用 JavaScriptSerializer
序列化物件成 Json 字串,日期經轉換後無法讓前端 JavaScript 直接套用 new Date()
。
ASP.NET MVC
[轉載] 系統架構設計
來源:[鐵人30天]當ASP.NET MVC 遇見 Angular.js MVC 客戶管理系統(3) – 系統架構設計
* 為何要分層架構規劃
分層的重要性,也是寫程式幾年後才真的領悟,ASP時代那種義大利麵式的寫法(html、Script、後端程式、SQL程式全混再一起),接著WebForm CodeBind將前後端拆後,再到現在慣用的MVC,深深覺得”關注點分離”這的確是一件很重要的事,目前開發習慣,即使專案在小,也會整理出自己的三層架構起手式,如果是更大型專案,就會再更細分。
架構圖:
各分層介紹:
- DAL(Data Access Layer) : 有關SQL語法、EntityFream資料庫溝通,都會在此層。
- BLL(Business Login Layer):資料流從DB或從User輸入後,一定會有一些邏輯判斷,該商業邏輯流程都會寫在此層
- UI(User Interface):有關Web專案、Web API(Web Service)、Apps 都會在此層
- Domain Entity : 裡面會有定義ViewModel Class,用來貫穿三個分層
- Resources:用來放一些資源檔 … e.g 多國語系檔
- Utility : 用來放置一些共用函示庫 … 如加解密
整個專案建立完如下圖:
[C#][ASP.NET MVC5] 繼承 ValidationAttribute 簡單實作表單欄位驗證
有用過MVC的人一定都知道只要在Model上面加標籤[Required]
,即可達到前後端驗證欄位必填的效果,無聊研究了一下來簡單實作自定義的表單欄位驗證 !!
- 首先建立一個類別,繼承
ValidationAttribute
,宣告m_BaseText
變數來儲存預設禁止的文字,而m_Text
則是用來儲存禁止其他文字用的變數。public class ExampleAttribute : ValidationAttribute { private string[] m_Text; private string[] m_BaseText = new string[] { @"\", @"/", @"<", @">" }; }
- 接下來利用多載宣告兩個建構子,一個是不帶參數的,另一則是型別為
string[]
的Text
變數,並放到剛剛宣告的m_Text
裡面。public ExampleAttribute() { m_Text = new string[] { }; } public ExampleAttribute(string[] Text) { this.m_Text = Text; }
- 接下來把
IsValid
方法override
掉,並實作驗證機制。protected override ValidationResult IsValid(object value, ValidationContext validationContext) { string strValue = (string)value; string[] strBaseError = m_BaseText.Where(x => strValue.Contains(x)).ToArray(); string[] strCustomError = m_Text.Where(x => strValue.Contains(x)).ToArray(); if (strBaseError.Count() == 0 && strCustomError.Count() == 0) { return ValidationResult.Success; } else { List<string> temp = new List<string>(); temp.AddRange(m_BaseText.ToList()); temp.AddRange(m_Text.ToList()); string errorMsg = $"禁止輸入 [{string.Join(", ", temp.ToArray())}] !!"; return new ValidationResult(errorMsg); } }
- 完成 !!
原始碼:https://github.com/shuangrain/WebApplication_CustomAttribute
[C#][ASP.NET MVC5] 繼承 AuthorizeAttribute 來實作自訂驗證
有時候會需要頁面會需要依照使用者權限的不同,可以進入的頁面也不同,在MVC裡面有預設Role與User的方式來篩選使用者,不過有時候權限分細一點時就沒辦法應付了,這個時候就需要自訂驗證了。
權限表單的結構資料如下:
- 先在登入成功的地方放入一段程式,來把使用者有權限進入的頁面以字串的方式存入Session
using (var db = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString)) { String account = HttpContext.User.Identity.Name.ToString(); var list = db.Query<int>(@" SELECT [a].[List_Id] FROM [dbo].[Permissions] AS [a] WHERE [a].[User_Id] = ( SELECT [Id] FROm [dbo].[AspNetUsers] AS [z] WHERE [z].Email = @Email)", new { Email = model.Email }).ToList<int>(); Session["Permissions"] = string.Join(",", list.ToArray()); }
- 再來新建一個檔案名為
CustomAuthorize.cs
,程式碼如下:using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace WebApplication_CustomVerification.Verification { public class CustomAuthorize : AuthorizeAttribute { public int ListId { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext) { if (httpContext == null) { throw new ArgumentNullException("httpContext"); } //判斷是否已驗證 if (httpContext.User.Identity.IsAuthenticated == false) { return false; } bool boolIsRight = false; //Session過期,要求重新登入 HttpSessionStateBase session = httpContext.Session; if (session.Count != 0 && session["Permissions"] != null && session["Permissions"].ToString() != "") { List<string> list = session["Permissions"].ToString().Split(',').ToList(); foreach (var item in list) { if (item == ListId.ToString()) { boolIsRight = true; break; } } } return boolIsRight; } } }
這邊覆寫了原本驗證的機制,改成判斷先前存入Session內的字串。
- 這樣就可以在Action上面加上標籤來驗證使用者權限囉!
using System.Web.Mvc; using WebApplication_CustomVerification.Verification; namespace WebApplication_CustomVerification.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } [CustomAuthorize(ListId = 1111)] public ActionResult List_01() { return View(); } [CustomAuthorize(ListId = 1112)] public ActionResult List_02() { return View(); } [CustomAuthorize(ListId = 1113)] public ActionResult List_03() { return View(); } } }
範例程式:https://github.com/shuangrain/WebApplication_CustomVerification
[C#][ASP.NET MVC5] 使用 jQuery Form Plugin 與 HttpPostedFileBase 檔案上傳
先前提到過 [C#][ASP.NET MVC5] 使用 HttpPostedFileBase 檔案上傳 ,這次我要使用Ajax的方式上傳檔案,但研究了Ajax.BeginForm
許久都無法上傳檔案,最後找到使用jQuery Form Plugin來實作此功能。
來源:
使用NuGet安裝所需套件
- 安裝jQuery Form Plugin
- 安裝Javascript Alert:toastr
View
@{ ViewBag.Title = "Upload"; } <h2>Upload</h2> <hr /> @using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { <div class="form-horizontal"> <div class="form-group"> <label class="control-label col-sm-2">選擇檔案</label> <div class="col-sm-10"> <input type="file" name="file"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" value="提交" class="btn btn-primary" /> </div> </div> </div> } @section css { @Styles.Render("~/Content/toastr") } @section scripts { @Scripts.Render("~/bundles/jqueryajax") @Scripts.Render("~/bundles/jqueryform") @Scripts.Render("~/bundles/toastr") <script> $("form").ajaxForm({ iframe: true, dataType: "json", success: function (result) { $("form").resetForm(); if (result.success) { toastr.success(result.message, 'Success Message') } else { toastr.error(result.message, 'Error Message') } }, error: function (xhr, textStatus, errorThrown) { $("form").resetForm(); toastr.error('檔案上傳錯誤.', 'Error Message') } }); </script> }
HTML的部分可以不用更動,無須使用Ajax.BeginForm
,只需要在JavaScript的地方使用ajaxForm
即可將表單轉換成Ajax模式。
Controller
[HttpPost] public JsonResult Upload(HttpPostedFileBase file) { if (file != null && file.ContentLength > 0) { var fileName = Path.GetFileName(file.FileName); var path = Server.MapPath("~/App_Data/FileUploads"); //若該資料夾不存在,則新增一個 if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } path = Path.Combine(path, fileName); file.SaveAs(path); return Json(new { success = true, message = fileName, ContentLenght = file.ContentLength }, "text/html"); } else { return Json(new { success = false, message = "請上傳正確的檔案." }, "text/html"); } }
由於前端使用Ajax所以後台回傳資訊則必須從ActionResult
修改成JsonResult
,這樣才能強制回傳Json格式,需要注意的一點是在IE底下若無指定contentType
的話則會出現問題如下圖,必須指定回傳的contentType
為text/html
才能在IE底下正常接收資訊。
[C#][ASP.NET MVC5] MySQL Entity Framework 學習筆記(一) – 環境設定
剛開始在碰ASP.NET MVC的時候,查詢該如何連結資料庫時常常會聽到Entity Framework這個方法,但由於那時沒有太多時間,沒去查太多資料都用SQL自幹,現在閒下來了,就來玩玩看這個東西,首先是環境設定:
- 從
NuGet
安裝MySQL使用Entity Framework時所需要的套件
- 總共有三個東西要安裝,分別是
- MySql.Data
- MySql.Data.Entity
- MySql.Web
- 安裝完畢後就要開始設定連結字串,打開
Web.config
新增連結字串<connectionStrings> <add name="MySqlConnectionString" providerName="MySql.Data.MySqlClient" connectionString="Server=server;Database=dbname;UID=username;Password=password" /> </connectionStrings>
- 建立
MySqlContext.cs
並繼承DbContext
供後續使用public class MySqlContext : DbContext { public MySqlContext() : base("MySqlConnectionString") { } }
結論:
這樣環境就建置完成囉,後續就可以開始使用Entity Framework了!
參考:
[C#][ASP.NET MVC5] FileUpload 上傳檔案大小的限制
ASP.NET為防止駭客利用大檔案進行DOS(Denial Of Service)攻擊,所以把上傳檔案大小限制在4096KB(4MB),因此當上傳檔案超過4MB時,就會收到System.Web.HttpException 超出最大的要求長度的錯誤訊息如下:
那如果需要上傳超過4MB的檔案怎麼辦?那就必須修改Web.config
的system.web
讓該站自訂大小,修改內容如下:
<system.web> <httpRuntime targetFramework="4.5" maxRequestLength="102400" executionTimeout="600"/> </system.web>
- maxRequestLength 檔案大小(KB)
- executionTimeout 上傳時間(秒)
修改完後雖然不會跳出上面的錯誤了,不過卻跳出另外一個訊息:
拜了Google才發現,原來 Windows Server 2008 (IIS 7.0) 上又多了一個 maxAllowedContentLength 屬性 (單位為 Byte),於是乎又打開了Web.config
找到system.webServer
,修改內容如下:
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="1073741824" /> </requestFiltering> </security> </system.webServer>
其預設值為 30000000 Bytes (~28.6 MB),所以上傳 30 MB 的檔案就會產生錯誤了。這邊我是修改為1 GB (1024 x 1024 x 1024)可視情況調整。
綜合以上的修改結果為:
如果使用者上傳超過設定的最大上限怎麼辦?是不是又會跳出錯誤訊息?不要害怕,只要在Global.asax裡面處理這項錯誤即可,處理方式如下:
protected void Application_BeginRequest(object sender, EventArgs e) { HttpRuntimeSection section = (HttpRuntimeSection)ConfigurationManager.GetSection("system.web/httpRuntime"); int maxFileSize = section.MaxRequestLength * 1024; if (Request.ContentLength > maxFileSize) { try { Response.Redirect("~/SizeError.aspx"); } catch (Exception ex) { Logger logger = LogManager.GetCurrentClassLogger(); logger.Warn(ex.Message); } } }
結論:
一個簡單的上傳功能,想不到有這麼多眉眉角角需要注意,做這行真是不容易QQ
參考:
[C#][ASP.NET MVC5] 使用 HttpPostedFileBase 檔案上傳
檔案上傳功能在網頁中常常出現,在ASP.NET MVC裡面要上傳檔案非常簡單,這裡簡單筆記一下有關 ASP.NET MVC 的檔案上傳基本操作方法。
- Controller
[HttpPost] public ActionResult Upload(HttpPostedFileBase file) { if (file != null && file.ContentLength > 0) { var fileName = Path.GetFileName(file.FileName); var path = Server.MapPath("~/App_Data/FileUploads"); //若該資料夾不存在,則新增一個 if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } path = Path.Combine(path, fileName); file.SaveAs(path); } return RedirectToAction("Upload"); }
- View
@using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { <div class="form-horizontal"> <div class="form-group"> <label class="control-label col-sm-2">選擇檔案</label> <div class="col-sm-10"> <input type="file" name="file"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" value="提交" class="btn btn-primary" /> </div> </div> </div> }
這邊要注意的是,如需要使用
HttpPostedFileBase
上傳檔案的話,則必須添加enctype = "multipart/form-data"
,不然怎麼樣都沒辦法正常收到資料!
另外如果使用下列語法是無法接收資料的,因這樣表單並沒有含enctype = "multipart/form-data"
這項@using (Html.BeginForm("Upload", "Home", new { enctype = "multipart/form-data" }))
一定要用改為下面的方法
@using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
[C#][ASP.NET MVC5] 使用Ajax.BeginForm時無法正常運作
在ASP.NET MVC裡面,表單提交方法有兩種,一種是傳統的Html.BeginForm
另外一種是利用JQuery實現的Ajax.BeginForm
,平常我都是使用Html.BeginForm
在處理表單,最近在寫上傳檔案功能的時候想玩Ajax.BeginForm
看看,於是乎發生了Ajax無法正常運作的問題,拜了Google大神很久發現了原因是Visual Studio新建專案的時候沒有幫我加入Microsoft jQuery Unobtrusive Ajax這個套件,多虧這個原因讓我撞牆撞了好久,最後裝上去才解決問題。
解決步驟如下:
- 對著專案點選右鍵選擇
管理NuGet套件
- 搜尋
unobtrusive
找到Microsoft.jQuery.Unobtrusive.Ajax
選擇安裝
- 在
/App_Start/BundleConfig.cs
裡面新增套件
bundles.Add(new ScriptBundle("~/bundles/jqueryajax").Include( "~/Scripts/jquery.unobtrusive-ajax*"));
- 在要使用的頁面加入以下語法使用
@Scripts.Render("~/bundles/jqueryajax")