[转载]Log Parser使用教程

mikel阅读(1980)

Log Parser

Log Parser 是一種輕巧好用的公用程式,名符其實,它可以快速簡單地剖析純文字記錄檔。而且 Log Parser 也可以用來快速簡單地剖析事件記錄、檔案系統、登錄,甚至是 Active Directory。
像是對IIS的log file進行類似SQL語法的分析查詢,將結果顯示於螢幕、檔案或SQL數據庫內。

Log Parser本身是個命令字元工具(command-line tool),使用時必須先進入命令提示字元,並切換至Log Parser的安裝目錄,然後輸入LogParser指令即可使用。

LogParser -h GRAMMAR : SQL Language Grammar 查詢語言的語法

LogParser -h FUNCTIONS [ ] : Functions Syntax 函數語法

LogParser -h EXAMPLES : Example queries and commands 範例

輸出入格式

-i: : one of IISW3C, NCSA, IIS, IISODBC, BIN, IISMSID, HTTPERR, URLSCAN, CSV, TSV, W3C, XML, EVT, ETW, NETMON, REG, ADS, TEXTLINE, TEXTWORD, FS, COM (if omitted, will guess from the FROM clause)

-o: : one of CSV, TSV, XML, DATAGRID, CHART, SYSLOG, NEUROVIEW, NAT, W3C, IIS, SQL, TPL, NULL (if omitted, will guess from the INTO clause)

示範:
1. 2006/4/10當日IIS網站一天的擊點率(Hit Rate)

logparser “select count(*) from ex.log”
2. 以圖形化資料網格方式輸出

logparser “SELECT count(*) as [HitRate] INTO datagird FROM ex060410.log”

或是在INTO後面改為其它檔案格式:your_output_name.csv, Report.xml, MyChart.gif

LogParser “SELECT TOP 20 cs-uri-stem, COUNT(*) AS Hits INTO MyChart.gif FROM ex060410.log GROUP BY cs-uri-stem ORDER BY Hits DESC” -chartType:Column3D -groupSize:1024×768
3. 統計每一網頁資源的擊點率

logparser “SELECT cs-uri-stem as [Web Page], count(*) as [HitRate] INTO datagird FROM ex.log group by cs-uri-stem”
4. 輸出至Html

logparser “SELECT cs-uri-stem as [Web Page], count(*) as [HitRate] INTO datagird FROM ex.log group by cs-uri-stem order by count(*) desc” -o:TPL -tep:HTML_temp.txt
5. 查看登錄器並輸出至SQL Server存成資料表

LogParser “SELECT * INTO MyTable FROM \HKLM” -i:REG -o:SQL -server:MyServer -database:MyDatabase -driver:”SQL Server” -username:TestSQLUser -password:TestSQLPassword -createTable:ON
6. 搜尋電腦中前10大檔案

LogParser “SELECT TOP 10 * FROM C:\*.* ORDER BY Size DESC” -i:FS
7. 將指令列執行結果分析再輸出,例分析netstat結果

netstat | LogParser “SELECT * FROM STDIN” -i:TSV -iSeparator:space -nSep:2 -fixedSep:off -nSkipLines:3

備註:

IIS 記錄檔預設位置C:\WINDOWS\system32\LogFiles\W3SVC1\exyymmdd.log,可由「網際網路資訊服務(IIS) 管理員」/網站/預設的網站/右鍵「內容」/「網站」Tab/「啟用記錄」的「內容」來設定。

「一般」Tab可設定儲存Log的位置及排程。

「進階」Tab可設定記錄的資訊選項,例:用戶端IP位址(c-ip)、傳送方法(cs-method) 、URI主體(cs-uri-stem) 、URI查詢(cs-uri-query)、通訊協定狀態(sc-status)、花費時間(time-taken)。

參考:
* Windows 2003 Server 電子雜誌 2005年10月份
* 如果您還不熟悉 Log Parser 2.2,請您參閱《指令碼的物語》(英文) 專欄-Tales from the Script
* Microsoft TechNet Log Parser 2.2
* 尋找免費的 Log Analyser 工具?Internet Information Services (IIS) 6.0 Resource Kit Tools

應用範例:
* LogParser -h EXAMPLES 裡面有很多呦!
* 嗨,Scripting Guy!我要如何找出電腦中前 20 大的檔案呢?

[转载]用 LogParser 將 IIS Log 匯入SQL Server 2005 - 瓶水相逢.Net- 點部落

mikel阅读(945)

[转载]用 LogParser 將 IIS Log 匯入SQL Server 2005 – 瓶水相逢.Net- 點部落.

指令:

C:\Program Files\Log Parser 2.2\LogParser.exe ” “SELECT TO_LOCALTIME(TO_TIMESTAMP([date], [time])), [s-sitename], [s-computername], [s-ip], [cs-method], [cs-uri-stem], [cs-uri-query], [s-port], [cs-username], [c-ip], [cs-version], [cs(User-Agent)], [cs(Cookie)], [cs(Referer)], [cs-host], [sc-status], [sc-substatus], [sc-win32-status], [sc-bytes], [cs-bytes], [time-taken] INTO IISLog FROM C:\WINDOWS\system32\LogFiles\W3SVC1\ex*.log WHERE TO_LOWERCASE (EXTRACT_EXTENSION(cs-uri-stem)) NOT IN (‘gif’;’jpg’;’png’;’bmp’;’ico’;’axd’)” -o:SQL -server:192.168.x.x -database:MyDB -driver:”SQL Server” -username:uname -password:passwd-createTable:OFF

藍色 的地方,利用 TO_TIMESTAMP 函數將 Date 與 Time 合併成一個 Datatime 型態的欄位。再利用 TO_LOCALTIME 函數,將 IIS Log 內的 UTC 時間,轉換成本地時間 (GMT +8)。

紅色 的地方,依照自己的設定環境加以更換。

[转载]LogParser 日期相關心得筆記與分析 IIS Logs 技巧分享

mikel阅读(1136)

[转载]LogParser 日期相關心得筆記與分析 IIS Logs 技巧分享.

最近工作上又有機會用到 LogParser 來分析 IIS 紀錄,藉此整理一下 LogParser 對時間、日期處理的各種使用情境,以及在分析 IIS Log 時的一些小技巧。

與日期時間相關的操作方法

取得系統時間 ( GMT +0000 )

SYSTEM_TIMESTAMP()

取得系統本地時間 ( 若在台灣就會回傳 GMT +0800 的時間 )

TO_LOCALTIME(SYSTEM_TIMESTAMP())

取得 GMT +1000 的時間

