目錄javascript
寫了一個.Net MVC的我的筆記,可是不是MarkDown,好難受,博客園也不支持之前的舊文章轉MarkDown,之後有時間看看能不能整理一下,此次新開一個MarkDown的html
在母版頁的footer裏面寫前端
<footer> @RenderSection("Footer") </footer>
而後在子頁中寫java
@section Footer{ <h3>你們好,我是腳</h3> }
這樣就能夠把子頁的內容放到母版頁的節點位置了程序員
public ActionResult Index () { ViewBag.UserName = "小李飛刀"; ViewData["UserName"] = "陸小鳳"; TempData["UserName"] = "楚留香"; //臨時數據 User model = new User { UserName = "謝曉峯" }; return View (model); //這行代碼其實就至關於ViewData.Model=model }
View代碼web
@{ ViewBag.Title = "Index"; } <div>@ViewBag.UserName </div> <div>@ViewData["UserName"] </div> <div>@TempData["UserName"] </div> <div>@Model.UserName</div>
結果是:ajax
陸小鳳數據庫
陸小鳳編程
楚留香json
謝曉峯
緣由是ViewData和ViewBag本質上都是【ViewDataDictionary】類型,而且二者之間的數據共享,只不過提供了不一樣的語法操做方式而已。因此「陸小鳳」覆蓋了原先的值」小李飛刀「。
TempData使用一次以後會當即刪除數據
至於Model,在視圖裏面能夠直接使用@Model.username進行使用,可是你會發現沒有提示,這個是由於編譯器沒法在編譯的時候獲取Model的類型,若是想要提示能夠這樣作,在視圖的上面寫上@model是user類型的,注意必須是@model小寫的,不能是@Model
@model User
有的時候我在項目的文件夾里加了一些文件,可是Visual Studio不顯示,明明已經加了,就是不顯示,這時候能夠點擊顯示全部文件按鈕,以下圖
點擊了這個按鈕,全部的文件都會顯示了
Ajax在.net MVC中用的很是多,通常是用來從後端獲取數據,而後無刷新加載,Ajax的特色就是無刷新加載
基本上使用Ajax都是使用Jquery的Ajax,因此Jquery的js文件引用一下,最好放在母版頁裏面,這樣全部的子頁直接開寫
前端,Razor
@{ ViewBag.Title = "Index"; } <h2>我是學習ajax的頁面</h2> <div> <p id="text">我是字段</p> <button id="ctbtn">傳統ajax</button> </div> <script> $(function () { $('#ctbtn').click(function () { $.post('/Ajax/GetData', { id: 666 }, function (data, status) { $('#text').html(data + ' 狀態是:' + status); }); }); }); </script>
後端:
public ActionResult GetData(int id) { return Json("普通的Ajax"+id,JsonRequestBehavior.AllowGet); }
其實前端的ajax有三種,load,get和post,也能夠寫成
<script> $(function () { $('#ctbtn').click(function () { $.get('/Ajax/GetData', { id: 666 }, function (data, status) { $('#text').html(data + ' 狀態是:' + status); }); }); }); </script>
新建一個Model,我新建的是User,以下
public class User { public string Name { get; set; } public string Sex { get; set; } public int Phone { get; set; } }
而後前端Razor如此:
<script> $(function () { $('#ctbtn').click(function () { $.post('/Ajax/GetData', { id: 666 }, function (data, status) { $.each(data, function (key, value) { //console.log(data[key].Name); 不須要再使用data了,能夠直接使用value,這個value就是一個User對象,後面的屬性記得保持一致,沒有提醒 console.log(value.Name); }); }); }); }); </script>
後端返回一個Json就能夠了
public ActionResult GetData(int id) { List<User> userList = new List<User>() { new User(){ Name="蜀雲泉",Sex="男",Phone=123 }, new User(){ Name="許嵩",Sex="男",Phone=123 }, new User(){ Name="林俊杰",Sex="男",Phone=123 } }; return Json( userList ); }
看看效果圖
這個簡單的介紹一下,什麼是非入侵式,就是前端頁面只有純粹的HTML和CSS,HTML元素裏面沒有一丁點的JavaScript,好比onclick方法之類的,全部的JavaScript都是單獨的一個文件,這就是非入侵式,可是,根據我目前的水平來看,根據我目前接觸的項目來看,JavaScript都是寫在Razor裏面的,並無作到非入侵式,因此簡單的介紹一下
打開.Net MVC的web.config文件,你能夠發現以下
<add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" />
這兩行就是開啓客戶端驗證和非入侵式JavaScript的
若是隻想在指定頁面關閉非入侵式,能夠在頁面寫
@{ Html.EnableUnobtrusiveJavaScript(false); } @{ Html.EnableClientValidation(false); }
==講真這個非入侵式開啓關閉什麼的,我是沒有看懂的,有什麼用?==
這個是封裝的,分爲異步鏈接按鈕和異步表單,知道了就好了,感受用的很少,不學
爲何會有Areas?由於多人協做,好比一個網站,我作購物車,你作商品管理,他作權限驗證,若是使用普通的都在Controller文件夾裏面新建Controller,在View文件夾裏新建View,你會發現,好亂啊
Areas出現了,我作購物車,使用購物車Areas,你作商品管理,使用商品管理Areas
固然,首先每一個部分都得有必定的複雜度,要是很簡單的幾個Controller就完成的模塊,就不要分Areas了
新建一個AdminAreas,新建完成以後以下
點開AdminAreaRegistration看看,很明顯裏面的路由變成了Controller前面加了Admin
在Admin這個區域裏面的Controller新建index,運行,都是同樣的
不知道發現了沒,看看上圖,除了路由的文件多加了一個Admin,其餘的文件夾很明顯的三層,Controller,Model,View,因此Areas其實就至關於新建了一個文件夾而已,都是在項目內
先說一下,轉移到類庫有什麼好處.首先,仍是我上面說的,具有必定複雜度的模塊纔會新建一個Areas,兩三個Controller就能夠實現的模塊不必新建Areas
那麼問題來了,多人協做的時候,我在Admin的Areas裏面新建東西,寫代碼,測試,我一提交,那麼StudyMVC這個項目的人就得獲取個人代碼,就得編譯,萬一個人AdminAreas有bug,他必須等我修復bug
新建一個類庫就不同了,我StudyMVC引用了你這個類庫,你類庫有問題不影響個人主項目,你編譯你本身的,我編譯我本身的
我第一次沒看清,新建的是.Net Standard,坑死了啊,選Net FrameWork類庫
而後把StudyMVC裏面的AdminAreas裏面的AdminAreaRegistration.cs複製到外面類庫AdminAreas裏面,複製以後,會報錯,這是由於必須引用兩個文件
第一個using System.Web.Mvc,使用Nuget引用本地的就行
第二個using System.Web,這個就更簡單了,直接在類庫的引用上面右鍵添加引用就能夠了
AdminAreaRegistration.cs文件複製到類庫以後,原項目裏面的就刪了,原項目StudyMVC右鍵添加服務引用,引用AdminAreas這個類庫
而後再來運行一下,輸入Admin這個區域加上原項目的Controller,發現運行同樣是ok的
若是你沒有運行成功,請再次檢查你的MVC項目有沒有引用區域類庫
==重點來了==
我如今在AdminAreas類庫裏面新建一個文件夾,叫Controller,而後新建一個Controller叫UserController,可能你沒法新建控制器,能夠從StudyMVC項目裏面複製一個控制器過來,改更名字就行了
UserController裏面新建一個Index方法,而後在MVC項目的Admin區域裏面的View裏面新建對應的User文件夾,下面新建Index.cshtml
給大家看看代碼吧
首先外部AdminAreas類庫的UserController
using System.Web.Mvc; namespace StudyMVC.Areas.Admin.Controllers { public class UserController : Controller { // GET: Admin/AreaTest public ActionResult Index() { ViewBag.user = "我是區域類庫裏面的UserController"; return View(); } } }
而後,我是MVC項目裏面的index.cshtml
@{ Layout = null; } <h2>什麼東西</h2> <h2>@ViewBag.user</h2>
最後,結果大圖
我講一下,這個區域啊,放到外部的類庫以後,就好多了.個人Admin區域的Controller在類庫裏,隨便過來一個程序員,你改吧,只要符合個人前端cshtml的要求,返回值不要動,其餘的邏輯代碼你隨便改
並且,改完以後,你本身在類庫裏面編譯,編譯成功後把dll丟給個人MVC項目就能夠了
看到這裏,可能有人會有疑問,爲何Controller丟在外面的類庫,視圖cshtml還在MVC 內部呢?
由於Controller是代碼須要編譯.....
視圖cshtml不須要編譯......
並且視圖一旦寫好了,不會常常修改的,反而是後臺,會須要修修改改,因此我只須要定好我這個前端cshtml須要的返回值,你類庫那邊的Controller怎麼寫我不關心,只要返回值給對就行
我終於學會了Areas了
代碼以下:
User user = null; if (user?.Name=="蜀雲泉") { Console.WriteLine("測試"); }
若是我不加?的話,由於user對象是null,因此我調用user.Name的時候會直接報錯
對象後接一個?就是判斷是否爲空的意思,若是是空的話就不會執行判斷了,很好用
過濾器通常用來作身份驗證,好比購物電商網站,你在購買的時候會檢測你是否登陸,若是沒登陸就讓你登陸,還有其餘不少地方須要身份驗證的,若是你每一個地方都寫一次身份驗證的代碼,那就違背DRY原則了,代碼重複了
因此,有了過濾器,這個至關於AOC切面編程
我不會詳細介紹,網上大把,我簡單的說一下怎麼用
通常都是在Filter文件夾下面新建,個人
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace StudyMVC.Filter { /// <summary> /// 這是我寫的第一個過濾器,過濾器一般用在身份驗證吧我感受,F12進去看看ActionFilterAttribute就知道了 /// </summary> public class MyCustomerFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write("方法執行以前"); base.OnActionExecuting(filterContext); } public override void OnActionExecuted(ActionExecutedContext filterContext) { filterContext.HttpContext.Response.Write("方法執行以後"); base.OnActionExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write("視圖加載以前"); base.OnResultExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write("視圖加載以後"); base.OnResultExecuted(filterContext); } } }
爲何我會這麼寫?由於你在ActionFilterAttribute上按下F12,查看源代碼就知道了,以下
#region 程序集 System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 // 未知的位置 // Decompiled with ICSharpCode.Decompiler 4.0.0.4285 #endregion namespace System.Web.Mvc { // // 摘要: // Represents the base class for filter attributes. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter { // // 摘要: // Called by the ASP.NET MVC framework before the action method executes. // // 參數: // filterContext: // The filter context. public virtual void OnActionExecuting(ActionExecutingContext filterContext) { } // // 摘要: // Called by the ASP.NET MVC framework after the action method executes. // // 參數: // filterContext: // The filter context. public virtual void OnActionExecuted(ActionExecutedContext filterContext) { } // // 摘要: // Called by the ASP.NET MVC framework before the action result executes. // // 參數: // filterContext: // The filter context. public virtual void OnResultExecuting(ResultExecutingContext filterContext) { } // // 摘要: // Called by the ASP.NET MVC framework after the action result executes. // // 參數: // filterContext: // The filter context. public virtual void OnResultExecuted(ResultExecutedContext filterContext) { } } }
裏面說的很明白了,方法執行先後,和結果執行先後
由於過濾器是AOC面向切面編程的產物,因此使用很方便,有三種方式,不過最經常使用的仍是在方法上加,其餘兩個一個是在Controller上加,一個是在全局過濾器加,不講。
[MyCustomerFilter] public ActionResult Index() { string test = string.Format("{0:P0}", 0.24583); return View(); }
就是這麼簡單,Index這個方法已經加上過濾器了,
這裏講一下OutputCache這個緩存
我在Index方法上加上OutputCache緩存,以下
[OutputCache(Duration =5)] public ActionResult Index() { ViewBag.time = DateTime.Now.ToString(); Response.Cache.SetOmitVaryStar(true); return View(); }
Duration這個屬性是時間,就是緩存幾秒的意思,屬性有不少,不寫,去網上查
運行,發現,網絡是200,而後我刷新一次,發現是304,以下圖
這個304就是從緩存讀取的意思,由於我設置了Duration緩存時間爲5秒,因此,你在5秒內刷新都是304,過了5秒就又變成200了
SEO,搜索引擎優化。作網站最看重的就是SEO了吧,這關乎到你的網站在搜索引擎中的權重,SEO作的好,就會出如今搜索引擎的前幾名,對網站的流量和知名度影響很大
JavaScript動態生成的HTML,蜘蛛是不會爬取的,蜘蛛就是各大搜索引擎獲取網站信息的工具,說到這裏我想到了一點,Bootstrap Table這個插件是在js裏面生成表格的,這樣蜘蛛就不會爬取,SEO會變差
由於搜索引擎蜘蛛只認a標籤,不認JavaScript,例如
<a href="javascript:document.location='www.baidu.com'">百度</a>
這個搜索引擎的蜘蛛是不認的,而LinkButton就是這個
WebService是什麼暫且不細說,先看看怎麼新建最簡單的WebService
直接右鍵新建空的EntityFramework網站,沒什麼好說的
這個新建Web服務便可,如圖
個人命名是MyWebService,雙擊打開能夠看到裏面已經有了一個HelloWorld方法
如今咱們來新加一個方法,以下:
[WebMethod] public int Multiplier(int a,int b) { return a * b; }
直接在MyWebService右鍵,在瀏覽器中運行,如圖
超經典的圖面了,這就是WebService了,你點擊Multiplier,還能夠直接在線測試方法
首先,項目先引用WebService,直接在引用那裏,右鍵添加服務引用,選擇咱們剛纔的WebService地址便可,注意,這裏引用的地址不要和WebService重名了
而後新建一個Razor頁面來測試吧,單純的HTML沒辦法寫C#,仍是Razor好用
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title></title> </head> <body> <div> @{ WebService.ServiceReference.MyWebServiceSoapClient client = new WebService.ServiceReference.MyWebServiceSoapClient(); <text>@client.Multiplier(5,6)</text> } </div> </body> </html>
直接右鍵運行這個Razor,能夠直接看到30,至此,最簡單的WebService建立和調用完成了
這個有點坑啊,本地的WebService是直接添加服務引用,網絡上的不同,須要點擊添加服務引用,點擊高級=>添加Web引用才能夠,以下圖,網絡的WebService帶有Web
咱們這裏以天氣服務助手爲例,網上搜一下天氣服務助手,能夠搜到這個
http://www.webxml.com.cn/WebServices/WeatherWS.asmx
你能夠點着看看各個接口是幹嗎的,我不介紹了,直接調用,仍是在我很喜歡的Razor裏面
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title></title> </head> <body> <div> @{ WebService.WeatherService.WeatherWS weatherClient = new WebService.WeatherService.WeatherWS(); string[] text = weatherClient.getWeather("深圳", ""); foreach (var item in text) { <text> @item </text> } } </div> </body> </html>
不知道發現了沒,這個getWeather接口也是坑,明明兩個參數,一個是城市名,一個是城市的編號ID,可是輸入ID卻沒有身份驗證....因此上面的幾個接口都是獲取Id的有什麼用???我直接輸入城市名就獲取了.....
看看結果
確定不能寫死數據了,因此從枚舉獲取值最好
<select class="form-control input-sm" id="adjustStatus"> <option value="-1">調整狀態</option> @foreach (var item in Enum.GetValues(typeof(ProductSaleStatus))) { <option value="@item.GetHashCode()">@item</option> } </select>
$('#adjustStatus').on("click", function () { if ($('#adjustStatus').val() == -1) { return } id = []; $('input[name="checkbox"]:checked').each(function () { if ($(this).prop("checked")) { id.push($(this).val()); } }) if (id.length > 0) { var ids = id.join(","); $.ajax({ type: "post", url: "/ProductSale/AdjustSaleStatus", data: { ids: ids, status: $('#adjustStatus').val() }, success: function (data) { if (data.Status == 1) { layer.alert(data.Message, { title: '提示', icon: 1, time: 10000}); # 刷新頁面 window.location.reload() } }, error: Common.Public.Error }); } else { layer.alert("請最少選擇一條銷售記錄", { title: '提示', icon: 2, time: 5000, anim: 6 }); } # 最後,把個人下拉框恢復原樣,否則點一次又執行了 $('#adjustStatus').val(-1) });
還可使用更好的方法,以下
//這個是HTML <td> <select class="adjustStatus"> @foreach (var item in Enum.GetValues(typeof(ChatMessageStatus))) { <option value="@item.GetHashCode()" @if ((int)chatMessage.Status == item.GetHashCode()) { <text> selected</text> }>@item</option> } </select> </td> //這個是js $('.table tbody').on('change', 'tr .adjustStatus', function () { $.ajax({ type: "post", url: "/Messenger/AdjustStatus", data: { id: $(this).parents("td").attr("id"), status: $(this).val() }, success: function (data) { if (data.Result == "succeed") { window.location.reload() } }, }); });
通常來講,枚舉的值在數據庫中都是一個int類型的字段,裏面存儲0123這樣的數字
而後枚舉是相似這樣的,通常都在類上面寫了
public enum People { 許嵩 = 0, 蜀雲泉 = 1 }
添加一個枚舉類型的字段,給個默認值
public People people { get; set; } = People.許嵩;
前端頁面使用的時候,直接調用,由於字段自己就是枚舉的,因此顯示的時候顯示的就是中文
<td>@chatMessage.people</td>
使用了Bootstrap的一個table插件,發現點擊下一頁以後,js不起做用了
例如我下面的表格裏面的一個class是people,我直接使用.people不行,下一頁js根本不生效,換成下面那種就能夠了
//$('.people').on("click", function () { //將上面的換成下面的,就能夠了 $('.table tbody').on('click', 'tr .people' , function () {
if (HttpContext.Current.Request.UrlReferrer != null) { rurl = HttpContext.Current.Request.UrlReferrer.AbsoluteUri; }
代碼裏面寫的是bool,我就很奇怪,我還得再寫一個返回int類型的方法?
可是我萬萬沒想到, customer.CustomerId = id;這一行代碼就已經返回了id了
public bool AddCustomer(Customer customer) { string strSql = @"insert into Customer(company,contact) values (@company,@contact);Select @@Identity"; DbParameter[] cmdParms = { _helper.CreateInDbParameter("company", DbType.String,customer.Company), _helper.CreateInDbParameter("contact", DbType.String,customer.Contact)}; int id = _helper.GetInt(_helper.ExecuteScalar(CommandType.Text, strSql.ToString(), cmdParms)); customer.CustomerId = id; return id > 0; }
在使用push.js進行通知提醒的時候,使用本地的IIS部署的項目沒法測試,由於push.js只能在https這樣的請求頭下工做,因此新加一個IIS部署爲https便可
選擇網站,點擊右側的綁定,出來一個網站綁定,選擇https便可,如圖
選擇https,輸入本機ip,默認443端口,最下面的證書能夠選擇IIS
而後訪問網址直接輸入https://192.168......便可
只須要把代碼改成如下
return new JsonResult() { Data = new { total, rows }, MaxJsonLength = int.MaxValue, ContentType = "application/json", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
使用KeyName真的是太必要了,有的Name裏面是有奇怪的字符的,顯示效果太差,因此須要KeyName
public static string GetKeyName(this string name) { if (!string.IsNullOrWhiteSpace(name)) { return name.Trim().Replace("~", "-").Replace("`", "-").Replace("!", "-").Replace("@", "-") .Replace("#", "-").Replace("$", "-").Replace("%", "-").Replace("^", "-").Replace("&", "-").Replace("*", "-") .Replace(" ", "-").Replace("(", "-").Replace(")", "-").Replace("+", "-").Replace("®", "-").Replace("™", "-") .Replace("=", "-").Replace(",", "-").Replace(".", "-").Replace("<", "-").Replace(">", "-").Replace("’", "-").Replace(",", "-").Replace("±", "-").Replace("[", "-").Replace("]", "-") .Replace("?", "-").Replace("/", "-").Replace("\\", "-").Replace(";", "-").Replace(":", "-").Replace("–", "-").Replace("ω", "-").Replace("{", "-").Replace("}", "-") .Replace("'", "-").Replace("\"", "-").Replace("「", "-").Replace("」", "-").Replace("|", "-").Replace("_", "-").Replace("---", "-").Replace("--", "-").ToLower().Trim(); } return ""; }