转载注明出处
http://blog.csdn.net/xugangjava/article/details/14121801
现有项目都是基于.Net3.5的,后台以前项目全部使用的Nhibernate。Extjs前台 主要和后台ashx文件交互。
自己设计了一套简单的MVC框架,设计一个RouteHandler 类 实现 IHttpHandler IRequiresSessionState来实现路由功能。主要实现一个ashx可以实现多个方法。
为了提高性能IResuserable 返回true,所以必须进行同步处理,将HttpContext封装到一个对象里面(SyncContext后面给出),在Handler初始化的时候
会反射一次读取该类含有[Ajax]属性的方法信息保存到一个Map里面,根据页面请求的参数来执行不同的方法。
//实现方法路由功能,ashx基类
public abstract class RouteHandler : IHttpHandler, IRequiresSessionState
{
#region IHttpHandler 成员
//这里返回true,所以需要对登录用户的相关变量进行同步处理
public bool IsReusable { get { return true; } }
//类方法信息对象
public class HandlerInfo
{
public MethodInfo mehtod { private set; get; }
public AjaxAttribute attr { private set; get; }
public int paramLen { private set; get; }
public List paramInfos { private set; get; }
public HandlerInfo(MethodInfo mehtod, AjaxAttribute attr)
{
this.mehtod = mehtod;
paramInfos = mehtod.GetParameters().ToList();
paramLen = paramInfos.Count;
this.attr = attr;
}
}
protected static NLog.Logger Log
{
get { return NLog.LogManager.GetCurrentClassLogger(); }
}
private readonly IDictionary _routes;
//初始化的时候进行反射,将方法名 ,方法信息保存到一个map里面
//页面调用的时候,前台直接给出参数 /url?f=methodName 来调用相关方法
protected RouteHandler()
{
_routes = new Dictionary();
var type = GetType();
var methods = type.GetMethods();
foreach (var m in methods)
{
var attributes = m.GetCustomAttributes(false);
var ajax = (AjaxAttribute)attributes.SingleOrDefault(x => x is AjaxAttribute);
if (ajax == null) continue;
_routes[m.Name] = new HandlerInfo(m, ajax);
}
}
//为了快速开放,如果调试状态下,前台将直接弹出 错误对话框
public void Debug(string message, HttpContext context)
{
#if DEBUG
context.Response.ContentType = "text/html";
context.Response.Write("TRACE:" + message);
#endif
}
//这里其实就是一个模版模式了,直接查找 参数 f来
//调用不同的方法,Extjs前台数据 Ext.endcode 可以转换为不同的json对象
//或者基本对象,来调用函数,为了提高性能,所以只在类初始化的时候 反射一次
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
SyncContext syncContext = null;
try
{
var function = context.Request["f"];
var type = GetType();
if (null == function || !_routes.ContainsKey(function))
{
Debug(type.Name + "方法" + function + "未找到!", context);
return;
}
var handler = _routes[function];
var ajax = handler.attr;
var method = handler.mehtod;
var paramLen = handler.paramLen;
if (handler.attr.Login && context.Session[ConstKey.UserModle] == null)
{
Debug(type.Name + "方法" + function + "需要登录", context);
context.Response.StatusCode = 905;
return;
}
if (handler.attr.Login)
{
var user = (Users)context.Session[ConstKey.UserModle];
var noLoging = user == null;
var isMachine = !noLoging && user.IsMachineBind==-1 ;//&& cmpLevle!=0;
if (noLoging)
{
Debug("您无权访问该页面,需要登录", context);
return;
}
//todo other limits
}
var mParams = method.GetParameters();
var paramArray = new object[paramLen];
foreach (var mParamInfo in mParams)
{
if (mParamInfo.ParameterType == typeof(SyncContext))
{
syncContext = new SyncContext(context);
paramArray[mParamInfo.Position] = syncContext;
continue;
}
var value = context.Request[mParamInfo.Name];
if (value == null)
{
Debug(type.Name + "方法" + method.Name + "参数" + mParamInfo.Name + "缺失", context);
return;
}
if (mParamInfo.ParameterType == typeof(int))
{
paramArray[mParamInfo.Position] = int.Parse(value);
}
else if (mParamInfo.ParameterType == typeof(bool))
{
paramArray[mParamInfo.Position] = bool.Parse(value);
}
else if (mParamInfo.ParameterType == typeof(JArray) ||
mParamInfo.ParameterType == typeof(JObject))
{
paramArray[mParamInfo.Position] = JsonConvert.DeserializeObject(value);
}
else
{
paramArray[mParamInfo.Position] = value;
}
}
if (method.ReturnType != typeof(void))
{
context.Response.Write(JsonConvert.SerializeObject(method.Invoke(this, paramArray)));
}
else
{
method.Invoke(this, paramArray);
}
}
catch (Exception ex)
{
Log.Error(ex + ex.Message);
Debug(ex + ex.Message, context);
}
finally
{
if (syncContext != null) syncContext.Dispose();
context.Response.Flush();
// context.Response.End();
}
}
#endregion
}
//方法属性信息,含有该属性的方法均可为前台调用
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class AjaxAttribute : Attribute
{
public bool Login { get; set; }
public int[] Auth { get; set; }
}
//实现用户信相关变量的同步,前面IReuseable是true的,所以相关信息全部保存到该类里面
//来实现同步功能,Nhibernate的 ISession是非线程安全的,所以也与该类进行绑定
//SyncContext 包装HttpContext 通过HttpContext 来绑定ISession
public sealed class SyncContext :IDisposable
{
private readonly HttpContext _context;
public HttpContext Context { get { return _context; } }
public HttpSessionState Session { get { return _context.Session; } }
public HttpRequest Request { get { return _context.Request; } }
public HttpResponse Response { get { return _context.Response; } }
public HttpServerUtility Server { get { return _context.Server; } }
public Cache Cache { get { return _context.Cache; } }
public ISession NSession { get { return DocSessionFactory.CurrentSession(_context); } }
private ITransaction _nTransactionTran;
//事物支持
public ITransaction NBeginTransactionTran()
{
return _nTransactionTran ?? (_nTransactionTran = NSession.BeginTransaction());
}
//关闭的时候 释放 ISession
public void Dispose()
{
if (_nTransactionTran != null && !_nTransactionTran.WasCommitted)
{
_nTransactionTran.Rollback();
}
if (NSession.IsConnected) NSession.Disconnect();
}
//用户登录对象
public Users UserModel
{
get
{
return (Users)Session[ConstKey.UserModle];
}
set
{
Session[ConstKey.UserModle] = value;
}
}
public SyncContext(HttpContext context)
{
_context = context;
}
public string LoadParam(string key)
{
if (Request[key] == null) throw new Exception("以无效参数非法访问页面");
return Request[key];
}
public IDictionary LoadParam(params string[] param)
{
var d = new Dictionary();
foreach (var s in param)
{
if (Request[s] == null) throw new Exception("以无效参数非法访问页面");
d[s] = Request[s];
}
return d;
}
public bool TryLoadParam(string key, out string value)
{
value = string.Empty;
if (Request[key] == null) return false;
value = Request[key];
return true;
}
public object LoadSessionKey(string key)
{
var o = Session[key];
if (o == null)
throw new Exception("获取 Session中的" + key + "缺失!");
return o;
}
public void DropSessionKey(params string[] param)
{
foreach (var s in param.Where(s => Session[s] != null))
{
Session.Remove(s);
}
}
///
/// 判断是否只有否一用户登录的权限
///
public bool VaildateRole(params short[] roles)
{
return roles.Any(r => UserModel.Role == r);
}
}
下面是项目中基于该框架的调用示例。查询列表显示使用通用存储过程来实现效率最高。保存 修改则使用Nhibernate 简单明了。这里的参数只有int 和String,前台使用Ext.encode 后台会自动将其转换为JObject JArray,第一个方法是最常见的分页查询,第二个是业务处理方法
public class DocFileInfoHandler : RouteHandler
{
[Ajax(Login = true)]
public JObject QuerySelfFolder(int node, int start, int limit, SyncContext syncContext)
{
var id = node;
var dao = new DocFolderDao(syncContext.NSession);
var p = JPage.Load(start, limit);
p.columsForSelect = @"ID,
Name,
ISize,
SName,
LastModifyTime,
CurVersion,
SLogo,
LockerFullName,
IsDir,
FtpPath,
LockerId";
p.orderBy = "IsDir desc,CreateTime desc";
p.fromWhere = "DocumentFolderView";
p.condition = " FolderID= " + id + " AND [Status]=1 ";
DoMachineLimit(ref p.condition, syncContext.UserModel);
return dao.JPageProc(p);
}
[Ajax(Login = true)]
public JObject FileRename(int fileid, bool isDir, string newName, SyncContext syncContext)
{
var session = syncContext.NSession;
if (isDir)
{
var folder = session.Get(fileid);
if (folder == null)
{
return CallBack("文件夹不存在!");
}
var fdao = new DocFolderDao(session);
if (fdao.IsExistsSelfFolder(folder.ParentDirID, newName))
{
return CallBack("该文件夹下或回收站中,已经存在名为" + newName + "的文件夹了!");
}
folder.DirName = newName;
session.Update(folder);
session.Flush();
}
else
{
var file = session.Get(fileid);
if (file == null)
{
return CallBack("文件不存在!");
}
var ne = RawNameExt(file.FileName);
var newFileName = newName + "." + ne.Ext;
var fdao = new DocFileInfoDao(session);
if (fdao.IsExistsFileName(file.FolderId, newFileName))
{
return CallBack("该文件夹下或回收站中,已经存在名为" + newName + "的文件了!");
}
file.FileName = newFileName;
session.Update(file);
session.Flush();
}
return OK;
}
}
var f = new XG.Form.SimpelPoupForm({
title: title,
url: '/logic/DocFileInfoHandler.ashx?f=FileRename',
width: 350,
height: 150,
items: [{
fieldLabel: label,
width: 200,
emptyText: json.IsDir ? '文件夹名称' : '文件名,不包括扩展名',
name: 'newName'
}, {
name: 'fileid',
value: json.ID,
xtype: 'hidden'
}, {
name: 'isDir',
value: isDir,
xtype: 'hidden'
}],
before_submit: beforeSubmit,
success: function (action) {
var msg = f.getMessage(action);
if (msg === "OK") {
grid.getStore().reload();
}
}
});
f.show(this);
这里是我封装的一个弹出表单,/logic/DocFileInfoHandler.ashx?f=FileRename 就表示调用DocFileInfoHandler.ashx 的FileRename 方法。 name: 'newName',name: 'fileid', name: 'isDir',就自动对应后台的方法的三个变量名称。
这样就节省了大量的重复,繁杂的权限验证,参数传递,关闭数据库连接等容易遗漏出错的重复相关代码。
文中的代码只是为了阐述大概思路,所以相关类没有全部给出。
如有不对的地方,欢迎指正。