在前面文章ExtJs与WCF之间的跨域访问已经通过服务端代理的方式解决了ExtJs与WCF跨域访问的问题,那个方案看起来并不怎么优雅,而当我在写过用Restful方式调用WCF进行上传下载后,愕然发现原来WCF支持原生数据(Raw)的返回,这就解决了ExtJs与Wcf之间进行跨域调用中的难题:返回数据必须满足格式。下面根据ExtJs与WCF之间的跨域访问中实现的项目,通过Stream和ContentType的联合使用,返回原生数据给Extjs,从而实现跨域调用。
在前面文章ExtJs与WCF之间的跨域访问已经通过服务端代理的方式解决了ExtJs与WCF跨域访问的问题,那个方案看起来并不怎么优雅,而当我在写过用Restful方式调用WCF进行上传下载后,愕然发现原来WCF支持原生数据(Raw)的返回,这就解决了ExtJs与Wcf之间进行跨域调用中的难题:返回数据必须满足<script>格式。下面根据ExtJs与WCF之间的跨域访问中实现的项目,通过Stream和ContentType的联合使用,返回原生数据给Extjs,从而实现跨域调用。
第一步:在PageGridService.svc后台代码中,添加操作契约GetProductsByPageCorssDomain,代码为:
GetProductsByPageCorssDomain方法
[OperationContract]
[WebInvoke(Method = "*", ResponseFormat = WebMessageFormat.Json,
UriTemplate = "GetProductsByPageCorssDomain?start={start}&limit={limit}&callback={callback}")]
public Stream GetProductsByPageCorssDomain(int start, int limit,string callback)
{
ProductsDataContext productDbContext = new ProductsDataContext();
IQueryable<Product> res = productDbContext.Product.Select(product => product);
PageData<Product[]> returnData = new PageData<Product[]>();
returnData.TotolRecord = res.ToArray<Product>().Length;
res = res.Skip<Product>(start);
res = res.Take<Product>(limit);
returnData.Data = res.ToArray<Product>();
System.Runtime.Serialization.Json.DataContractJsonSerializer formater = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(PageData<Product[]>));
MemoryStream ms = new MemoryStream();
formater.WriteObject(ms, returnData);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string objContent = sr.ReadToEnd();
string returnStr = callback+"("+objContent+")";
sr.Close();
ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.AutoFlush = true;
sw.Write(returnStr);
ms.Position = 0;
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
return ms;
}
第二步:在项目中创建一个新的htm页面:PageGridCorssDomainWithRow.htm,代码为:
PageGridCorssDomainWithRow.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>ExtJs+WCF+LINQ打造分页Grid</title>
<link rel="stylesheet" type="text/css" href="resources/css/ext-all.css" />
<script type="text/javascript" src="adapter/ext/ext-base.js" charset="gb2312"></script>
<script type="text/javascript" src="ext-all-debug.js" charset="gb2312"></script>
<link rel="stylesheet" type="text/css" href="shared/examples.css" />
<script type="text/javascript" src="shared/examples.js" charset="gb2312"></script>
<script type="text/javascript" src="PageGridCrossDomainWithRow.js" charset="gb2312"></script>
</head>
<body>
<h1>
ExtJs+WCF+LINQ打造分页跨域Grid</h1>
<div id="page-grid">
</div>
</body>
</html>
第三步:在项目中创建一个新的js脚本文件:PageGridCrossDomainWithRow.js
PageGridCrossDomainWithRow.js
/**//*
* Ext JS Library 2.1
* Copyright(c) 2006-2008, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
Ext.onReady(function(){
var proxy = new Ext.data.ScriptTagProxy({
url:'http://127.0.0.1:50643/PageGridService.svc/GetProductsByPageCorssDomain'
});
var reader = new Ext.data.JsonReader(
{root:'Data',totalProperty :'TotolRecord'},
[
{name: 'ProductID'},
{name: 'Name'},
{name: 'ProductNumber'},
{name: 'MakeFlag'},
{name: 'FinishedGoodsFlag'},
{name: 'Color'},
{name: 'SafetyStockLevel'},
{name: 'ReorderPoint'},
{name: 'StandardCost'},
{name: 'ListPrice'},
{name: 'Size'},
{name: 'SizeUnitMeasureCode'},
{name: 'Weight'},
{name: 'DaysToManufacture'},
{name: 'ProductLine'},
{name: 'Class'},
{name: 'Style'},
{name: 'Weight'},
{name: 'ProductSubcategoryID'},
{name: 'ProductModelID'},
{name: 'SellStartDate'},
{name: 'SellEndDate'},
{name: 'DiscontinuedDate'},
{name: 'rowguid'},
{name: 'ModifiedDate'}
]
);
var store = new Ext.data.Store(
{proxy:proxy,reader:reader}
);
// create the Grid
var grid = new Ext.grid.GridPanel({
store: store,
columns: [
{id:'ProductID',header: "编号",width:30, sortable: true, dataIndex: 'ProductID'},
{header: "名称", width:75, sortable: true, dataIndex: 'Name'},
{header: "产品编码", width:75, sortable: true, dataIndex: 'ProductNumber'},
{header: "是否标记", width:75, sortable: true, dataIndex: 'MakeFlag'},
{header: "颜色", width:50, sortable: true,dataIndex:'Color'},
{header: "数量", width:50, sortable: true,dataIndex:'ReorderPoint'},
{header: "单价", width:50, sortable: true,renderer:'userMoney',dataIndex: 'StandardCost'}
],
stripeRows: true,
autoExpandColumn: 'ProductID',
height:400,
width:600,
title:'产品信息',
viewConfig:
{
columnsText:'列',
sortAscText:'升序',
sortDescText:'降序'
},
bbar: new Ext.PagingToolbar({
pageSize: 25,
store: store,
displayInfo: true,
displayMsg: '总记录数 {0} - {1} of {2}',
emptyMsg: "没有记录"
})
});
grid.render('page-grid');
//载入
store.load({params:{start:0,limit:25}});
grid.getSelectionModel().selectFirstRow();
});
接下来,浏览PageGridCorssDomainWithRow.htm,便可以得到如下运行结果:
两种方案对比:
- 第一种方案要通过服务端WebClient访问WCF服务,然后由ExtJs访问本地代理页面,这样势必造成一些性能损失。而本文的方式,无须架设服务端代理,由ExtJs直接请求WCF,从操作复杂度上,要低一下,理论上也能因此提高一些性能
- 从实现原理上,第一种方式有些背离ExtJs的设计初衷,ExtJS强调用ScriptTagProxy跨域访问数据,但这样对数据格式有一定要求,第一种方案采用绕过拦路虎的方式,通过一个中转将跨域问题化解掉了,虽然效果达到,但毕竟没有充分利用到ExtJs的ScriptTagProxy,而且违背了WCF中的Restful访问方式。
- 上面两点都是说明第一种方案的缺点,本文方案的优点,但在现实中,考虑到可用性,还是建议用第一种方式的。本文这种方式虽然有一定优点,却大大破坏了WCF程序结构,使得WCF服务程序开发难度加大,且以后难于维护,因为一个服务,他的使命不光光针对ExtJs一方的调用,他的消费者可能很多,消费方式也不仅仅局限在Restful上,更多需求可能体现在SOAP方式上,他消费者所在平台也可能是linux,这样事情就变的复杂起来,一个返回stream的操作具有普遍性么?对于其他消费者,stream友好么?而且设置了ContentType是不是对其他消费者有致命影响呢?这些问题都是要考虑的,如果是一个面向大众的服务,考虑到上面的问题,纵是有千万种理由,这种方案还是不可取的。相比较一点点的性能,更有些得不偿失!当然具体情况要具体分析,如果是专门为ExtJs或者其他Ajax设计的,那本文方案就比较合理了。
修改后的项目示例: