2013年10月31日 星期四

Asp.net MVC 實作TempData應用自訂Error(10)

先廢話一下
大部份都會設計刪Master時Detail也順便刪
依需求所以做個不一樣的
如果Detail是有資料時就提示不能刪除Master
這樣好像讓資料更安全點
使用TempData來紀錄錯誤的訊息
為何用TempData因為他只會被使用一次
所以在post錯誤時傳給Get一次顯示
  public ActionResult Delete(short? id)
        {            
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
     //重點在這裡接收後顯示錯誤
            if (TempData["CustomError"] != null)
            {
                ModelState.AddModelError(string.Empty, TempData["CustomError"].ToString());
            }

            //Account account = db.Account.Find(id);
            var query = from u in db.Tab where u.TabID == id;

            if (query == null)
            {
                return HttpNotFound();
            }
            return View(query);
        }

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(short? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            string Sql = " TabID == @0";
            var query1 = (from u in db.TabColumn.Where(Sql, id)
                          select u).ToList();
            if (query1.Count() > 0)
            {               
  //有錯時丟給Get顯示
                TempData["CustomError"] = "頁籤內有欄位所以不可刪除";
                return RedirectToAction("Delete",null, id);
            }

            Tab query = db.Tab.Find(id);
            db.Tab.Remove(query);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
在View裡記得加以下才會顯喔
@Html.ValidationSummary(true)
參考網址

2013年10月30日 星期三

Asp.net MVC 實作TextBoxFor DataFormat(9)

先廢話一下
多實作多健康
一般會在Model裡設定DataFormat
[DisplayName("時間")]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:yyyy-MM-dd HH:mm:ss}", ApplyFormatInEditMode = false)]
public Nullable pDate { get; set; }
以上在list用DisplayFor都沒什麼問題
但TextBoxFor時如果MS-SQL是中文版的還是會出現上午、下午這種字眼當然驗證就不會過了
改成如下就能正常顯示yyyy-MM-dd HH:mm:ss
@Html.TextBoxFor(model => model.LoginDate, "{0:yyyy-MM-dd HH:mm:ss}", new { @class = "form-control", @readonly = "readonly" })
參考網址
點我

Asp.net MVC 實作使用TryUpdateModel更新資料(8)

先廢話一下
最近真的越寫MVC越上癮了
雖然因為很不熟一直卡
但是做出來的成就感特別好
有些東西做出來但是還是有些詳細的原理還需了解

終於做到需要開始CRUD了
首先針對會員資料做更動
最先遇到的是修改欄位後有些欄位不更新
找了兩篇相當好的文章 點我 點我
使用TryUpdateModel有10個多型能使用
重點再於第四個參數
為不更新的欄位名稱
沒輸入密碼時不更新>
  [HttpPost]
        [ValidateAntiForgeryToken]
       
        public ActionResult Edit(int Id, AccountEdit Form)
        {
            if (ModelState.IsValid)
            {              
                Account query = db.Account.Find(Id);               
                List list = new List();

                //  沒打 是空時 時不更新
                if (Form.Password == null || Form.Password.ToString().Equals(""))
                {
                    list.Add("Password");
                }
           
                if (TryUpdateModel(query,"",null,list.ToArray()))
                {
                    try
                    {
                        // 有打時 不是空 這段必需要寫在TryUpdateModel
                        if (Form.Password != null && !Form.Password.ToString().Equals(""))
                        {                            
                            query.Password =  PublicFunction.EncodeStringMD5(Form.Password); 
                        }                      

                        db.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        string e = ex.Message.ToString();
                        throw;
                    }
                   
                    return RedirectToAction("Index");
                }
                return View(query);
            }
            return View(Form);          
        }
       
有個特別的地方就是要輸入時才更新密碼部份
原本寫在TryUpdateModel外面感謝保哥指導
"TryUpdateModel會蓋掉 model的資料"
"因為他在這時後才做 Model Binding"
所以要寫在TryUpdateModel裡面
參考網址
點我
點我
微軟官方

2013年10月29日 星期二

Asp.net MVC 實作一個View多個Model(7)