ADD(SYSTEM_TIMESTAMP(), TIMESTAMP('10', 'h'))

取得今天的日期時間字串 (與 .NET 的 DateTime.ToString 方法 類似)

TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'yyyy-MM-dd HH:mm:ss')

取得今天的日期字串 (與 .NET 的 DateTime.ToString 方法 類似)

TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'yyyy-MM-dd')

顯示昨天的日期時間

請注意下例的 TIMESTAMP 函示,由於所有 Timestamp 是從 0000-01-01 00:00:00 開始計算的(代表0),所以當第一個參數是傳入 2,第二個參數是 d (日期),則代表 0000-01-02 00:00:00 這個時間點,所以這裡所換算出來的 Timestamp 數值代表 1 天的時間,然後再利用 SUB 相減函示與本地時間計算後,就是昨天的時間了!

SUB(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('2', 'd'))

顯示明天的日期時間

ADD(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('2', 'd'))

取得本月第一天的開始時間

SUB(
    TO_LOCALTIME(SYSTEM_TIMESTAMP()), 
    TO_TIMESTAMP(TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'dd HH:mm:ss'), 'dd HH:mm:ss')
   )

注意事項

  • 如果你希望用「本地時間」計算日期部分,請不要用 SYSTEM_DATE(),即便你用範例所提供的 TO_LOCALTIME( SYSTEM_DATE() ) 我自己實驗的結果騎日期一樣是錯的,並非系統本地日期!

與 IIS 相關的技巧

由於 IIS Log 的日期、時間是分開兩個欄位,必須用以下語法合併

TO_TIMESTAMP([date], [time])

由於預設 IIS Log 都是以 GMT 標準時間當成 Log 的紀錄,若要轉換成本地時間可用以下語法:

TO_LOCALTIME(TO_TIMESTAMP([date], [time]))

篩選出特定時間區間的 Log 紀錄

SELECT
    *
FROM
    C:\inetpub\logs\LogFiles\W3SVC1\*.log
WHERE
    TO_TIMESTAMP([date], [time])
    BETWEEN TO_TIMESTAMP('2009-11-13 00:00:00', 'yyyy-MM-dd HH:mm:ss')
    AND     TO_TIMESTAMP('2009-11-13 23:59:59', 'yyyy-MM-dd HH:mm:ss')

讀取 IIS Log 可以不用明確指定路徑,如果要分析本機的 IIS Log 可以直接指定 [站台識別元] ( Site ID ) 或 [站台名稱] 即可載入該站台下所有 Logs  ( 可用 * 萬用字元 )

如下範例為載入 Site ID 為 1 與 2232 以及站台名稱為 VWeb 開頭的所有站台的所有 Logs

SELECT * FROM <1>, <2232>, <www.*.com.tw>

在 IIS 7 / IIS 7.5 如果要用這種簡易的語法必須要安裝 [IIS 6 Metabase 相容性] 才能使用:

網頁伺服器 (IIS)  :: 新增角色服務 :: 管理工具 :: IIS 6 管理相容性 :: IIS 6 Metabase 相容性

Windows Features: IIS Metabase and IIS 6 configuration  compatibility

[转载]IIS日志分析工具AWStats配置与应用详解(上)

mikel阅读(1255)

[转载]IIS日志分析工具AWStats配置与应用详解(上) – Chikoo’s Blog.

AWStats是sourceforge.net上很有名的Web/Mail/FTP服务器日志文件分析工具。
安装配置步骤(适用于分析IIS 日志文件)
1、下载AWStats, 下载地址:
http://sourceforge.net/projects/awstats/

2、由于AWStats是Pertl写的,所以要下载Perl 解释器, 下载地址:
http://activestate.com/Products/ActivePerl/

3、安装Perl 解释器ActivePerl
4、安装AWStats(这里假设安装在C:\Program Files), 出现命令提示时,第一次输入none, 第二次输入你的主机的域名
5、配置IIS日志
5.1 活动日志格式选用默认的“W3C扩充扩展日志文件格式”
5.2 点击“属性”,再选择“扩展属性”,选中下列项目:
date
time
c-ip
cs-username
cs-method
cs-uri-stem
cs-uri-query
sc-status
sc-bytes
cs-version
cs(User-Agent)
cs(Referer)
其他都不要选中。
6、建立虚拟目录cgi-bin,映射到C:\Program Files\AWStats\wwwroot\cgi-bin。
建立虚拟目录ico, 映射到C:\Program Files\AWStats\wwwroot\icon
7、修改C:\Program Files\AWStats\wwwroot\cgi-bin中的相应的配置文件:awstats.myvirtualhostname.conf(myvirtualhostname为 你第4步中输入的域名, 比如
www.cnblogs.com
)
。修改下列项目:

程序代码 程序代码
LogFile=”C:\WINDOWS\System32\LogFiles\W3SVC1\ex%YY-0%MM-0%DD-0.log”
‘ 以上为日志文件存储目录,请务必检查并输入正确路径,否则将会报错

LogFormat=”date time c-ip cs-username cs-method cs-uri-stem cs-uri-query sc-status sc-bytes cs-version cs(User-Agent) cs(Referer)”
‘以上各项与日志文件Fields字段相关,IIS5与II6 的记录格式存在细微差别,且AWStats新版设置与旧版设置也存在部分区别。新版无须指定字段,只需按配置脚本中的帮助说明设置”0″或”1″即可。上 述实例为IIS 5的设置方法,下述实例为IIS 6的设置方法:
LogFormat=”date time cs-method cs-uri-stem cs-uri-query cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-bytes”

AllowToUpdateStatsFromBrowser=1
‘ 以上设置为是否允许在AWStats分析结果页面中出现”更新信息”

LoadPlugin=”timezone +8″

8、 重启IIS, 删除C:\WINDOWS\System32\LogFiles\W3SVC1下的所有日志文件
9、使配置生效: awstats.pl -config=myvirtualhostname -update
10、配置完成,通过Web访问日志http://www.myserver.mydomain/cgi-bin/awstats.pl?config=myvirtualhostname, 点击“立即更新”。
页面截图:

11、通过任务计划自动更新:
创建批处理文件AwstatsUpate.bat,内容为 :

程序代码 程序代码
c:
cd C:\Program Files\AWStats\wwwroot\cgi-bin
awstats.pl -config=www.cnblogs.com -update

在任务计划调度运行该批处理文件。

更详细的步骤,请查看帮助文件 C:\Program Files\AWStats\docs\awstats_setup.html
参考文章:
1、http://briandesmond.com/blog/archive/2003/09/08/176.aspx
2、http://www.cnblogs.com/Files/dudu/InstallingAWStatsOnIIS6.rar(pdf 文件)

