目录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 ""; }