HTML5、CSS3、ASP.NET MVC4 WebAPI大锅烩
今日闲来无事,把玩了下传说中的HTML5、CSS3、ASP.NET MVC4,以及随之发布的WebAPI。不得不说,新鲜的知识点太多了,不可能按部就班地去学。参照网上的几篇文章,边看边做,搭建了一个简单的demo,现将一些要点记录下来。
首先从一个介绍CSS3的国外站点down了一个登录页(HTML5+CSS3构建同页面表单间的动画切换),做了简单修改后就成了下面这个样子。
页面中没有用到任何图片,阴影是CSS3新加的渲染标记,文本框中的图标乃是一种字体,这里还用到CSS中伪类的东东。比如用户编号和对应文本框的html:
1 <label for="username" data-icon="u">用户编号</label> 2 <input id="username" name="userCode" required="required" type="text" />
生成图标的CSS:
1 [data-icon]:after { 2 content: attr(data-icon); 3 font-family: 'FontomasCustomRegular'; 4 color: rgb(106, 159, 171); 5 position: absolute; 6 left: 10px; 7 top: 35px; 8 width: 30px; 9 }
其中'FontomasCustomRegular'就是图标字体。
注意,如果将上面代码中的label设为不可见,那么相应的伪类元素也将不可见,因为伪类元素其实仍是目标元素(这里是label)的子元素(内容);input 不支持伪类元素,因为input无法容纳其他元素。
难点在于怎么样让登录区域垂直居中。要实现垂直居中,以前除了写JS,没有其它更好的办法(还可参考登陆页面怎么让DIV垂直居中,还要兼容)。CSS3带来了flexbox的概念,能让其中的子元素灵活地排版布局。这里我们定义外围容器的样式,让它作为flexbox,登录区块就是它的子元素。
1 #container { 2 width: 100%; 3 height: 100%; 4 position: absolute; 5 left: 0; 6 top: 0; 7 background: url("bg.jpg"); 8 display: -webkit-box; 9 -webkit-box-align: center; 10 display: -moz-box; 11 -moz-box-align: center; 12 -moz-box-pack: center; 13 display: -o-box; 14 -o-box-align: center; 15 display: -ms-box; 16 -ms-box-align: center; 17 display: -ms-flexbox; 18 -ms-flex-align: center; 19 }
为了实现垂直居中,我查阅了一些资料,当时就有点怒了。本质同样的东西,不同厂商非得搞一套自己的命名规则。上述代码在IE与Chrome中工作预期,但在Firefox中则没有成功,原因未知。关于flexbox的详细讲解请看深入了解 Flexbox 伸缩盒模型,需要注意的是现行的一些概念并非所有浏览器都支持,CSS3距离统一标准还有很长的路要走。
验证通过后转到主界面,我们用localStorage.setItem("UserID", "@Model.ID");保存登录用户的ID。
注意顶部bar,退出链接靠右显示。在以前,我们用“float:right”就搞定了,但这里没起作用,推测由于这个bar是flexbox。后来我将退出链接设置成“margin-left:auto”,解决。左侧菜单我用WebAPI获取数据。
1 public class AccountController : ApiController 2 { 3 /// <summary> 4 /// 根据用户ID获取有权限的功能模块 5 /// </summary> 6 public IEnumerable<ModuleTreeItem> GetUserModules(int uid) 7 { 8 var modules = UserLogic.ModuleProcessOfUser(uid); 9 return RoleVM.ChangeSysModuleToTreeItem(modules); 10 } 11 }
关于获取数据和构造树的细节和本主题无关,略之。按照WebApi(这里指的就是Asp.Net MVC4 WebApi,下同)的默认约定,现在可以用api/Account?uid=1(不能直接api/Account/1,因为参数名如果不是路由规则默认的话,需要显式指定)请求该资源了。(读者:啥,你说资源?GetUserModules这个action哪去了?)WebApi遵照Restful风格,在Rest中,任何对服务的请求都是针对资源的,请求类型包括增删改查。这里Account就是资源,当服务端接收到Get请求时,会查找以“Get”开头的action并匹配参数,这里就匹配到了GetUserModules,POST\DELETE\PUT的路由方式同理。如果有多个Get开头的不同名方法,但参数一样,客户端就会接受到下面这个信息:
这对我这类习惯了一个类里面N多方法的人来说,相当不习惯。我想既然Rest没有action这个概念,那么我将所有的action都转为controller的形式,作为资源暴露出去,不过这种方式也同样别扭。于是我增加了一个路由规则:
1 config.Routes.MapHttpRoute( 2 name: "ActionApi", 3 routeTemplate: "api/{controller}/{action}/{id}", 4 defaults: new { id = RouteParameter.Optional } 5 );
后记:在ASP.NET MVC4中,web api默认就支持api/{controller}/{action}模式,不需要显式增加该路由规则。
虽然违背了rest的原则,但我想凡事不能照本宣科,新的路由规则既然不会带来什么坏处,为什么不用呢?此时咱们就可以用api/Account/GetUserModules?uid=1来请求了。接着我轻快地敲出JS调用代码。
1 var requestData = JSON.stringify({ uid: localStorage.getItem("UserID") }); 2 $.ajax({ 3 url: '/api/Account/GetUserModules', 4 data: requestData, 5 type: "post", 6 dataType: "json", 7 contentType: "application/json; charset=utf8", 8 success: function (data) { 9 var inline = new kendo.data.HierarchicalDataSource( 10 { 11 data: data, 12 schema: { 13 model: { 14 children: "Children" 15 } 16 } 17 }); 18 $("#treeview").kendoTreeView({ 19 dataSource: inline, 20 dataTextField: ["Module.Name"] 21 }); 22 23 } 24 });
运行时出现
405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)
I know,action默认只接受Get请求,so我加上[AcceptVerbsAttribute("GET","POST")],重新请求。
404的意思是找不到请求的资源,查看详细。
这种情况估计是参数没传到服务端。按照我以往的经验,应该不会有这种问题。于是我从网上down了几个大牛的demo,发现皆出现404错误,但是看后面的评论,似乎没有童鞋提出该问题,于是哥纠结了。考虑到前段时间微软发布的vs更新包,难道是版本更新导致传参方式变动?正打算去找一下官方的更新文档,一个特性FromBody映入眼帘(asp.net webapi下json传值方式)。我抱着试试看的心情,尝试了下。
public IEnumerable<ModuleTreeItem> GetUserModules([FromBody] int uid),运行。
看到这里,是不是有种想撞墙的赶脚?400,错误请求。查看详细信息:{"Message":"请求无效。","MessageDetail":"对于“WebHabilimentERP.API.AccountController”中方法“System.Collections.Generic.IEnumerable`1[SysProcessViewModel.ModuleTreeItem] GetUserModules(Int32)”的不可以为 null 的类型“System.Int32”的参数“uid”,参数字典包含一个 null 项。可选参数必须为引用类型、可以为 null 的类型或声明为可选参数。"}。
经过一番google、bing,甚至百度都上场了,终于找到一篇稍微有用的文章:ASP.NET WebAPI throw 404 if method parameter is string or int。按照文中方法尝试,无果。不过它倒提醒我,是不是不用传参数名,既将data: { uid: localStorage.getItem("UserID") }改为data: localStorage.getItem("UserID"),成功。
最终代码如下:
服务端
1 [AcceptVerbsAttribute("GET","POST")] 2 public IEnumerable<ModuleTreeItem> GetUserModules([FromBody] int uid) 3 { 4 var modules = UserLogic.ModuleProcessOfUser(uid); 5 return RoleVM.ChangeSysModuleToTreeItem(modules); 6 }
客户端
1 $.ajax({ 2 url: '/api/Account/GetUserModules', 3 data: localStorage.getItem("UserID"), 4 type: "post", 5 dataType: "json", 6 contentType: "application/json; charset=utf8", 7 success: function (data) { 8 var inline = new kendo.data.HierarchicalDataSource( 9 { 10 data: data, 11 schema: { 12 model: { 13 children: "Children" 14 } 15 } 16 }); 17 $("#treeview").kendoTreeView({ 18 dataSource: inline, 19 dataTextField: ["Module.Name"] 20 }); 21 22 } 23 });
敲门(非入门)完毕。
关于Post参数值传不到后台的补充连接:Web.API and jQuery JSON Post- null value?
转载请注明出处:http://www.cnblogs.com/newton/archive/2013/04/25/3043615.html