引用内容 引用内容
以下为几点需要注意的问 题:
·为确保首次配置可以被正确执行,尽可能为相关目录降低安全级别,待调试正常后再更改目录安全级别
a)Active Perl 目录安全添加账户EveryOne>>完全控制
b)IIS日志存储 目录安全添加账户EveryOne>>完全控制
c)AWStats 安装目录中 \wwwroot\cgi-bin\ 目录安全添加账户EveryOne>>完全控制

·如何将服务器中IIS日 志文件迁移至本地应用AWStats对其进行分析?
只需将服务器IIS日志文件迁移至本地IIS日志相同存储路径即可,同时修改 awstats.myvirtualhostname.conf 配置文件中的IIS日志对应文件名称,并利用
awstats.pl -config=myvirtualhostname -update 该命令生成新的日志记录,打开浏览器访问http://www.myserver.mydomain/cgi-bin/awstats.pl?config=myvirtualhostname即 可观测到服务器迁移至本地的IIS日志分析报告;

·如何分析以前的IIS日志信息?如何分析指定日期的IIS日志信息?
操作方法 与”如何将服务器中IIS日志文件迁移至本地应用AWStats对其进行分析?”相同,只需对配置文件进行修改,指定要分析的文件名称,并生成报告即可;

· 为什么我的没有数据呢??一直显示从未更新!
类似情况在我的实际应用中也遇到过,通过与dudu沟通得知,可能原因为日志文件过大(1GB或更 高)而导致类似现象发生,解决方法就是利用”awstats.pl -config=myvirtualhostname -update”该命令生成报告,并通过IE浏览分析内容;

·执行awstats.pl -config=myvirtualhostname -update报错,提示信息如下:
Error: Couldn’t open server log file “h:\WINDOWS\System32\LogFiles\W3SVC1\ex051106.log” : Permission denied … …
出现该提示信息的原因在于没有正确设置IIS日志存储路径,在IIS管理器日志存储设置中指 明存储路径后,在该目录中还会生成一个新的目录,通常在观察路径与设置路径时易被忽视,请仔细检查配置文件中的IIS日志存储路径;

·AWStats 分析报告存放在什么位置?
详见下节,AWStats配置详解

·AWStats安全验证解决方案
详见下节,AWStats 配置详解

·IIS中多站点目录日志分析如何设置
详见下节,AWStats配置详解

[转载]【翻译】ASP.NET操作WMI——用代码向IIS6添加站点、虚拟目录和主机头

mikel阅读(1201)

[转载]【翻译】ASP.NET操作WMI——用代码向IIS6添加站点、虚拟目录和主机头 | 天机不可泄漏.

原文:WMI Functions from ASP.NET
作者:JeroenMX

前言

本文介绍了ASP.NET如何通过WMI创建站点、添加虚拟目录和添加主机头。并且已在Windows Server 2003及IIS6的环境下测试通过。

这玩意儿花了老子3天时间才搞定,用了几个小时写代码,而且当中还花了不少时间解决Win32: Access denied error的问题。当然我要指出的是,无论NETWORK SERVER帐户还是IUSR_<servername>帐户都不要设置过大的权限。对于WMI和IIS metabase的安全机理,我还是一无所知的。我只不过解决问题而已。

看代码

首先要从Internet信息服务(IIS)管理器中获取网站标识符,点击“网站”根节点,右侧“标识符”显示的就是网站的ID。默认网站的标识符 通常是1。

获取网站标识的功能,我们要用到一个namespace,代码如下:

1 using System.Management;

下文所有’ServerName’都表示你的服务器名称,或者如果你的代码是本地运行的,也可以用一个点来表示。

创建一个站点,你会用到如下函数。这个函数返回新网站的ID,这样你可以进一步对这个网站进行操作。

01 public static string CreateWebsite(string serverName, string appPoolName, string ip, string pathToRoot, string hostName, string domainName, int port)
02 {
03 ConnectionOptions options = new ConnectionOptions();
04 options.Authentication = AuthenticationLevel.Connect;
05 options.EnablePrivileges = true;
06 options.Impersonation = ImpersonationLevel.Impersonate;
07 ManagementScope scope = new ManagementScope(string.Format(@\\{0}\root\MicrosoftIISv2, serverName), options);
08 scope.Connect();
09 ManagementObject oW3SVC = new ManagementObject(scope,
10 new ManagementPath(@"IIsWebService='W3SVC'"), null);
11
12 ManagementBaSEObject[] serverBindings = new ManagementBaSEObject[1];
13 serverBindings[0] = CreateServerBinding(scope, string.Format("{0}.{1}", hostName, domainName), ip, port);
14 ManagementBaSEObject inputParameters = oW3SVC.GetMethodParameters("CreateNewSite");
15 inputParameters["ServerComment"] = string.Format("{0}.{1}", hostName, domainName);
16 inputParameters["ServerBindings"] = serverBindings;
17 inputParameters["PathOfRootVirtualDir"] = pathToRoot;
18 ManagementBaseObject outParameter = oW3SVC.InvokeMethod("CreateNewSite", inputParameters, null);
19
20 string siteId = Convert.ToString(outParameter.Properties["ReturnValue"].Value).Replace("IIsWebServer='W3SVC/", "").Replace("'", "");
21 ManagementObject oWebVirtDir = new ManagementObject(scope,new ManagementPath(string.Format(@"IIsWebVirtualDirSetting.Name='W3SVC/{0}/root'", siteId)), null);
22 oWebVirtDir.Properties["AppFriendlyName"].Value = string.Format("{0}.{1}", hostName, domainName);
23 oWebVirtDir.Properties["AccessRead"].Value = true;
24 oWebVirtDir.Properties["AuthFlags"].Value = 5; // Integrated Windows Auth.
25 oWebVirtDir.Properties["AccessScript"].Value = true;
26 oWebVirtDir.Properties["AuthAnonymous"].Value = true;
27 oWebVirtDir.Properties["AppPoolId"].Value = appPoolName;
28 oWebVirtDir.Put();
29
30 ManagementObject site = new ManagementObject(scope, new ManagementPath(Convert.ToString(outParameter.Properties["ReturnValue"].Value)), null);
31 site.InvokeMethod("Start", null);
32 return siteId;
33 }

创建一个虚拟目录:

