---- IT民工 第10xx01号“名言”
在今天你抛弃了ASP.NET了吗?问题篇中,我被各位“猛士们”骂了个狗血淋头,感到鸭梨很大。既然已经里外不是人,我就爽快来个玉石俱焚,鱼死网破吧!这篇文章,我通过分析现有的一些前端开发技术,显示一种ASP.NET下的快速开发模型——银蛋(SILVER EGG)!
ASP.NET中的银蛋
------------------------------------------------------------------------
asp.net就是一头大象,为了让asp.net敏捷起来,无数的烈士们不断的去寻找着各种类型母象(ASP.NET AJAX, ASP.NET MVC, Nvelocity...),可是这头大象就是提不起精神。既然如此难伺候,那我何不请只小老鼠出来试试?
我先展示一下快速开发模型的代码和实现,然后介绍我的思考过程和资料收集过程。
1. 我的小老鼠-- JsonPage
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Globalization;
using System.Data;
namespace AspNetSilverBullet
{
public class JsonPage<T> : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
// if Non-json request, directly return.
if (Request.QueryString.Count == 0) { base.OnLoad(e); return; }
// get method name
string methodname = Request.QueryString["method"];
// reflect method info.
MethodInfo method = typeof(T).GetMethod(methodname);
// construct method call arguments
List<object> arguments = new List<object>();
foreach (ParameterInfo parameterType in method.GetParameters())
{
arguments.Add(EnsureType(parameterType.ParameterType, Request.QueryString[parameterType.Name]));
}
// reflect object instance
object targetObject = typeof(T).GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
// invoke method result
object result = method.Invoke(targetObject, arguments.ToArray());
// response
Response.Clear(); Response.Write(ObjectToJson(result)); Response.Flush(); Response.End();
}
private object EnsureType(Type targetType, string argument)
{
if (targetType.Equals(typeof(string))) return argument;
return TypeDescriptor.GetConverter(targetType).ConvertFrom(null, CultureInfo.InvariantCulture, argument);
}
private string ObjectToJson(object result)
{
if (result == null) return "0";
if (result.GetType().IsValueType) return result.ToString();
if (result.GetType().Equals(typeof(string))) return result.ToString();
if (result.GetType().Equals(typeof(DataTable))) return DataTableToJson(result as DataTable);
return result.ToString();// TODO use Newtonsoft to serialize object to json.
}
private string DataTableToJson(DataTable table)
{
StringBuilder builder = new StringBuilder();
builder.Append("[");
foreach (DataRow row in table.Rows)
{
builder.Append("{");
foreach (DataColumn column in table.Columns)
{
builder.AppendFormat("\"{0}\"", column.ColumnName.Trim().ToUpper()); builder.AppendFormat(":", new object[0]);
if (column.DataType.IsValueType) { builder.Append(row[column]); } else { builder.Append("\"").Append(row[column]).Append("\""); } builder.Append(",");
}
builder.Remove(builder.Length - 1, 1); builder.Append("},");
}
if (builder.Length > 1) { builder.Remove(builder.Length - 1, 1); } builder.Append("]");
return builder.ToString();
}
}
}
这是个继承了Page的类,负责分析用户请求,转化为JSon请求,然后返回客户端。
2. Default.aspx + Default.aspx.cs 微软的大象
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Asp.net Silver Bullet</title>
<script type="text/javascript" src="js/jquery-1.2.3.min.js"></script>
<script type="text/javascript" src="js/ejs.04.js"></script>
<script type="text/javascript" src="js/parseuri.js"></script>
<script type="text/javascript" src="js/getfunctionname.js"></script>
<script type="text/javascript"> // jquery extend by pixysoft, http://www.cnblogs.com/zc22
function invoke(param,callback)
{
var method = getFnName(invoke.caller);
$.get(parseUri(window.location).file+"?method="+method, param, callback);
}
</script>
<script type="text/javascript"> // jquery ajax
$(document).ready(function() // register event
{
$("#Button1").bind("click", Caculate);
});
function Caculate() // invoke server side.
{
invoke({ a: $("#Text_a").val(), b: $("#Text_b").val() }, function(responseText,textStatus)
{
$("#Text1").val(responseText);
});
}
</script>
</head>
<body>
<div>
<b>Asp.net + Jquery Example:</b><br />
a:<input id="Text_a" style=" 52px" type="text" />
b:<input id="Text_b" style=" 52px" type="text" />
Result:<input id="Text1" type="text" /><input id="Button1" type="button" value="Invoke Value" style=" 104px" />
</div>
</body>
</html>
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using AspNetSilverBullet;
public partial class _Default : JsonPage<DefaultController>
{
}
3. 让大象跳舞吧!
using System.Data;
using System.Configuration;
public class DefaultController
{
public int Caculate(int a, int b)
{
return a + b;
}
}
4. 大象的舞姿!
5. 银蛋下载:
http://www.boxcn.net/shared/4t3qpevyap
银蛋的 WHAT? HOW? WHY?
------------------------------------------------------------------------
1. 什么是JsonPage, 为什么继承Page?
JsonPage就是一个HttpHandler,通过拦截页面的ajax回调请求,反射逻辑方法,然后返回json结果。
同时JsonPage使用了泛型参数作为了逻辑方法的注册机制,极大简化了开发。
首先,我必须使用ASP.NET,否则所有c#代码都会报废。
我曾经思考过HttpHandler,虽然实现一个Httphandler,然后在web.config里面注册,貌似整个架构变得很干净。但是,在实际使用中,用户对服务端发出的URL请求如何被映射到我的逻辑类里面呢?
如果用spring的机制,另写一个xml做映射,这简直就是自掘坟墓。一个action写一个xml,那么一个网站不就100+条的配置了?而且一旦修改起来,很快就会崩溃,接下来就需要再写一个xml配置器了;之后xml配置器变得庞大功能越来越多,就希望集成到VS里面了。。。然后就是自己不断给自己挖坑,还被蒙的理所当然。
ASP.NET使用了一些trick,在aspx文件头有个<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>, 这是啥?这个就是个spring的动态加载!微软把用户请求路由到了磁盘的aspx文件,然后读取了头声明,动态加载了逻辑类(Codebehind),然后输出的时候再过滤掉头声明。
多么的优雅又巧妙的设计!这个是Java servlet学不来的!而且Page最终会被动态编译为一个HttpHandler。
既然微软提供了如此优雅的架构,我何不顺手拈来!
所以,最终我使用了一个继承JsonPage的类作为拦截器。即利用了微软aspx的优势,同时又嵌入了自己
2. 不使用Web Control, 页面如何回调服务端代码?
这部分就是微软的ASP.NET大象,也是让我最头疼的地方。最终:
前端我使用了Jquery作为页面的核心模块;
使用了来自博客园伟大的司徒正美的Javasctipt Template(EJS)作为前端模板
再引入了一些技巧性的Javascipt,例如获取当前页面请求的URl、当前JS调用的函数名等,简化JS代码。因此我们调用一个服务端代码仅仅需要:
注册js事件:
声明js事件内容并回调 invoke(param, callback):
代码
{
invoke({ a: $("#Text_a").val(), b: $("#Text_b").val() }, function(responseText,textStatus)
{
$("#Text1").val(responseText);
});
}
通过一些js技巧,前端的js函数直接调用了服务端同名的类方法,简化了开发流程。
之前花了一个多星期搜索前端开发的技术,看了EXTJS,那500k+的体积让我顿时害怕。看了Mootools/Prototype/YUI等等,最终选择了Jquery,原因只有一个,很多人在用。多人用,就意味着很多人会在JQuery有贡献有积累,有沟通。我们快速开发网站也变得更加容易(copy+Paste嘛)。
可是Jquery仅仅实现了一个选择器的功能,涉及到了和服务器互通又遇到了困难。网络有人介绍使用web service / WCF / HttpHandler等和Jquery通讯,可是我实在没有兴趣。一个简单的http调用干嘛又要引入一堆的技术架构?而且使用了web service等,项目开发部署又变得复杂了,肯定又被PHP们嘲笑了。所以我就十两挑千斤,回归Page。
使用原有的aspx技术,如何去控制页面逻辑呢?例如最简单的GridView、Repeater之类的。于是看了Php/Cocoon/NVelocity/ASP.net MVC。但是都让我很失望。无意中搜索到了JQuery Template,顿时眼前一亮。我搜索了JavaScript Micro-Templating,JTS(Javascript Template Syntax), 微软的JQuery提议,甚至是让我非常激动的PURE,可是,他们离完美就是这么的差一点,So Close...。最后,我来到了博客园的司徒正美,我知道我找到了。首先不需要写各种奇怪的模板标记({{ }} ...)其次用JS就完美演绎了页面模板编程,这让前端的技术学习曲线下降了很多。使用司徒的模板,我们仅仅需要:
<ul>
<# for(var i=0; i< json.length; i++){ #>
<li><#= json[i].COLUMN #></li>
<# } #>
</ul>
</script>
多么的优雅啊。
后续
------------------------------------------------------------------------
曾经,作为一名asp.net的我,总是发现google的前端技术, fackbook, kaixin001, digg等等他们的前端技术离我很远。看着他们很炫的JS效果,丰富的plugin,我总是不知所措。我真的很想用,但是一打开VS20xx,打开了aspx就不知所措,仿佛他们是来自另外一个世界的。
现在,我总算尝试走出了一步。
用了本文的思路,业务逻辑可以完全打包进入一个类库DLL,前端也完全可以脱离了asp.net的代码,直接用JS写各种逻辑。上一篇怨文中各种问题都不再出现。
而且,我并没有改变ASPX的开发习惯,页面还是那个页面,c#还是那个c#。如果有人觉得不爽,想用web control, 用<%%>服务端标记也没有任何问题。
这,还不能作为一个银弹吗?