先廢話一下
這功能看似簡單
但不知如何架構跟下語法可能會寫不出來
先在ViewModels裡建一個Class裡面有三個IEnumerable
public class TabContainerVM
    public class TabContainerVM
    {
        public IEnumerable tab { get; set; }
        public IEnumerable tabcolumn { get; set; }
        public IEnumerable tabrow { get; set; }
    }
在Controller裡把資料自行加入到model裡後回傳
  public ActionResult TabContainer()
        {
            var tab = from u in db.Tab                      
                      select u;

            var tabcolumn = from u in db.TabColumn
                            select u;

            var tabrow = from u in db.TabRow
                         select u;

            var model = new TabContainerVM
            {
                tab = tab,
                tabcolumn = tabcolumn,
                tabrow = tabrow
            };

            return PartialView("_TabContainer",model);
        }
view的話就能使用
@model  ViewModels.TabContainerVM

@foreach (var tab in Model.tab) {
    @foreach (var tab in Model.tabcolumn ) { //do something
  • }
}
參考網址 點我

2013年10月27日 星期日

Google 分析網頁效能工具

Google 分析網頁效能工具
剛忽然逛到真的需要記一下

Asp.net MVC 實作RenderBody和RenderSection(6)

先廢話一下
MVC好像越來越有趣了
如果個人邏輯相當好的話
可以在View中發揮的彈性很大
因為MVC網頁一種個感覺就很像在挖洞
可以到處填補彈性的確比WebFrom來的好
我想RenderBody和RenderSection的範例在新增一個專案後就會有了
RenderBody搭配一個_Layout所以一個_Layout只會有一個RenderBody
只要將頁面的_Layout路徑設定即能套用
  @RenderBody()
RenderSection在_Layout裡可以有許多的RenderSection
但切記是_Layout裡而不是在一般頁面裡喔
如果直接在一般頁面裡他會跳出以下錯誤
無法直接要求檔案 "~/Views/Home/Index.cshtml",因為它會呼叫 "RenderSection" 方法。

_Layout裡
required為true表示您只要是套這個_Layout的頁面都必需要又head的section不然會出錯這相當重要
  @RenderSection("head",required: false)
一般頁面
@section head{
//你的程式碼
}

2013-10-27補充
我做了一個測試在_Layout裡設定@RenderSection("head",required: false)
再將/Home/Index裡在呼叫一個Partial
而Partial裡加入
 @section head{
                
        }
在執行後Partial裡的程式碼完全不會顯示
所以Partial裡寫@section 是沒用的僅供參考摟!!

2013年10月26日 星期六

Asp.net MVC 實作登出及驗證(5)

先廢話一下
登入登出看似簡單但有許多需要注意的小地方
參考了很多網站才實作出來
上次做到Asp.net MVC 實作登入驗證(1)
當然要有驗證是否登入跟登出
找了許多站終於結合出比較有效的程式碼
驗證是否已登入,程式碼不長但很實用
if ( User.Identity.IsAuthenticated)
{
  //這邊是有驗證過已登入
}
如果是一整個Controller都需要驗證可加[Authorize]在Class上
如果整個Controller裡有不需驗證的可加[AllowAnonymous]
    [Authorize]
    public class HomeController : Controller
    {       
        [AllowAnonymous]
        public ActionResult Index()
        {
            ViewBag.Title = PublicFunction.WebSiteName; 
            return View();
        }

        public ActionResult Create()
        {
            return View();
        }
     
    }
以下是登出的完整程式碼
 [HttpPost]
        [ValidateAntiForgeryToken]     
        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            Session.Abandon();

            // clear authentication cookie
            HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
            cookie1.Expires = DateTime.Now.AddYears(-1);
            Response.Cookies.Add(cookie1);

            // clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
            HttpCookie cookie2 = new HttpCookie("ASP.NET_SessionId", "");
            cookie2.Expires = DateTime.Now.AddYears(-1);
            Response.Cookies.Add(cookie2);

            //FormsAuthentication.RedirectToLoginPage(); 
            return RedirectToAction("Index", "Home", null);
        }