01 public static void AddVirtualFolder(string serverName, string websiteId,string name, string path){
02 ManagementScope scope = new ManagementScope(string.Format(@"\\{0}\root\MicrosoftIISV2", serverName));
03 scope.Connect();
04
05 string siteName = string.Format("W3SVC/{0}/Root/{1}", websiteId, name);
06
07 ManagementClass mc = new ManagementClass(scope,new ManagementPath("IIsWebVirtualDirSetting"), null);
08 ManagementObject oWebVirtDir = mc.CreateInstance();
09
10 oWebVirtDir.Properties["Name"].Value = siteName;
11 oWebVirtDir.Properties["Path"].Value = path;
12 oWebVirtDir.Properties["AuthFlags"].Value = 5; // Integrated Windows Auth.
13 oWebVirtDir.Properties["EnableDefaultDoc"].Value = true;
14 // date, time, size, extension, longdate ;
15 oWebVirtDir.Properties["DirBrowseFlags"].Value = 0x4000003E;
16 oWebVirtDir.Properties["AccessFlags"].Value = 513; // read script
17 oWebVirtDir.Put();
18
19 ManagementObject mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDir='" + siteName + "'"), null);
20 ManagementBaseObject inputParameters = mo.GetMethodParameters("AppCreate2");
21 inputParameters["AppMode"] = 2;
22 mo.InvokeMethod("AppCreate2", inputParameters, null);
23 mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDirSetting='" + siteName + "'"), null);
24 mo.Properties["AppFriendlyName"].Value = name;
25 mo.Put();
26 }

给网站添加一个主机头:

01 public static void AddHostHeader(string serverName, string hostHeader, string ip, int port, string websiteID){
02 ManagementScope scope = new ManagementScope(string.Format(@"\\{0}\root\MicrosoftIISV2", serverName));
03 scope.Connect();
04
05 string siteName = string.Format("'W3SVC/{0}'", websiteID);
06
07 ManagementObject mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebServerSetting=" + siteName), null);
08 ManagementBaseObject[] websiteBindings = (ManagementBaseObject[])mo.Properties["ServerBindings"].Value;
09
10 ManagementObject mco = CreateServerBinding(scope, hostHeader, ip, port);
11
12 ManagementBaseObject[] newWebsiteBindings = new ManagementBaseObject[websiteBindings.Length+1];
13 websiteBindings.CopyTo(newWebsiteBindings, 0);
14 newWebsiteBindings[newWebsiteBindings.Length - 1] = mco;
15
16 mo.Properties["ServerBindings"].Value = newWebsiteBindings;
17 mo.Put();
18 }

最后别忘了这个函数,它可以为网站绑定一个网络标识。

01 </pre>
02
03 <pre>private static ManagementObject CreateServerBinding(ManagementScope scope,string hostName, string ip, int port)
04 {
05 ManagementClass mc = new ManagementClass(scope, new ManagementPath("ServerBinding"), null);
06 ManagementObject mco = mc.CreateInstance();
07
08 mco.Properties["Hostname"].Value = hostName;
09 mco.Properties["IP"].Value = ip;
10 mco.Properties["Port"].Value = port;
11 mco.Put();
12
13 return mco;
14 }

注意的几点

安全。如果之用上面的那坨代码还不行。我千方百计想让其运行,但貌似忽视了2件事情。访问WMI和IIS metabase。 ASP.NET在Windows Server 2003和IIS6.0上运行默认使用的是NETWORK SERVICE帐户。但是,我们还是要使用客户端模拟。 所以在Web.config中添加下面一个配置:

1 <identity impersonate="true" />

使用了这个配置,IUSR_<servername>会使用客户端模拟的方式去访问IIS metabase。在后面的文章里,我就用IUSR_来表示这个帐户。不要忘记,在IUSR_后面加上你的服务器名称才是这个帐户的名字。

WMI权限设置

  • 控制面板 –> 管理工具 –> 计算机管理 –> 服务和应用程序。
  • 右键WMI控制,点击“属性”。
  • 选择“安全”选项卡。
  • 展开Root树
  • 点击MicrosoftIISv2。
  • 点击“安全设置”。
  • 点击“高级”。
  • 双击IUSR_(如果“组或用户名称”里面没有的话,就把它添加进去)
  • 把IUSR_ “应用到”设置成“这个名称控件和子名称空间”
  • “允许”所有权限。
  • 所有窗口都点击“确定”。

IIS metabase权限设置

  • 下载并安装IIS6 Resource Kit
  • 运行MetaBase Explorer (在开始菜单的IIS Resource Kit中可以找到)。
  • 展开树形目录,右键第一个或第二个节点并且选择“Permissions”。
  • 如果提示你“The current key inherits its security permissions from the key /”,点击“是”。
  • 选择“IIS_IUSRS”,如果没有的话,把它添加进去。
  • 选择“Full Control”。
  • 所有窗口都点击“确定”。

有了足够的权限就能运行了。

如果有高人能对这个方法谈谈自己的感想,并且能指出更好的配置IIS和WMI的方法,那就更赞了。记得之前,我都是自己捣鼓出来的,所以我不知道这 个方法是不是最佳的方案。

如果你在运行代码的时候碰到任何问题,我愿意效劳。

译者注:

网上能搜索到的还有一种方法:使用DirectoryEntry方法,方法差不多,关键是要把权限设置好。

[转载]淘宝API读取实体类的属性的备注作为表头的应用

mikel阅读(1187)

[转载]读取实体类的属性的备注作为表头的应用 – wuhuacong(伍华聪)的专栏 – 博客园.

在做淘宝API的学习过程中,发现一个API的封装类库Top4NET对操作封装的比较好,试用了一下,效果非常不错,另外由于淘宝的API对象参 数很多,多数是英文的,另一方面Top4NET的实体类对象备注信息很详细,因此可以考虑吧实体类的属性描述信息作为列表的表头说明使用。

我们看到他的Domain里面的实体类信息描述很详细,代码如下所示:

代码

