今日,在 Codeplex 上看到一个开源项目,对 Http 协议进行了封装,这下可以方便这些在 .NET 平台下访问 Web 服务器的同学们了,比如,从 Web 服务器抓取一个页面,使用 .NET 而不是借助浏览器向服务器发一个 Post 请求之类的操作,就可以直接调用一下实现好的方法了。
项目的地址:http://httplib.codeplex.com/
也可以直接到下载页面下载你需要的源代码或者编译完成的类库。
下载页面的地址:http://httplib.codeplex.com/releases/view/99620
项目比较新,所以下载量看来并不大。
项目的使用说明:
在项目的首页上有一个简短的说明,虽然简单,但是比较使用。
Project 说明
HttpLib 使得在 C# 中异步地访问 Web 服务器上的数据变得更加容易。 库中包含了将文件上传到服务器中以及从服务器下载页面的方法。
这个项目设计用来访问当前已经存在的 Web 服务。如果你计划创建新的包含服务器端和客户端的应用,建议考虑使用 WCF.
支持的 HTTP 方法
- GET
- POST
- Form Encoded
- Multipart File Upload
- PUT
- HEAD
- DELETE
示例
首先,在项目中引用 httplib 程序集。
在代码的开头部门,一般先 using 命名空间。
从服务器端获取内容。比如抓取页面。
result=>
{
Console.Write(result);
});
向服务器提交信息,使用 Post 方式。提交的参数使用一个对象表示。
result=>
{
Console.Write(result);
});
以 Post 方式提交信息,如果出现异常,捕获异常。
result=>
{
Console.Write(result);
},
e=>
{
Console.Write(e.ToString());
});
上传文件到服务器,第三个参数是上传文件。
new NamedFileStream("file", "photo.jpg", "image/jpeg", new FileStream(@"C:\photo.jpg",FileMode.Open))
},
result=>
{
Console.Write(result);
});
本文链接
在实际项目管理系统应用中,需要根据用户的不同角色、权限,来控制甘特图上可编辑的内容。
普加甘特图权可根据权限,精确控制单元格编辑、条形图拖拽、右键菜单功能项等功能显示和操作细节。
1.单元格编辑控制
监听"cellbeginedit"事件,在编辑前,判断用户是否可以编辑此单元格,不满足,就取消操作。
//控制单元格是否可编辑gantt.on("cellbeginedit", function (e) {
var task = e.record, column = e.column, field = e.field;
if (task.Summary == 1) {
e.cancel = true;
}
if (field == 'Duration') {
e.cancel = true;
}
});
本代码控制摘要任务、工期列不能编辑。
2.条形图拖拽控制
监听"itemdragstart"事件,在拖拽前,判断用户是否可修改任务日期,不满足,就取消操作。
//只允许调整百分比gantt.on("itemdragstart", function (e) {
if (e.action == "finish" || e.action == "move") { //percentcomplete
e.cancel = true;
}
});
本代码控制用户不能拖拽开始、不能移动条形图。
3.右键菜单项显示控制
监听菜单的"beforeopen"事件,如果当前点击的任务跟用户操作权限不匹配,则隐藏、禁用一些菜单项。
//右键菜单var menu = new GanttMenu();
gantt.setContextMenu(menu);
//监听菜单的opening事件,此事件在菜单显示前激发。可以控制菜单项的显示和可操作。
menu.on("beforeopen", function (e) {
var gantt = this.owner; //PlusProject对象
var task = gantt.getSelected();
if (!task) {
e.cancel = true;
return;
}
//显示和可编辑所有菜单项
this.add.show();
this.edit.show();
this.remove.show();
this.upgrade.enable();
this.downgrade.enable();
if (task.Summary) {
this.edit.hide();
this.remove.hide();
this.upgrade.disable();
this.downgrade.disable();
} else {
this.add.hide();
}
});
访问示例请点击这里。
本文链接
问题描述
假设我有一个应用场景:Core Framework可以用于任何区域的站点,其中的CustomersController有个取customer的fullname的方法GetFullName(),可想而知,这个api在中国和美国的站点上,应该得到不同的返回值。如下图所示:
这样的设计可以带来两个好处:
1、利用了OO的思想,可以封装各个区域customer service相关的一些公共逻辑
2、使得client端可以一致的接口访问服务,如:http://hostname/api/customers
这看上去不错,但是为了达到我的目的,就必须让web api支持在不同的namespace(或者area)中,存在相同名称的controller。但是web api默认情况下是不支持的,那么是否可以通过某种方法,使web api支持这种效果呢?答案是肯定的。
查找原因
为了让web api支持namespace(或者area),就必须找到为什么默认情况下web api不支持,在这个过程中,也许能找到切入点。为了能找到原因,我做了如下工作:
1、Server-Side Handlers
从mvc4官网,找到了server端的request处理过程,如下图所示:
从图中我们可以看到,controller是通过HttpControllerDispatcher调度器,来处理的。
2、HttpControllerDispatcher
HttpControllerDispatcher 位于System.Web.Http.Dispatcher命名空间中,其源代码中有一个私有属性:
private IHttpControllerSelector ControllerSelector{
get
{
if (this._controllerSelector == null)
{
this._controllerSelector = this._configuration.Services.GetHttpControllerSelector();
}
return this._controllerSelector;
}
}
从代码中可以看出,该属性,只是简单的从services容器中,得到IHttpControllerSelector类型的一个对象,所以问题现在转移到在这个controllerselector对象上。
3、DefaultHttpControllerSelector
在this._configuration.Services.GetHttpControllerSelector();这条语句中,_configuration其实就是System.Web.Http.HttpConfiguration,在其构造函数中,可以看到:
this.Services = new DefaultServices(this);DefaultServices为ServicesContainer的一个子类,所以可以称之为服务容器,定义在System.Web.Http.Services命名空间下,在其构造函数中,有如下代码:
......this.SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator());
this.SetSingle<IHttpControllerSelector>(new DefaultHttpControllerSelector(configuration));
this.SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());
......
从红色部分这正是我所需要的找的controllerselector。
4、问题所在
从第2步中,如果通过源代码,可以发现:HttpControllerDispatcher 在处理request时,需要通过HttpControllerDescriptor对象的CreateController方法,才能最终实例化一个ApiController。而HttpControllerDescriptor是通过IHttpControllerSelector(默认就是DefaultHttpControllerSelector)的SelectController方法构造的。我进一步在DefaultHttpControllerSelector源码中,发现如下代码:
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache(){
ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
HashSet<string> set = new HashSet<string>();
foreach (KeyValuePair<string, ILookup<string, Type>> pair in this._controllerTypeCache.Cache)
{
string key = pair.Key;
foreach (IGrouping<string, Type> grouping in pair.Value)
{
foreach (Type type in grouping)
{
if (dictionary.Keys.Contains(key))
{
set.Add(key);
break;
}
dictionary.TryAdd(key, new HttpControllerDescriptor(this._configuration, key, type));
}
}
}
foreach (string str2 in set)
{
HttpControllerDescriptor descriptor;
dictionary.TryRemove(str2, out descriptor);
}
return dictionary;
}
和HttpControllerTypeCache这样一个cache辅助类里的:
private Dictionary<string, ILookup<string, Type>> InitializeCache(){
IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver();
return this._configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(assembliesResolver).
GroupBy<Type, string>(t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length), StringComparer.OrdinalIgnoreCase).
ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(g => g.Key,
g => g.ToLookup<Type, string>(t => (t.Namespace ?? string.Empty), StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase);
}
这就是问题所在,导致在同一个assembly中,不能有两个相同名字的api controller。否则就会执行:
ICollection<Type> controllerTypes = this._controllerTypeCache.GetControllerTypes(controllerName);if (controllerTypes.Count == 0)
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, System.Web.Http.Error.Format(SRResources.ResourceNotFound, new object[] { request.RequestUri }), System.Web.Http.Error.Format(SRResources.DefaultControllerFactory_ControllerNameNotFound, new object[] { controllerName })));
}
throw CreateAmbiguousControllerException(request.GetRouteData().Route, controllerName, controllerTypes);
controllerTypes.Count大于0,导致抛出“Multiple types were found that match the controller named…”异常。
解决问题
到现在为止,其实解决办法已经出来了:就是不用上面的两个方法,来构造HttpControllerDispatcher。