參考網站
登出實作
Session.RemoveAll() 及Session.Abandon() 的差別

2013年10月25日 星期五

Asp.net MVC 實作FancyBox(4)

今天沒廢話

先到FancyBox下載套件
將js及css放入如圖

在/App_Start/BundleConfig.cs加入
bundles.Add(new ScriptBundle("~/bundles/fancybox").Include("~/Scripts/fancybox/jquery.fancybox.js"));
bundles.Add(new StyleBundle("~/Content/fancybox").Include("~/Content/fancybox/jquery.fancybox.css"));
View的部份新增兩個一個為主頁面(Index)一個為被呼叫頁面(FancyBox)
加入jQuery參考及FancyBox參考
ActionLink寫法如下

@Scripts.Render("~/bundles/fancybox")
@Styles.Render("~/Content/fancybox")

@Html.ActionLink("[新增]", "FancyBox", "Home", null, new { @class = "fancybox fancybox.iframe" })

2013年10月24日 星期四

Asp.net MVC 實作TinyMCE文字編輯器搭配@Html.Raw(3)

先廢話一下
昨天在想目前常接到需要文字編輯器的網頁程式
之前都是使用CkEditor但在MVC上不是很好套
所以找了一款我個人覺得蠻合適的編輯器TinyMCE
就看個人的需求來選擇
套用方式很簡單form部份如下
如果是強型別可改為@Html.TextAreaFor
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        @Html.TextArea("abc")
        
    }
再加入參考js及寫script




在action裡接收程式碼如下
當然也可以使用強型別接會更會
記得加[ValidateInput(false)]
不然會出現"具有潛在危險"Request.Form 的值已從用戶端"
但最好使用時要小心最後是在後台有權限判斷才能使用
[HttpPost]
[ValidateInput(false)]
public ActionResult Index(FormCollection form)
{
  if(ModelState.IsValid)
  {
    string test = form["abc"];
  }
    return View();
}

2013-10-29補充
使用文字編輯器時通常為在前後產生

而在MVC中如果是以欄位
@item.content
來輸出裡面有

則會備程式編成html 所以需搭配

@Html.Raw(@item.content)

2013年10月23日 星期三

Asp.net MVC 實作常用的Model驗證(2)

先廢話一下
MVC重點在於關注點分離
保哥說過Model要重、Controller要輕、View要笨
所以設計Model相當重要
為來減輕Controller跟View所以很多事情都要拿到Model來做
表單欄位驗證就必需要在Model裡做

以下就寫幾個會常用到的欄位驗證(os其實是以後自己要copy用)
//先using以下兩個命名空間
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
以下幾個例子大概都包含了常用的
public class AccountInfo
{

        [Key]
        public int AccountID { get; set; }

        [DisplayName("登入帳號")]
        [Required(ErrorMessage = "請輸入登入帳號")]
        [StringLength(40, ErrorMessage = "登入帳號最多20個字")]
        public string AccountName { get; set; }

        [DisplayName("密碼")]
        [Required(ErrorMessage = "請輸入密碼")]        
        [MaxLength(20, ErrorMessage = "密碼最多20個字")]
        [MinLength(8, ErrorMessage = "密碼最少8個字")]        
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [DisplayName("確認密碼")]
        [DataType(DataType.Password)]
        [Compare("Password", ErrorMessage = "密碼與確認密碼不符")]
        public int ConfirePassword { get; set; }

        [DisplayName("姓名")]
        [StringLength(10, ErrorMessage = "姓名最多10個字")]   
        public string Name { get; set; }

        [DisplayName("生日")]     
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime BirthDay { get; set; }

        [DisplayName("年齡")]
        [Range(1,100,ErrorMessage="年齡請輸入1~100歲")]    
        public int Age { get; set; }

        [DisplayName("電子郵件")]
        [EmailAddress(ErrorMessage="信箱格式錯誤")]
        public string Email { get; set; }

        [DisplayName("個人部落格")]
        [Url (ErrorMessage="連結格式錯誤")]
        public string BolgUrl { get; set; }