/// <summary>
/// 商品结 构
/// </summary>
[Serializable]
[JsonObject]
[XmlRoot(
item)]
public class Item : BaSEObject
{
/// <summary>
///<%

[转载]网站测试自动化系统—在测试代码中硬编码测试数据

mikel阅读(1044)

[转载]网站测试自动化系统—在测试代码中硬编码测试数据 – Killmyday – 博客园.

在前面的文章数据驱动测试里,讲到了将测试数据以及表现测试步骤的代码分开的技术。从测试的角度来看,固然希望能够覆盖的测 试场景越多越好,但是在设计和编写自动化测试代码的时候,却又可以事先设计好一些固定的测试数据简化自动化测试代码的编写工作。

之所以要这样做 (按照编程的术语讲是硬编码),是因为按照等价类划分,固定的测试数据一般都已经被其他测试用例覆盖了。请考虑下面这个例子,假设你要测试一个博客网站 (例如博客园)的文章评论功能,例如测试禁用一篇文章的评论功能,或者是测试文章作者删除评论的功能。按照正常的流程,肯定是需要先编码发布一篇文章,然 后再编码指定的评论功能测试用例。这样的流程有以下几个缺点:

1. 需要冗余的编码,因为每个评论测试用例的代码都要包含发布文章的步骤,在编程里面,我们都是极力推 荐,什么只要代码在不同的地方重复两次,就要考虑是否将它封装成一个函数之类的理念。这种包含冗余编码的方式是我们在测试过程中极力要避免地,否则,程序 员可能哪天心情很好,重构一下代码,破坏了一些网页的HTML结构但是从用户的角度来看又没有任何区别;这种代码重构,作为测试人员只能跟着程序员的代码重构,修改 测试代码,那个时候,你当然会希望改的地方越少越好啦。

对于这个 缺点,可能有人要说,在前面的文章“网站测试自动化系统—基于SeleniumVSTT”,创建博客的测试步骤不是已经被有效地封装成一个函数了吗,为什么还会说有冗余?这是因为在自动化 测试过程中, 测试人员会定期(一些高规格的软件开发团队要求每天)将所 以编写完毕的测试代码批量执行一遍,这就涉及到对于任何测试用例编码都非常重要的两个原则:

1) 一个测试用例可以独自执行成功,就是说如果是单独执行这一个测试用例的话,这个测试用例是可以执行成 功的否则就是产品编码的失误(Bug)。举个例子,你正要编码测试一个管理博客文章的功能,这个功能通常来说都是登录用户才可以使用的。 然而,也许你刚刚编码完毕一个登录方面的测试用例,而且用例执行完毕的时候,没有执行注销操作。这个时候你不能想当然地以为下一个测试用例一定就是你现在 正在编码的文章管理的测试用例。

因为测试 人员既保留有将多个测试用例任意排列执行的权力,也可以选择单独执行这一个测试用例比如程序员刚刚重构了文章管理功能的代码,为了节省测试时间,测试人员可能会选择只执行文章管理方面的测试用例。所以不要将自己的命运寄托在 别人手里。即除了整个团队都公认的前提以外,不要相信任何前提。

2) 测试用例可以在任意排列的用例序列中执行通过,因此测试代码应该尽量保护测试环境。举个例子,你设计 了一个管理用户权限的测试用例,一般来说这种功能只有管理员才有权限操作的。然而,也许另一个粗心大意的测试工程师编码了一个测试删除用户的用例,恰好将 管理员删除了,而你的用例正好在他的用例之后执行……己所不欲,勿施于人,既然你不希望碰到这种情况,那么在编码自己的测试用例之前也应该避免类似的事情 发生。

回过头 来再举评论管理测试用例的设计,于是你的几个测试代码可能看起来像下面这样:

[TestMethod]

public void BlogCommentIsDisabled()

{

TestLibrary.UserHelper.LogOnAsAdmin();

var blog = TestLibrary.BlogHelper.CreateBlog(博客文章标 题, 文章内容);

// 去管理文章的网页

TestLibrary.BlogHelper.ManageArticles();

// 在文章管理的网页的文章列表里依次 查找标题为

// “博客文章标题的文章连接,

var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

// 并且在网页上点击浏览这个链接,打开阅读文章的网页

blogListItem.View();

// 评论这篇文章

TestLibrary.BlogHelper.Comment(blog);

// 然后执行一些验证判断评论功能的确 被禁用掉了

// …

}

[TestMethod]

public void DeleteBlogComment()

{

TestLibrary.UserHelper.LogOnAsAdmin();

var blog = TestLibrary.BlogHelper.CreateBlog(博客文章标 题, 文章内容);

// 去管理文章的网页

TestLibrary.BlogHelper.ManageArticles();

// 在文章管理的网页的文章列表里依次 查找标题为

// “博客文章标题的文章连接,

var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

// 并且在网页上点击浏览这个链接,打开阅读文章的网页

blogListItem.View();

// 评论这篇文章

var comment = TestLibrary.BlogHelper.Comment(blog);

// 找到刚才的评论、删除评论,然后执 行验证确定

// 评论被删除掉

}

每个测试 用例单独执行的时候,都不会有任何问题,但是两个放在一起执行的时候,问题就来了,两个用例创建了同名的文章,这样就直接导致测试结果的不稳定。为了解决 这个问题,也许有人会创建一个随机生成文章标题的帮助类(Helper Class),这种编码的难度很大,因为需要确保文章的标题永远是唯一的(或许可以考虑Guid?)。

2. 节省测试的时间,在用例中执行过多的步骤也会增加测试时间。虽然测试团队都会在晚上批量执行自动化测 试用例,但是在产品开发的过程当中,测试用例通过率不能达到100%是很 正常的。对于每一个失败的测试用例,测试人员都要分析失败的原因判断是 产品的缺陷导致的,还是由于测试代码本身的问题引起的。额外的测试步骤也会相应地增加测试人员分析失败的时间(一般测试人员都会重新执行一遍测试代码来找 出问题原因)。

3. 增加不必要的测试用例失败,测试可以分好几块,一种是功能测试,也就是验证产品的功能是否可以正常工 作;一种是压力测试,即测试产品在极端情况下的执行情况;还有其他的例如性能测试,国际化测试等等。一般来说,不同的测试都会有自己的自动化测试用例集 合。如果在功能测试当中,用例代码在系统里面添加了很多冗余数据,执行的测试用例多了,必然导致网站的性能和反应速度会有所下降。而在测试代码中,一般都 会在执行一步操作以后,等待一段时间等网页的内容刷新。网站反应速度的 下降,直接导致测试失败。例如本来在编写测试代码的时候,3秒钟肯定会刷 新的网页,在测试执行的环境中,因为过多的冗余数据,30秒可能都打不开 一个网页。当然啦,网站反应速度的下降肯定是产品代码的缺陷,但是不应该将压力测试和功能测试混合起来做。

因此,我个人建 议,在测试过程中,例如前面举的评论功能的测试中,完全可以事先在网站的数据库中先创建好一篇或多篇专门用来做评论测试的文章。而每天晚上,在大规模执行 自动化测试用例之前,编写一个小的脚本,将网站的数据库替换成这个基准数据库。

又比如,为了测试 用户权限管理的功能,完全可以事先在网站的数据库当中先准备好一个管理员帐号,这个管理员帐号和密码可以当作一个常量,然后测试代码里都使用这个帐号来执 行权限管理的测试。例如下面的代码:

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string AdminPassword = “0123456”;

}