       [DisplayName("薪資")]
       [RegularExpression(@"^d+$", ErrorMessage = "請輸入數字.")]
        public int Salary { get; set; }
}

2013年10月22日 星期二

Asp.net MVC 實作登入驗證(1)

先廢話一下
離上次寫MVC文章剛好一年了
這一年裡基本上我都沒機會去使用MVC
剛好這次公司有點數所以去上了課
自己又投資自己去上保哥的MVC5課程
剛好兩邊教法不一樣有些重點也不一樣
趁著還熟著時順手向同事要了個已經快開發成的案子
當然第一次寫要上線應該很難
接下來幾天會針對實作及遇到的問題做整理

一開始我想剛學完基礎的人
基本上跟我一樣不知如何下手
隨便要個舊專案或是拿個不是很重要的案子來寫
首先應該會從會員開始吧
但千萬別從新增會員開始
因為這樣就有點難了
我是以VS2013來開發目前是RTM
1.新增一個專案 > .net版本選4.5 > 選MVC > 建好了網站 > 刪除一些不必要的原件(Controllers Views Models)裡都刪了 只留View裡的Shared
2013範本是Bootstrap 3.0,jQuery 1.10.2。
2.開發分成Code First、DB First、Model First因個人習慣DB First所以先來建資料庫,先求簡單只有三個欄位,ID、登入帳號、密碼,但有一個重點記得設PK,EF必要的就是PK,這次實做簡單登入 所以直街先手動新增一筆假一料
3.建立一個ADO.NET 實體模型資料庫 > 由資料庫產生 > 新增連接 > 依提示操作設定連線資訊 > 測試連接看能不能成功 > 完成後記得build一下
4.在Controller新增一個AccountController 在新增一個View /Account/Login.cshtml
因為在微軟內建的驗證中有一個已經幫我們做好驗正了
 app.UseCookieAuthentication(new CookieAuthenticationOptions
 {
         AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
         LoginPath = new PathString("/Account/Login")
 });
5.新增一個ViewModels資料夾來存放自己定義的model 加入
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
定義Model
    public class Login 
    {
        [DisplayName("登入帳號")]
        [Required(ErrorMessage = "請輸入登入帳號")]
        [StringLength(40, ErrorMessage = "登入帳號最多20個字")]
        public string AccountName { get; set; }

        [DisplayName("登入密碼")]
        [Required(ErrorMessage = "請輸入登入密碼")]
        [StringLength(40, ErrorMessage = "密碼最多20個字")]
        [DataType(DataType.Password)]
        public string  Password { get; set; }
    }