public class UserHelper : UIHelperBase

{

public UserHelper(TestLibrary settings)

: base(settings)

{

}

public void LogOnAsAdmin()

{

LogOn(TestLibrary.Consts.AdminUserName, TestLibrary.Consts.AdminPassword);

}

public void LogOn(string username, string password)

{

if (String.IsNullOrEmpty(username))

throw new CaseErrorException(new ArgumentNullException(“username”));

if (String.IsNullOrEmpty(password))

throw new CaseErrorException(new ArgumentNullException(“password”));

selenium.Open(“/”);

Thread.Sleep(2000);

if (selenium.IsElementPresent(“link=Log On”))

{

selenium.Click(“link=Log On”);

}

if (selenium.IsElementPresent(“link=Login”))

{

selenium.Click(“link=Login”);

}

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Type(“username”, username);

selenium.Type(“password”, password);

selenium.Click(“//input[@value=’Log On’]”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

}

}

在上面的代码中, 我也把等待网页刷新的时间设置成常量。对于在测试代码中使用事先在基准数据库中准备的测试数据,需要一点编程技巧。请先看下面的代码,下面的代码是一段记 录通过网页操作创建文章的代码:

public class Blog : UIHelperBase

{

// 博客的标题

public string Title { get; private set; }

// 博客的超链接

public string Permalink { get; private set; }

// 博客的超链接文本

public string MenuText { get; private set; }

public string Owner { get; private set; }

public Blog(TestLibrary settings, string title,

string permalink, string menutext, string owner)

: base(settings)

{

Title = title;

Permalink = permalink;

MenuText = menutext;

Owner = owner;

}

// 通过网页界面的操作创建一篇新文章

//

// PostSetting是一个结构,包含了一篇新文章的所有元素,

// 例如文章标题,内容等等.

public Post CreatePost(PostSettings settings)

{

if (settings == null)

throw new CaseErrorException(new ArgumentNullException(“settings”));

if (!String.IsNullOrEmpty(settings.Body))

throw new CaseErrorException(“Set post body is not implemented yet!”);

if (settings.PublishDateTime.HasValue)

throw new CaseErrorException(“PublishDateTime is not implemented yet!”);

// selenium这个变量,你可以想象成是一个正在浏览网页的 网友的封装

selenium.Open(“/”);

selenium.Click(“link=Admin”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Click(“link=Manage Blogs”);

selenium.WaitForPageToLoad(“60000”);

selenium.Click(String.Format(“link={0}”, Title));

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Click(“link=New Post”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Type(“Routable_Title”, settings.Title);

selenium.Type(“Tags”, settings.Tags);

if (settings.Permalink != null)

selenium.Type(“Routable_Slug”, settings.Permalink);

if (settings.DisableNewComments)

selenium.Click(“CommentsActive”);

if (settings.PublishSetting == PostSettings.PublishSettings.PublishNow)

selenium.Click(“Command_PublishNow”);

else if ( settings.PublishSetting == PostSettings.PublishSettings.PublishLater )

throw new CaseErrorException(“PublishLater is not implemented yet!”);

selenium.Click(“submit.Save”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

return new Post(TestSettings, settings, this);

}

}

public class PostSettings

{

public enum PublishSettings

{

SaveDraft,

PublishNow,

PublishLater

}

public string Title { get; set; }

public string Permalink { get; set; }

public string Body { get; set; }

public string Tags { get; set; }

public bool DisableNewComments { get; set; }

public PublishSettings PublishSetting { get; set; }

public DateTime? PublishDateTime { get; set; }

}

public class Post : UIHelperBase

{

// 当初创建文章的原始详细信息

public PostSettings Settings { get; private set; }

// 文章的标题从网页上获取

public string Title { get { return selenium.Read(…); } }

// 下面省略文章相关的操作若干

// …

public Post(TestLibrary settings, PostSettings postSettings, Blog blog)

: base(settings)

{

Settings = postSettings;

ContainerBlog = blog;

}

// 下面省略文章相关的操作若干

// …

}

从上面的代码中, 你可以观察到,Post的属性,除了Settings属性以外,其他的属性都是从网页上直接读取的当然是假设当前网页正在显示对应的文章。因此,要将基准数据库集成到自动化测试代码中来,只要实例化 一个PostSettings变量就好了。TestLibrary 负责连接到Selenium-RC,并保存对应连接的类。下面的代码演示了这个思想:

public class TestLibrary

{

public UserHelper UserHelper { get; private set; }

public BlogHelper BlogHelper { get; private set; }

public CommentHelper CommentHelper { get; private set; }

public Blog DefaultBlog { get; private set; }

public Post DefaultPost { get; private set; }

public ISelenium Selenium { get; private set; }

public string SiteUrl { get; private set; }

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string AdminPassword = “0123456”;

}

public TestLibrary(ISelenium selenium)

{

this.UserHelper = new UserHelper(this);

this.BlogHelper = new BlogHelper(this);

this.CommentHelper = new CommentHelper(this);

Selenium = selenium;

InitialDefaultSiteDate();

}

private void InitialDefaultSiteDate()

{

DefaultBlog = new Blog(this, “Default Test Blog”, “default-test-blog”, “Default Test Blog”, Consts.AdminUserName);

DefaultPost = new Post(this, new PostSettings()

{

Title = “Default Test Post”,

Permalink = “default-test-post”,

Body = “This is for web site testing purpose.”,

Tags = “Test”,

PublishSetting = PostSettings.PublishSettings.PublishNow

},

DefaultBlog);

}

}

下面是TestLibrary的完整源代码:

public class TestLibrary

{

public UserHelper UserHelper { get; private set; }

public BlogHelper BlogHelper { get; private set; }

public CommentHelper CommentHelper { get; private set; }

public Blog DefaultBlog { get; private set; }

public Post DefaultPost { get; private set; }

public ISelenium Selenium { get; private set; }

public string SiteUrl { get; private set; }

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string ContributorUser = “Contributor1”;

public const string AuthorUser = “Author1”;

public const string ModeratorUser = “Moderator1”;

public const string EditorUser = “Editor1”;

public const string CommonPassword = “0123456”;

public const string AdminPassword = “0123456”;

public const string DefaultSeleniumHost = “localhost”;

public const int DefaultSeleniumPort = 4444;

public const string DefaultBrowser = “*firefox”;

public const string DefaultSite = “http://localhost:30320”;

}

public TestLibrary(ISelenium selenium)

{

this.UserHelper = new UserHelper(this);

this.BlogHelper = new BlogHelper(this);

this.CommentHelper = new CommentHelper(this);

Selenium = selenium;

InitialDefaultSiteDate();

}

private void InitialDefaultSiteDate()

{

DefaultBlog = new Blog(this, “Default Test Blog”, “default-test-blog”, “Default Test Blog”, Consts.AdminUserName);

DefaultPost = new Post(this, new PostSettings()

{

Title = “Default Test Post”,

Permalink = “default-test-post”,

Body = “This is for web site testing purpose.”,

Tags = “Test”,

PublishSetting = PostSettings.PublishSettings.PublishNow

},

DefaultBlog);

}

public static TestLibrary SetupTest(TestContext testContext)

{

if (testContext != null && testContext.DataRow != null && testContext.DataRow.Table.Columns.Contains(“seleniumHost”))

{

return SetupTest(testContext.DataRow[“seleniumHost”].ToString(),

Int32.Parse(testContext.DataRow[“seleniumPort”].ToString()),

testContext.DataRow[“browser”].ToString(),

testContext.DataRow[“site”].ToString());

}

else

{

return SetupTest(Consts.DefaultSeleniumHost, Consts.DefaultSeleniumPort,

Consts.DefaultBrowser, Consts. DefaultSite);

}

}

public static TestLibrary SetupTest(string seleniumHost, int seleniumPort,

string browser, string site)

{

var selenium = new DefaultSelenium(

seleniumHost, seleniumPort, browser, site);

selenium.Start();

return new TestLibrary(selenium) { SiteUrl = site };

}

public void Shutdown()

{

try

{

Selenium.Stop();

}

catch (Exception)

{

// Ignore errors if unable to close the browser

}

}

}

未完待续……

[转载]淘宝API开发系列--开篇概述

mikel阅读(1282)

[转载]淘宝API开发系列–开篇概述 – wuhuacong(伍华聪)的专栏 – 博客园.

前阵子有空发现淘宝的API挺有意思的,就留意学习了一下,淘宝从2008.6月开始公测以来,API渐趋稳定,文档的相关说明也比较细致,不过基 于淘宝API做应用的软件目前应该还不是很多,不过由于马云的影响力,淘宝API终会发光发热,给我们做开发的程序员多一份选择,多一个摆弄的东西。

在学习中,发现有一些比较好玩,比较有趣的地方,如果有空继续的话,准备开一个系列,总结介绍一下相关的知识,希望能够给博客多一份人气,多一份积 累。

淘宝开放平台(Taobao Open Plateform,又叫TOP),地址是http://open.taobao.com/ , 我们看看其官方对TOP的描述:

淘宝开放平台(即TOP,无特别说明下文中TOP即指淘宝开放平台)对各类合作伙伴所提供的产品可以概括为两个平台,三条支撑主线。两个平台指的是Open.taobao.comApp.taobao.com ;三条支撑主线是开放数据和业务流程、开放淘宝自有插件平台、开放对外接入标准,关于这两个产品和三条业务支撑线。

和众多平台一样,如Google,淘宝一样需要开发者注册一个账号,淘宝每个应用需要注册一个应用程序键(App Key),淘宝就是基于应用程序键来控制用户的访问频率和流量的,另外可以通过应用程序键,让使用者登陆确认,获取到相关的授权码,然后获取 SessionKey,作为访问使用者淘宝资源(如买入卖出等私人记录的信息)。


淘宝的授权码是在用户确认后产生,并且只能使用一次,用来生成SessionKey,一般来说,SessionKey间隔的调用API事件不能超过 10分钟,否则就会失效,需要重新获取用户的授权码,然后再次生成SessionKey。这样的做法虽然比较麻烦,但是对保证使用者的淘宝资源确是比较不 错的做法。大概的界面如下所示:

淘宝的论坛有各种语言开发的介绍,相对来说还是比较详细,开发者有什么问题,在这里都可以问问,论坛地址是:http://open.taobao.com/bbs/forum.php

淘宝的API是以REST服务提供了,通过HTTP访问,返回相关的信息,格式可以是Json格式的,也可以是XML格式的,各取所需吧。

有一位能人把淘宝的API做了进一步的封装,就是TOP4NET的类库,封装了各种API资源的访问,并且把信息封装为实体类对象进行传递,使用起 来还是不错的,本篇后面会进一步介绍一下基于TOP4NET的基础上做淘宝的应用开发。

TOP4NET 是作为开源组件提供的,其SVN地址是http://code.google.com/p/top4net/source/browse/#svn/trunk/Top4Net, 大家可以下载下来研究研究,做一定的修改补充,代码风格写得不错,赞一个。

主要研究技术:代码生成工具、Visio二次开发

转载请注明出处:
撰写人:伍华聪  http:
//www.iqidi.com

[转载]web报表开发技术专题六:尴尬的Web打

mikel阅读(1106)

[转载]web报表开发技术专题六:尴尬的Web打印 – webreport – 博客园.

自从电脑被发明以来,将电脑中的内容打印出来是自然而然的需求。进入互联网时代后便更是如此。但要想完美地实现Web打 印功能却并非易事,甚至于根本都不可能彻底地解决好这个问题。

记得很早以前田荣举(注:田荣举现任金蝶集团高级副总裁兼任首席技术官)和我私下聊 过:“打印功能要占到整个系统开发量的两成”。打印功能是一个系统开发的重要组成部分。中国人历来好面子,往往要求有漂亮的打印,以显露科技之高深。打印 功能对于web报表工具来说就更是不可或缺的了。

因为打印功能的重要性,所以进入web开发后,很快就开始关注web打 印功能的开发了,web打印有下面的相关问题需要考虑清楚:

1 在客户端还是服务器端打印?考虑到数据传递到客户端的速度问题,在服务器 端做大数据量的打印还是不错的选择。但在后台打印必竟很不方便,所以主要还是要在客户端打印,即利用客户端的打印机打印。

2 是导出为PDF或EXCEL文件后打印还是直接打印?将要打印的内容生成 为PDF或EXCEL文件后再打印,这也是一种方式,这种方式实际上也就几乎不需要专门考虑打印功能的开发了,开发工作量少。但其缺点也很明显:需要客户 端安装打开PDF的软件(或EXCEL软件),需要在另一个软件中打印,步骤多,无法实现点击后直接打印,这种打印模式也无法实现某个单据是否打印了要当 作一个标志保存起来的场合。

3 是利用IE的打印还是自己实现打印?如果打印功能全部由自己实现,因为要 处理数据,样式,位置,图片等的打印,自然程序量小不了,往往会使第一次的下载量很大。而且往打印控件中传递要打印的数据的过程会比较复杂而且慢。

4 第一次打印时需不需要下载ActiveX控件?如果不需要,则无法用 JavaScript脚本程序控制客户端的打印机,无法自定义纸张,无法不弹出打印对话框就直接打印。也就是说,最终用户在每次打印时都要手工选择好打印 参数(如打印方向,纸张大小等),这样显然会使最终用户的操作比较麻烦。

最开始我是设想直接用JavaScript脚本来计算分页,直接利 用IE中的打印机制来解决打印问题。后来随着IE的 版本升级,安全机制不断完善。在不下载ActiveX插件的情况下,IE的 安全机制是不可能让你用JavaScript脚本来全面控制客户端的打印机的。试想如果这样的话就 有可能浏览网上的某个网页时,你连接的打印机就自动开始打印东西了。也就是说,要想弄好Web打印,必须要在客户端下载插件。 在中国流氓插件泛滥的情况下,大家一看到插件都怕了。要不就要下载插件,要不打印就不太方便。这成了web打 印的死结。且觉得一样子无法解开,真是尴尬的Web打印

既然最佳的方式是无解的,哪就退而求其次吧。用一个小的轻量级的activeX控 件来控制客户端的打印机吧,这样第一次下载这个activeX控件是也不会很慢。

记得以前在卖web打印控件时,有一用户将scriptx网站告诉我,说要能实现这样的功能就买。于是便开始研究起scriptx来。按照我以前用visual c++编程的经验,觉得大体就是用钩子函数来实现。结合IE的打印模 版技术,终于基本上达到了。用visual c++ATL(为 了减少字节而没有用MFC)做了一个webprint.dll, 这个webprint.dll压缩成cab文 件后只有75K,和一个网页的大小相当。这样在第一次下载时便不会太慢了。因为真正的打印机制还是 调用IE内部的,并没有在webprint.dll里 面实现具体的打印功能,所以就不需要将要打印的内容传递到webprint.dll中,这是区别于 其它用activex控件实现web打印的 重要一点。如果靠activex控件来实现全部的打印功能的话,就必然要求要将打印的内容传递到activex控 件中,这个传递的过程会比较麻烦而且速度也慢。Webprint.dll只完成向IE的 内部实现的打印功能模块传递打印的基本参数(如:页面大小,页边距,打印方向,直接打印),网页上显示什么内容就打印什么内容,没有向webprint.dll传 递打印内容的过程。如下图所示的打印预览界面图:

实际上,打印的需求很简单,无非就是:分页,页眉页脚,页边距,打印方向,自定义纸张,直接打印,弹出打印对话框再打印, 预览,放缩打印,成批打印,套打等功能,打印的需求很固定,明确而且通用。将一个多页的报表一页页地打印出来这样一个简单的需求,在网页上却相当麻烦,我 也是在不久前才将它在e表中算是比较理想的实现了,即可以象cs程 序一样,点一下按钮,就将多页的报表一页页地打印出来。这其中要考虑到在互联网上从服务器端向客户端传递大量数据时的分页处理,以及打印了一页数据再传递 另一页数据的问题。因为如将要打印的多页内容一次从服务器端全部发送到客户端的话,在当今互联网的条件下很可能会因速度慢而受不了。只能每次从服务器端发 送一页数据到客户端,打印,然后再发送下一页数据到客户端,再打印,如此下去直至打印结束。这一点也是web打 印比c/s程序的打印要麻烦的地方。

总之,Web打印问题已经和浏览网页速度慢的问题一样,成为Web程 序的难题之一。只能作一些权衡。比如象国外因为PDF查看工具的流行,大多选择生成PDF文 件后再打印的方式。我研究Web打印解决方案多年,最终采用的轻量级的ActiveX控 件的Web打印方案,是我现在认为最适合中国国情的方案。也许以后还能找到更好的。

待 续…

相关链接:
[原创]web报表开发技术专题一:序号问题
[原创]web报表开发技术专题二:报表工具的核心—数据集的变换
web报表开发技术专题三:不规则分组和跨行组运算
web报表开发技术专题四:开发报表设计器的背后故事
web报表开发技术专题五:Excel的阴影
web报表工具的制表效率分析
几种web报表打印方案的比较

[转载]归纳asp.net mvc学习中应该要掌握的内容

mikel阅读(866)

[转载]归纳asp.net mvc学习中应该要掌握的内容 – longgel – 博客园.

自从自己学习ASP.NET mvc 有过一段时间了,今天回想一下之前学习ASP.NET mvc模型中的几个把准方向的内容,今天在这里归纳一下,也许会ASP.NET mvc不了解的园友有所帮助。

当然首先要弄清楚第一个的 就是MVC这个的概念,Controller,View,Model三者之间的关系。

我刚开博的时候,就有写关于asp.net MVC学习的一些相关文章,说明一下,其实我开博的原因第一就是有一个技术交流的平台,第二就是提升自己的写作能力以及表达能力。下面也列出之前写过的相 关MVC概念上的内容。写的不好,请多多指出,谢谢。

(asp.net MVC学习)对ASP.NET MVC框架的了解

(asp.net MVC学习)控制器Controller以及Action

(asp.net mvc学习) 视图View的使用以及与Controller之间的数据传递

其 次学习ASP.NET MVC框架中有一个必须了解的内容就是URL Routing,虽然它并不是asp.net mvc框架中的内容,但是asp.net mvc能够使用都必须靠它。我也有写过关于URL Routing的文章,虽然都写的不好,但是对于需要入门的朋友应该是有帮助的。^_^

(ASP.NET MVC学习)url Routing 使用与介绍

另外还有就是ActionFilter,asp.net mvc中的一些帮助类,如HtmlHelper,UrlHelper的使用,这些之前也有写过,只不过我今天看了之前写的,感觉水平太差,今天还是推荐给 大家,希望大家不要见怪(不爽的都可以骂我的),因为之前都是没有什么代码的,这样讲大家可能是不能很好的理解,今后会贴上相关的代码。

(asp.net mvc学习)ASP.NET MVC ActionFilter的学习与使用

(asp.net MVC学习)System.Web.Mvc.HtmlHelper学习及使用

(asp.net MVC学习)System.Web.Mvc.UrlHelper的学习与使用

还 有在这里本人不得不提的就是Controller和view之间使用强类型数据,我也觉得很重要,之前就没有写这方面的文章,后面我将会把这一节给补上。

因为我的水平很低,而且写作和表达能力都比较差,所以写出来的内容只能给准备学asp.net mvc的朋友一些指引,对于大牛者,希望你们帮我指出问题,并且如果大牛们有相关的文章,都可以贴给我看一下,我也好跟着大牛们学习学习。谢谢