Login的畫面
2013-10-30更新
                @using (Html.BeginForm("Login", "Login", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
                {
                    @Html.AntiForgeryToken()
                    @*@Html.ValidationSummary()*@

                    

後台登入

@Html.LabelFor(model => model.AccountName, new { @class = "control-label" })
@Html.TextBoxFor(model => model.AccountName, new { @class = "form-control", placeholder = "請輸入登入帳號" }) @Html.ValidationMessageFor(model => model.AccountName, null, new { @class = "help-inline" })
@Html.LabelFor(model => model.Password, new { @class = "control-label" })
@Html.PasswordFor(model => model.Password, new { @class = "form-control", placeholder = "請輸入登入密碼" }) @Html.ValidationMessageFor(model => model.Password, null, new { @class = "help-inline" })
@TempData["Error"]
}
登入的Action
2013-10-30更新
      [HttpPost]
        [ValidateAntiForgeryToken]     
        public ActionResult Login(Login form)
        {
            if (ModelState.IsValid)
            {               
                //驗證資料庫登入
                //這邊請使用自行驗證摟
                string Sql = " AccountName == @0 and Password == @1 and IsUsed == @2";
                var query = (from u in db.Account.Where(Sql, form.AccountName, form.Password, "true")
                             select u).ToList();

                if (query.Count() != 1)
                {                   
                    ModelState.AddModelError(string.Empty, "帳號或密碼錯誤登入失敗");
                    return View("Index",form);
                }

                try
                {
                    query[0].LoginIP =PublicFunction.GetIpAddress();
                    query[0].LoginDate = DateTime.Now;
                    db.SaveChanges();
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError(string.Empty, ex.Message.ToString());
                    return View("Index", form);
                }
              

                bool isPersistent = false;//如果票證將存放於持續性 Cookie 中 (跨瀏覽器工作階段儲存),則為 true,否則為 false。 如果票證是存放於 URL 中,則忽略這個值。
                string userData = "";//可放使用者自訂的內容
                string mAccountID = query[0].AccountID.ToString();
               
                //寫cookie
                //使用 Cookie 名稱、版本、到期日、核發日期、永續性和使用者特定的資料,初始化 FormsAuthenticationTicket 類別的新執行個體。 此 Cookie 路徑設定為在應用程式的組態檔中建立的預設值。
                //使用 Cookie 名稱、版本、目錄路徑、核發日期、到期日期、永續性和使用者定義的資料,初始化 FormsAuthenticationTicket 類別的新執行個體。
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                  mAccountID,//使用者ID
                  DateTime.Now,//核發日期
                  DateTime.Now.AddMinutes(1800),//到期日期 30分鐘 
                  isPersistent,//永續性
                  userData,//使用者定義的資料
                  FormsAuthentication.FormsCookiePath);
              
                string encTicket = FormsAuthentication.Encrypt(ticket);
                Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

                //HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
                //cookie.Expires = ticket.Expiration;
                //Response.Cookies.Add(cookie);

                //FormsAuthentication.RedirectFromLoginPage(strUsername, isPersistent);

                if (form.ReturnUrl != null)
                {
                    return Redirect(form.ReturnUrl.ToString());
                }
                else
                {
                    return RedirectToAction("Index", "Admin");
                }
                
            }
            //return RedirectToAction("Index", "Login", null);
            return View("Index", form);
        }

最後再需要驗證的Class上加[Authorize] 雖然大部份程式碼都是copy來的
但也花了一下午才了解如何登入
看來還真有的學呢

2013-10-26補充 請注意Web.Config裡的設定

修改成
 
      
    

2013-10-30更新
在login頁多加一個returnUrl
這樣使用者登入後可直接到他需要的頁面
上面的View跟Controller都需更新
   public ActionResult Index(string returnUrl)
        {            
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }   

2013-11-13更新
MVC5使用範本新增如果加了authentication mode="Forms"
此時自訂的驗證會不正常
修改
public override void ExecuteResult(ControllerContext context)
            {               
                var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
                if (UserId != null)
                {
                    properties.Dictionary[XsrfKey] = UserId;
                }
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
改為
public override void ExecuteResult(ControllerContext context)
            {
               //多下列這行
                context.RequestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
                var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
                if (UserId != null)
                {
                    properties.Dictionary[XsrfKey] = UserId;
                }
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
參考網址
點我前往
點我前往
點我前往
點我前往
點我前往
點我前往

2013年10月3日 星期四

Google 讓Google幫你翻譯你的網站吧

剛在查Html5 TAG時偶然發覺得
這算是一個相單不錯的功能
但前提是你必需相信Google翻譯
點我前往

因為使用方式相當簡單所以就不另外寫了
還是不會使用的請點下面連結
網路上的教學

2013年10月1日 星期二

IIS Html5 Video tag影片不能撥放

我想Html5是一個必然的趨勢
他新增了許多的tag然後各大瀏覽器都會陸續支援功能
使用方式點我進入

但看標籤是IIS
所以今天並不是要講怎麼使用html5標籤來撥影片
基本上看了範例應該不難

將網頁及影片放到IIS上後發覺影片不動了
看原始碼點了連結發現原來404找不到檔案
看了一下原來IIS並不認識.mp4的副檔名是什麼


處理方式如下
在IIS上 > MIME類型右鍵 > 開啟功能
新增

副檔名打mp4
MIME類型打application/octet-stream

以上完成後瀏覽器有支援就能撥放

以下連結是有關html5 video 的好站
Safari Html5 Developer
點我前往