[转载]如何使用C#实现QQ号码的申请

mikel阅读(1137)

[转载]如何使用C#实现QQ号码的申请 – wuhuacong(伍华聪)的专栏 – 博客园.

腾讯向大众提供了申请QQ的界面,方便很多潜在用户加入QQ大军中,注册页面是http://reg.qq.com ,为了限制用户的过度使用,设置了验证码、IP限制等手段,一般用户默认一天只能申请几个QQ号码,号码是随机生成的,当然好的号码是不会有的,这些是腾 讯的资源,需要付费才能获取到。

本文主要介绍如何利用C#来实现QQ号码的快速、批量申请操作,以及考虑如何加入宽带拨号的方式实现IP的限制,尽可能的申请到更多的QQ号码,以供他用。

先看看演示程序的界面效果:

=====》

上面是我使用C#实现QQ号码申请的演示程序,是应用的雏形。其中为了方便,把用户需要填写的信息封装起来,用户开通QQ号码后,再自行修改即可,只需要输入验证码即可快速申请到QQ。

1、首先需要获取验证码,然后供用户输入并提交申请,这里我使用了后台线程进行处理,提高用户界面的响应效果。

public partial class Form1 : Form

{
private CookieContainer cookieReg = new CookieContainer();
private BackgroundWorker worker = new BackgroundWorker();
private WebProxy proxy;

public Form1()
{
InitializeComponent();

worker.DoWork +=new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted
+= new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

//proxy = new WebProxy(“120.203.214.184”, 80);
}

private void GetVerifyImage()
{
worker.RunWorkerAsync(
GetImage);
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
string newverifyUrl = http://captcha.qq.com/getimage?aid=1007901& +
new Random().NextDouble() + new Random().Next(10) + new Random().Next(10);

cookieReg = new CookieContainer();
HttpHelper httpHelper
= new HttpHelper();
string regurl = http://reg.qq.com;
httpHelper.GetHtml(regurl, cookieReg, regurl, proxy);

using (Stream s = httpHelper.GetStream(newverifyUrl, cookieReg, regurl, proxy))
{
if (s == null)
{
MessageUtil.ShowWarning(
获取登陆码错误,请检查您的网络!);
return;
}
e.Result
= Image.FromStream(s);
}
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Image image
= e.Result as Image;
if (image != null)
{
this.pictureBox1.Image = image;
}
}

2、 处理申请操作结果显示。申请QQ的处理操作相对比较繁琐,我把它封装在一个处理函数中,这样我们在事件处理的时候,先宏观处理结果,然后在进一步细化具体的操作,如下所示:

private void btnApplyNumber_Click(object sender, EventArgs e)
{
if (this.txtVerifyCode.Text.Length < 4)
{
SetTips(
请输入验证码);
this.txtVerifyCode.Focus();
}

string qqnumber = ApplyQQ();
if (!string.IsNullOrEmpty(qqnumber))
{
SetTips(
string.Format(恭喜您,申请到QQ {0}, qqnumber));
LogTextHelper.WriteLine(
string.Format({0}—-{1}, qqnumber, 123abc));
}
else
{
if (getnumHtml.Contains(此IP申请的操作过于频繁))
{
SetTips(
IP操作过于频繁);
}
else
{
SetTips(
申请失败,请重试);
}
}
GetVerifyImage();
}

以 上先对申请的操作进行处理,并记录最后申请结果页面,如果页面有一些特殊的如IP操作频繁的信息,则提示用户IP操作的问题,方便采取如“手工拨号”等方 式进行处理,实现IP的变化,从而可以申请更多的QQ号码。如果一切正常,返回QQ号码,那么记录到相关的媒介并显示即可。

3、 详细QQ申请操作处理逻辑。申请QQ的处理,不是一般的麻烦,为了不给用户识别相应的提交内容,腾讯把申请的资料进行乱码化处理,并把顺序打乱,把相应的 名称处理成0f2b4766321c2d6a4c7a61515324这样不可识别的字符串,然后统一提交,一般人是识别不了这些字符串的,字符串不是加 密串,是随机按照规则组合而成,很变态。这里我提供大致的思路,就是先获取Cookie的相关信息,把里面的变量作为基础,对数据进行处理,然后作为相应 的字段名称,混合提交的内容,一起POST到服务器,如果成功,则返回QQ号码,否则提示相应的错误信息。

private string getnumHtml = “”;//最后申请的网页内容
private string ApplyQQ()
{
string html = “”;// “g_dataArray=new Array(new Array(\”0f2b4766321c2d6a4c7a61515324\”,\”4621587a340437624e71315a5a2b\”,\”23755a6a29192c7b506d73474536\”,\”4f21596e22043464537b774b493a\”,\”2d2d5b6a01163078567072577102\”,\”4121587a340437624e7130525223\”,\”4f2d486122002679117b65555728\”,\”047548612200267910736d5d5f30\”,\”306d4a6024193779584b55656718\”,\”2574486122002679136b75454738\”,\”33344a66250e703f1429370b0970\”,\”252348683412256c49766a585c2d\”,\”161059673e192645547262564206\”),new Array(9796,9806,9807,9803,9797,9795,9793,9798,9802,9801,9800,9799,9792),\”/cgi-bin/getnum\”);”;
string regurl = http://reg.qq.com/;
HttpHelper helper
= new HttpHelper();

int BaseNum = 0;
BaseNum
= GetBaseNumByCookieSkey(cookieReg);

string nick = test;
string pass = 123abc;
string repass= pass;
string year = 2009;
string month = 12;
string day = 13;
string sex = 1;
string verifycode = this.txtVerifyCode.Text;
string province = 11;
string city = 1;
List
<string> ElementsArrName = GetElementArray(nick, pass, repass, year, month, day, sex, verifycode, province, city);
List
<short> DataArrayShort = new List<short>();
List
<string> DataArrayLong = new List<string>();

Thread.Sleep(100);
string regurl2 = http://reg.qq.com/cgi-bin/checkconn?seed + new Random().NextDouble() + new Random().Next(10);

html = helper.GetHtml(regurl2, cookieReg, regurl, proxy);
// html = “g_dataArray=new Array(new Array(\”0f2b4766321c2d6a4c7a61515324\”,\”4621587a340437624e71315a5a2b\”,\”23755a6a29192c7b506d73474536\”,\”4f21596e22043464537b774b493a\”,\”2d2d5b6a01163078567072577102\”,\”4121587a340437624e7130525223\”,\”4f2d486122002679117b65555728\”,\”047548612200267910736d5d5f30\”,\”306d4a6024193779584b55656718\”,\”2574486122002679136b75454738\”,\”33344a66250e703f1429370b0970\”,\”252348683412256c49766a585c2d\”,\”161059673e192645547262564206\”),new Array(9796,9806,9807,9803,9797,9795,9793,9798,9802,9801,9800,9799,9792),\”/cgi-bin/getnum\”);”;
GetDataArray(html, ref DataArrayShort, ref DataArrayLong);

string postData = GetPostData(ElementsArrName, BaseNum, DataArrayShort, DataArrayLong);
//MessageBox.Show(postData);

Thread.Sleep(
100);
string regurl3 = http://reg.qq.com/cgi-bin/getnum;
html
= helper.GetHtml(regurl3, cookieReg, postData, true, regurl, proxy);
getnumHtml
= html;//记录最后分析的内容,以便进一步分析操作

Regex re
= new Regex(var\\s*xyz=\(.*?)\;, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
Match mc
= re.Match(html);
if (!mc.Success)
{
return “”;
}
string qqnumber = mc.Groups[1].Value;
return qqnumber;
}

上 面注释的部分,就是获取到的相关信息,乱码一片,不过既然是本地处理,也是有规律可循的,本文只是介绍相关的规则,详细实现不在一一呈现,主要的处理逻辑 就是获取本地Cookie的某项内容,并把内容进行截断,作为一个BaseNum,然后把获取到的数组进行乱码化,作为提交字段的名称,然后一一放置相关 的内容,提交到服务处理。

上面只是一个简单的Demo,一般情况下,申请3~10左右的号码可能就会因为IP的限制,而不能继续,需要暂停几个小时才能继续可以申请一个左右,第二天才可以继续申请更多的号码。

为了更好的申请更多的QQ号码,一般需要加入拨号这种方式来处理IP的限制,如我的QQ搜通天中应用来批量申请QQ号码的界面如下所示:

如果需要了解详细,可以下载该软件来玩玩,熟悉下QQ申请的操作模式。

[转载]如何循序渐进学习Python语言

mikel阅读(910)

[转载]布同:如何循序渐进学习Python语言 – 布同世界 – 博客园.

大家都知道Python语言是一种新兴的编程语言。1989年,Python就由Guido van Rossum发明。Python一直发展态势很好。

原因有几点:1、跨平台性好。Linux、Windows、MacOS、塞班……。2、快速易学。3、模块齐全。

Python本身是一种面向对象的脚本语言。功能涉及到应用程序开发、网络编程、网站设计、图形界面编程等等,基本囊括众多应用。手机应用开发,电脑PC 程序开发,连Google也开始使用python作为其开发语言。很多编程比赛也开始接受python语言。

既然这么有用,那怎么学习呢?废话少说,马上解答。

《征服Python——语言基础与典型应用》

这本书中的ppt部分简单介绍了Python的相关知识。其源代码部分涉及到常见的数据结构和算法、语法的应用实例,完全可以用来进行模仿学习。这本书的源代码可以帮助你了解部分Python语言的应用和掌握几个简单的应用。

Python视频讲座-台湾辅仁大学教学视频

这个教学视频可以帮助你从一定程度上了解Python的一些属性,但是不能够帮助你入门。如果你简单看过Python的入门级别的语法教程,再 看看这个是有好处的,毕竟这个视频不长,几个小时就看完了。当然,里面的老师的普通话带一点台湾腔,程序说成是程式,字符串说成是字串或者序列 sequence……还有很多与大陆称呼的不同之处,不过这个不影响你的理解。

[Python教程].Game.Development.Tutorial

这个教程共19集,每集约10分钟,一共大概190分钟,三个小时。其中是一个老外Bucky Roberts的关于利用pygame模块进行小游戏编写的讲解,讲的非常细致,即使没有Python基础也可以看,你可以从中了解到Python编写 10-30行代码时的一些情形。他并不致力于讲解Python语法,而是讲解如何使用模块中的方法来快速编写小游戏。Bucky的英文不知道为什么,听起 来特别的顺,不像那些高深的系统专家的视频讲解那样枯燥。

[Python教程].Python.Programming.Tutorial

与之前那个视频比起来,这个视频就是Bucky Roberts讲解Python基本语法的视频。讲解非常细致。我相信大学英语四级水平完全可以看懂(PS:我知道四级什么水平,自信一点,相信我)。其 中完全是用示例来进行解释。很好懂。每集10分钟,一共43集,看完我估计你会跃跃欲试想要用Python编写自己的程序了。这个真的很不错的。用来在学 习python基础的时候间插着学习很好。

c经典100例Python语言实现

这本书是帮助那些有c或者c++数据结构基础的人,如何平滑过渡到Python语言上来。其中讲解了很多如何用Python解决实际的小型数学 问题。很不错的。看完你就知道Python用来替代c或者c++还是挺可能的。本书适合那种学完Python基础之后不知道如何下手写程序的人。

Python参考手册2004年3月22日版

共97页。这个版本是我看见的最新的版本了。如果你有更新的版本,请留言告诉我,谢谢了。其中用中英文对照的方式讲解了很多Python的特性 和语法知识。这本书不长,但是并不是一个全面的讲解Python知识的书籍。其中很多地道的实例可以教会你很多东西,这就是它的一个很好的价值所在。示例 好是很难得的。

Python Tutorial Python 入门指南Release 2.5b2

共137页。这本书是上面那本书的“升级版”,相似,而且更全面。很不错的示例,强力推荐。

《Python核心编程》第二版

好吧。我承认,这本书才是介绍语法知识最全面,涵盖面最广的书籍。用来为你构建一个完整的Python知识构架实在是太合适了。但是我推荐不要 一开始就看着本书。先了解Python的一些大概之后再来从这本书中吸取全面的营养会更加合适。如何了解大概呢?下面会给你介绍的,先别急。

Python技术参考大全

这本书,书如其名。非常厚实,非常全面。从语法知识到应用编程都包含了。完全是同上面这本书一个样子的。可以把这两本书结合起来看看。非常好。

Python学习笔记——皮大庆

这本书对于Python相当于《易学c++》对于C++。浅显而易懂,完全适合没有太多语言基础,甚至刚刚开始学习编程的人。这本书就是你学习Python的对好的第一本书。其中谈到了很多作者的个人理解,挺地道的,对你学习Python有很大帮助。

Python与Tkinter编程

这本书适合那种,学好了Python语法知识,需要进行应用程序设计的人。或者有MFC基础,了解了部分Python语法,急于用于编程的 人。(笔者是第二种)这本书讲到了如何利用Tkinter模块进行界面开发和应用开发。因为Python2.5已经包含了Tkinter模块,所以你不需 要安装和额外的导入就可以用Tkinter来编程了。你安装好Python就默认安装了Tkinter模块。

Python语言入门Learning Language

其实这本书用来学习Python语法和基础知识也是不错的,还可以简单了解一点应用编程的知识。但是由于国内好像还没有电子版的,只有影印版的,不利于你拷贝示例程序进行练习……如果你愿意自己一个个字母的敲打示例程序的话还是很不错的。

Python源码剖析

个人感觉这个书从整体和一些关键点去研究Python,比较高度。如果是有Python基础来看这本书比较好。其中有很多示例程序值得学习。也是一本好书,如果学好基础了,一定要过来看看这本书。

Tkinter编程代码示例

如果你有一些Python语法基础了,想要马上学习应用编程,又觉得学习Tkinter入手比较好,那么这本书很不错。其中的很多示例浅显易 懂,帮助你快速的搞出个widget在你的程序里易如反掌。要是学习Tkinter的widget,先读读这个书是很有好处的。帮你入门吧。由于这本书的 很多代码直接可以拷贝使用,所以这本书的作者提到还是尽量自己敲打示例程序比较好。我选择性的接受这个意见。

派森标准库Python standard library

如果你学完语法知识,感觉很好。但是开始变成后却发现Python的模块实在是太诡异了,到底有什么模块,都是干什么用的,如果你提出这个问题,这本书很好的解决你的疑问。其中讲解了大量的模块,你肯定很受用的。

深入Python (Dive Into Python)

这本书也是学习Python的最好的第一本书之一。浅显易懂,有选择性的为你展示Python的特点,这本书很好,而且厚度还可以接受。太厚的 书笔者不推荐用来作为学习的第一本。你可以试试看。这个东西还分为多个版本,不一定新版本就更加全面而易懂。笔者手中就有2001年版和2004年版。自 己看看就知道差别了。

Python document

这个是Python官方介绍Python的一个英文文档,系统的涉及到诸多Python的东西。但不像MSDN,它并不是一个全面而细致的文档 库,毕竟只有几兆大小。他还是会推荐你要看进一步详细的东西应该去看看“Python作者的书”。这句话不知道在其中提到多少次。这个document不 能当MSDN一样用,虽然都是英文的。还是可以查到部分信息。

Python bible

笔者感觉这个书就是把Python document整理之后形成的一本书。你想想圣经,代表神的旨意,这个书估计代表Python的作者的意图。挺强大的书,如果你有什么不懂的东西,也许你可以从中搜索到相关的解释和示例程序。所以一并推荐。

介绍了这么多书,我到底适合先读那本呢??下面介绍一下:

深入Python。阅读这个东西很轻松愉快。读到正则表达式的时候比较纠结。呵呵,你不一定要从一本书中学会Python基础。

Python核心编程。再学习这本书,你就轻松加愉快了。这本书的Tkinter部分之前你可以插读一下其他Tkinter入门更好的书。

下面介绍如何Tkinter入门:

Tkinter编程代码示例。这个读起来轻松愉快。我可不想把你搞的特别郁闷。这个作为开始点再合适不过了。其中作者还有很多东西没有在书中解决,呵呵,你多学两天也许就能够搞定这些书中遗留下来的问题,你会很有成就感的。

Python与Tkinter编程。这本书就稍微系统和周全的为你讲解Tkinter的知识了。

Python核心编程。又回到这本书了。

当然,你可以参考上面的简介自行选择一套学习路线的方案。请多参考上面的简介部分。

如何开始编写自己的第一个Python程序?

这个问题很重要。

1、安装IDLE。这个东西的名字叫做pywin32-214.win32-py3.0.exe。

字面解释。其中py:Python;

win32:Windows下的安装程序。

3.0:版本号。笔者使用的是2.5.4其实入门都可以用。

下载连接:http://www.python.org/download/。打开这个页面之后选择一款Windows下的安装程序。即可。

2、安装完成之后进入:开始/程序/Python*.*/DIE(Python GUI )。打开界面,输入print ‘Hello World’,回车,查看是否能够正常工作即可。

如何显示Hello World,那么就是正确可用的。这个是命令行方法,还有源代码编辑方法。要是需要进一步信息请发送邮件到wuqiangxx@126.com,我一定给你详细的解释,如何开始进行编程。

笔者的一点建议:

1、一棵树上吊死我不推荐你,你应该多试几棵。

2、每本书都有自己讲解的好的地方和不好的地方,你应该花几分钟体验一下,然后从最好入门的地方入手。

3、学习东西的过程是有一个加速度的。刚开始的时候是线形的速度,之后便是一个快速的上升期,然后又回到线形的速度。如果选择好书,一开始也可以处于有加速度的状态,那就是越学越开心,越来越感兴趣,好书常常能够让你做到这样子。

4、尽信书不如无书。有的示例程序看上去显而易见,但是你不拿过去运行一下,自己调试一下,你始终掌握不到其精髓和灵活运用的方法。自己动手改改,你会心悦诚服很多。

5、要善于总结。如果你光学不练,这是不好的,如果你不善于总结,这也是不好的。语言都是用不上的时候开始学习。都是用的上的时候开始复习。要 是用得上的时候开始学习,除非你抗压能力一流,不然我想你心情烦躁,效果会很不好的。学习的时候多总结一下,复习的时候可以翻出来看看,这样就不至于完全 荒废了,并且恢复相当快速。

6、学习编程不要太排斥英文。如果让你直接从英文开始学习,我想这个很难,但是如果用英文版本开始复习,这个就很好了。

笔者个人经验,不知道在你身上适不适用,如果你有收获,我就开心了。

[转载]使用MapReduce进行排序

mikel阅读(1132)

[转载]使用MapReduce进行排序 – Just For Fun – 博客园.

之前在工作中使用到过MapReduce的排序,当时对于这个平台的理解还比较浅显,选择的是一个最为简单的方式,就是只用一个Recude来做。因为 Map之后到Reduce阶段,为了Merge的方便,MapReduce的实现会自己依据key值进行排序,这样得出的结果就是一个整体排序的结果。而 如果使用超过一个Reduce任务的话,所得的结果是每个part内部有序,但是整体是需要进行merge才可以得到最终的全体有序的。今天读了 《Hadoop权威指南》中的第8章,对使用Hadoop这一MapReduce的Java实现进行排序有所了解,在此进行简单的总结。

首先我们来看一下Hadoop中内部Map和Reduce两个阶段所做的排序,可以使用下图来说明。

对MapReduce或者Hadoop有所了解的人可能都知道,所谓对于key值的排序,其实是在Map阶段进行的,而Rduce阶段所做的工作是对各个 Map任务的结果进行Merge工作,这样就能保证整体是有序的。如果想在使用多个Reduce任务的情况下保证结果有序,我们可以做的是在上图中的 partition阶段进行控制,使分配到每个reduce task的数据块为数值区域独立的,即比如整体数据在0~50之间,划分为5个Reduce任务的话,可以0~10区间的数据到第一个Reduce Task,10~20之间的到第二个,以此类推。但是这样就存在一个问题,划分出的各个任务中的数据可能并不是均等的,这样某些Reduce Task处理了很多数据,而其他的处理了很少的数据。Hadoop提供了RandomSampler类(位于InputSampler类中)来进行随机取 样,然后按照取样结果对值域进行划分。一个示例代码如下:

public class SortByTemperatureUsingTotalOrderPartitioner extends Configured implements Tool { @Override public int run(String[] args) throws Exception { JobConf conf = JobBuilder.parseInputAndOutput(this, getConf(), args); if (conf == null) { return -1; } conf.setInputFormat(SequenceFileInputFormat.class); conf.setOutputKeyClass(IntWritable.class); conf.setOutputFormat(SequenceFileOutputFormat.class); SequenceFileOutputFormat.setCompressOutput(conf, true); SequenceFileOutputFormat .setOutputCompressorClass(conf, GzipCodec.class); SequenceFileOutputFormat.setOutputCompressionType(conf, CompressionType.BLOCK); conf.setPartitionerClass(TotalOrderPartitioner.class); InputSampler.Sampler<IntWritable, Text> sampler = new InputSampler.RandomSampler<IntWritable, Text>( 0.1, 10000, 10); Path input = FileInputFormat.getInputPaths(conf)[0]; input = input.makeQualified(input.getFileSystem(conf)); Path partitionFile = new Path(input, "_partitions"); TotalOrderPartitioner.setPartitionFile(conf, partitionFile); InputSampler.writePartitionFile(conf, sampler); // Add to DistributedCache URI partitionUri = new URI(partitionFile.toString() + "#_partitions"); DistributedCache.addCacheFile(partitionUri, conf); DistributedCache.createSymlink(conf); JobClient.runJob(conf); return 0; } public static void main(String[] args) throws Exception { int exitCode = ToolRunner.run( new SortByTemperatureUsingTotalOrderPartitioner(), args); System.exit(exitCode); } }

使用上述程序执行所得的结果会是多个划分,每个划分内部是有序的,而且第i个划分的key值会比i+1个划分的key值都要小。这样,就可以不需要进行再一步的merge,就可以得到整体的上有序结果。

关 于排序,一个更加有意思的应用是所谓的Secondary Sort,亦即在保证第一个key值有序的情况下,对第二个key值也要保证有序(可以是升序或者降序)。此处的一个实现方法是将这两个需要排序的部分都 作为key值,使用IntPair进行存储,然后自己实现一个继承自WritableComparator的名为KeyComparator的用于比较的 类,其代码如下:

public static class KeyComparator extends WritableComparator { protected KeyComparator() { super(IntPair.class, true); } @Override public int compare(WritableComparable w1, WritableComparable w2) { IntPair ip1 = (IntPair) w1; IntPair ip2 = (IntPair) w2; int cmp = IntPair.compare(ip1.getFirst(), ip2.getFirst()); if (cmp != 0) { return cmp; } return -IntPair.compare(ip1.getSecond(), ip2.getSecond()); // reverse } }

这里对于第二列是得到降序的结果,在conf设置的时候,可以使用 conf.setOutputKeyComparatorClass(KeyComparator.class);语句进行设置。这样执行计算程序的话, 会存在一个问题,因为将两个int型的值共同作为key值来处理,在Map阶段结束后进行Partition的划分的时候,就会同样依照这个总key值进 行划分,我们想要两个值,比如(1900,20)和(1900,23)被放到同一个Reduce任务中进行处理就无法实现,于是我们需要实现自己的 Partitioner接口,代码如下:

public static class FirstPartitioner implements Partitioner<IntPair, NullWritable> { @Override public void configure(JobConf job) { } @Override public int getPartition(IntPair key, NullWritable value, int numPartitions) { return Math.abs(key.getFirst() * 127) % numPartitions; } }

同样在配置过程中使用conf.setPartitionerClass(FirstPartitioner.class);语句进行设置。除此之外,需 要进行控制的还有一个Reduce中的Group by操作,方法是实现一个GroupComparator类,其中的比较只使用第一个键值即可,代码如下:

public static class GroupComparator extends WritableComparator { protected GroupComparator() { super(IntPair.class, true); } @Override public int compare(WritableComparable w1, WritableComparable w2) { IntPair ip1 = (IntPair) w1; IntPair ip2 = (IntPair) w2; return IntPair.compare(ip1.getFirst(), ip2.getFirst()); } }

需要设置的是conf.setOutputValueGroupingComparator(GroupComparator.class);。这样就可以实现Secondary Sort过程了。

转载Asp.net防止盗链

mikel阅读(1358)

转载Asp.net防止盗链 – 钱李峰的博客 – 博客园.

今天在书上偶然看到了如何实现防盗链的问题,到网上查看了一下盗链主要是其他的一些网站引用本站图片或者下载链接。如果被引用的站比较小这样外站盗链带来 的“无作用”流量就给站长带来了压力了(流量也是要钱滴)。对于防盗链可以从两个方面来防,一个是服务器,一个是程序里面判断。各个服务器的判断不一 样,iis需要安装特定的防盗链软件。我们今天主要讨论的是程序里实现防盗链。

原理解释

网上现在比较流行的是使用handler来实现防盗链。具体的意思如下:专门新建一个针对某种文件请求的处理类(继承于IHttpHandler)并在 web.config里面配置好所有的该文件请求都指向该类。然后在类里面判断该请求的前一次请求是不是存在并且指向我们站的域名,如果存在则认为不是盗 链,返回真实的文件。否则返回error图片。

让我们设想一下如果现在另外一个网站引用了我们站的图片,并应用到了一篇文章当中。现在有个人请求那个网站的文章,用户的request是向他们的服务器 发出的,他们站的服务器返回html让浏览器解析。浏览器解析到我们的图片地址时他会向我们站发起这个图片的请求。因为我们程序的设置这个请求会被转发到 我们特定的类做处理,程序判断这个request前面的请求是不是为空(显然它只请求了我们的图片其他没有请求,所以当然没有前面的请求),为空则返回 error图片。理解了上面的过程就容易知道为什么请求我们站的那个网页时里面的图片则正确显示了,用户显示request了那个页面,所以里面当然有前 面访问的记录。当浏览器解析我们站的图片时候就正确返回了。不知道大家懂了没呢?

代码实现

首先创建一个类继承于IHttpHandler,我这里就叫做ForbiddenInvaliteDownload类了:

代码
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            if (null != context.Request.UrlReferrer)
            {
                context.Response.Expires = 0;
                context.Response.Clear();
                context.Response.ContentType = "image/jpg";
                context.Response.WriteFile(context.Request.PhysicalPath);  
                context.Response.End();
            }
            else
            {
                context.Response.Expires = 0;
                context.Response.Clear();
                context.Response.ContentType = "text/html";
                context.Response.Write("盗链");
                context.Response.End();
            }
        }

代码很少,大家一看就明白了。就是用context.Request.UrlReferrer这个判断一下前面一个请求是否存在,存在则认为是合法的,否则不合法。

仅仅是这个类还不能将所有的jpg请求转发过来,我们需要配置一下webconfig,在System.Web下配置:

代码
    <httpHandlers>
      <add verb="*" path="*.jpg" type="Namespace.ForbiddenInvaliteDownload,Namespace"/>
    </httpHandlers>

这里还要提到的是IIS默认是不会为JPG文件发送请求的,而是直接获取。所以我们还要在iis里面配置一下让所有的jpg请求都转发到我们的那个处理程序,而不是iis默认的直接获得。配置如图:

盗链

至此,你的网站已经可以防止jpg文件被盗了。当然,其他的zip等等文件同样也可以实现,您可以处理一个通用类,根据请求的后缀判断是什么类型从而进行操作。下面是效果图:

1

2

结束了吗

上面的方式并不能防止迅雷等下载软件的下载,在迅雷里面输入这些地址照样可以下载。如果别的站引用了你的某个.zip的文件链接,这样还是可以直接下载到的。那该如何解决呢?我目前想到的就是加入session验证。

如果用户访问了你的下载页面则在load里面设置session[“visited”]=”true”,然后在下载里面增加一个session验证,代码如下(注意,需要继承

System.Web.SessionState.IRequiresSessionState才能使用session):
代码
            if (null != context.Request.UrlReferrer && context.Session["visited"] == "true")
            {
                context.Response.Expires = 0;
                context.Response.Clear();
                context.Response.ContentType = "image/jpg";
                context.Response.WriteFile(context.Request.PhysicalPath);
                context.Response.End();
            }
            else
            {
                context.Response.Expires = 0;
                context.Response.Clear();
                context.Response.ContentType = "image/jpg";
                context.Response.WriteFile(context.Request.PhysicalApplicationPath + "Images/2.jpg");
                context.Response.End();
            }

这样我们调试的时候调用迅雷下载这个图片就可以发现session里面是没有值的,当然就下载error图片了。

[转载]中国标准书号校验码的计算方式(附C#代码)

mikel阅读(1129)

[转载]中国标准书号校验码的计算方式(附C#代码) – 枢木 – 博客园.

近段时间做了一个ERP项目,涉及到图书销售,于是熬了几个晚上研究了一下相关事情……抽空将一些内容梳理了一下。

首先了解一些必要的术语及定义:

—————————————
EAN•UCC前缀  EAN•UCC prefix
国际物品编码协会分配的产品标识编码。
—————————————
校验码  check digit
中国标准书号的最后一位,由校验码前面的12位数字通过特定的数学算法计算得出,用以检查中国标准书号编号的正确性。
—————————————
ISBN
国际标准书号英文International Standard Book Number的缩写,国际上通用的出版物标识编码的标识符。
—————————————
出版者  publisher
向中国ISBN 管理机构申请并获得出版者号的出版机构或组织。
—————————————
组区  registration group
由国际ISBN管理机构指定的,以国家、地理区域、语言及其他社会集团划分的工作区域。

=======================================

中国标准书号的结构:

—————————————
中国标准书号的构成
中国标准书号由标识符“ISBN”和13位数字组成。其中13位数字分为以下五部分:
1)EAN•UCC前缀
2)组区号
3)出版者号
4)出版序号
5)校验码
书写或印刷中国标准书号时,标识符“ISBN”使用大写英文字母,其后留半个汉字空,数字的各部分应以半字线隔开。如下所示:
ISBN EAN•UCC前缀-组区号-出版者号-出版序号-校验码
示例:ISBN 978-7-5064-2595-7
—————————————
EAN•UCC前缀
中国标准书号数字的第一部分。由国际物品编码(EAN•UCC)系统专门提供给国际ISBN管理系统的产品标识编码。
—————————————
组区号
中国标准书号数字的第二部分。它由国际ISBN管理机构分配。中国的组区号为“7”。
—————————————
出版者号
中国标准书号数字的第三部分。标识具体的出版者。其长度为2至7位,由中国ISBN管理机构设置和分配。
—————————————
出版序号
中国标准书号数字的第四部分。由出版者按出版物的出版次序管理和编制。
—————————————
校验码
中国标准书号数字的第五部分,也是其最后一位。采用模数10加权算法计算得出。
—————————————
出版者号的取值范围和出版量

出版者号设置范围
00 09
100 499
5000 7999
80000 89999
900000 989999
9900000 9999999

每一出版者号含有的出版量(与上面一一对应,如:00 09对应1000000)
1000000
100000
10000
1000
100
10

—————————————

10位数字中国标准书号校验码的计算:

代码

/// <summary> /// 10位数字中国标准书号校验码的计算。 /// <remarks> /// 10位数字中国标准书号校验码采用模数11的加权算法计算得出。 /// /// 数学公式为: /// 校验码 = mod 11 {11-[mod 11 (加权乘积之和)]} /// = mod 11 {11-[mod 11 (248)]} /// = 5 /// /// 以ISBN 7-5064-2595-5为例。 /// </remarks> /// </summary> /// <param name="barCode"></param> /// <returns></returns> public static string GetF10ISBN(string sCode) { string coreCode = sCode.Replace("-", ""); coreCode = coreCode.Substring(0, 9); int sum = 0; for (int i = 10; i > 1; i--) { // 从高位至低位,分别乘以(10-i) sum += i * Convert.ToInt32(coreCode.Substring((10 - i), 1)); } string checkCode = ""; if (sum % 11 == 0) { checkCode = "0"; } else if (sum % 11 == 1) { checkCode = "X"; } else { checkCode = (11 - (sum % 11)).ToString(); } return string.Concat(coreCode, checkCode); }

13位数字中国标准书号校验码的计算:

代码

/// <summary> /// 13位数字中国标准书号的校验码的计算。 /// <remarks> /// 13位数字中国标准书号的校验码采用模数10的加权算法计算得出。 /// /// 数学算式为: /// 校验码 = mod 10 {10 – [mod 10 (中国标准书号前12位数字的加权乘积之和)]} /// = mod 10 {10 – [mod 10 (123)]} /// = 7 /// /// 以ISBN 978-7-5064-2595-7为例。 /// </remarks> /// </summary> /// <param name="sCode"></param> /// <returns></returns> public static string GetF13ISBN(string sCode) { string coreCode = sCode.Replace("-", ""); coreCode = coreCode.Substring(0, 12); int oddSum = 0; //奇数和 int evenSum = 0;//偶数和 for (int i = 0; i < 12; i++) { if (i % 2 == 0) { evenSum += Convert.ToInt32(coreCode.Substring(i, 1)); } else { oddSum += Convert.ToInt32(coreCode.Substring(i, 1)); } } int sum = oddSum + evenSum * 3; string checkCode = null; if (sum % 10 == 0) { checkCode = "0"; } else { checkCode = (10 - (sum % 10)).ToString(); } return string.Concat(coreCode, checkCode); }

10位数字的中国标准书号转换为13位数字的中国标准书号:

代码

[转载]在Eclipse中的Android项目里实现代码复用

mikel阅读(912)

[转载]在Eclipse中的Android项目里实现代码复用 – SkyD – 斯克迪亚(徐明璐)个人博客 – 博客园.

引言

开发时通常都会有多个项目间代码复用的需求,这时通常的做法是设置项目依赖,让一个项目引用另一个项目,比如在Visual Studio中这样就很容易实现。

而在Eclipse中的Android项目里,如果要引用另一个项目,理论上应该也使用类似的办法:

SNAGHTML10cdf2c

这里是引用一个名为Core的项目。

这样做下来,在编写代码时不会有问题,但是在模拟器中运行时就会出现NoClassDefFoundError异常,原因是找不到引用项目中的类。

而通过此窗口中Libraries选项卡中的各种功能添加引用,通常得到的结果要么是编译不通过,要么也是和上面一样。

我在网上查了半天,看到很多人也都遇到此问题无解~

创建jar文件

看来Eclipse无法自动实现项目的整合,那么只能考虑先将公共项目输出为jar,再由需要的项目进行导入了。

由 于我这里的公共项目并不是一个Android常规项目,我只希望它充当一个公共的类库,提供一些通用功能,因此我将此项目创建为一个普通的Java项目, 然后再Libraries中去掉了Java-SE的引用,加入了Android.jar(这个请根据需要,从SDK中导入特定版本的 Android.jar文件),然后执行导出:

image

选择JAR文件类型:

image

下一步,这里设置保存路径:

image

下一步,再下一步,这里可以采用默认设置生成manifest文件,也可以自行设置指定的文件:

image

完成之后就导出完毕了。

导入jar文件

现在需要在要用到的项目中导入先前导出的jar文件,只需在Build Path中通过Add External JARs…功能选取先前导出的jar文件即可:

SNAGHTML11c454e

参考资料

http://blog.csdn.net/guanmjie/archive/2009/08/12/4437779.aspx

http://hi.baidu.com/gaogaf/blog/item/1757d309274b098cd1581bd7.html

[转载]用dynamic增强C#泛型表达力

mikel阅读(979)

[转载]用dynamic增强C#泛型表达力 – { Code Talk } – 博客园.

C++的泛型是基于模板(template)技术的。模板本身并不作为独立的编译单元,而是在编译时绑定实际参数类型进行模板实例化,类似与C语言 的宏展开,在运行时并不存在独立的模板类型。模板对泛型参数的约束是基于操作的语法特征的,属于一种静态的duck typing机制,十分灵活。

下面的代码定义了一个泛型Add函数,它对泛型参数T的要求只是支持+运算,并不要求T是某个类的子类,或是实现了某个接口。int, double, std::string等支持+运算符的类型都可以成功匹配T。

//C++

template<typename T>
T Add(const T& t1, const T& t2) {
return t1 + t2;
}

int main() {
int i = Add(1, 2);
double d = Add(1.1, 2.2);
std::string s = Add(std::string(“abc”), std::string(“def”));

std::cout << i << ” ” << d << ” ” << s << std::endl;

return 0;
}

输出:

>>3 3.3 abcdef

而类似的代码在C#中却无法编译通过:

csadd

这 是由于C#采用基于reification的泛型机制,泛型类会单独编译,并且在运行时存在;因此,C#对于泛型参数的要求更加严格,只能通过where 关键字表达基于继承关系的约束,无法通过duck typing的方式表达类型约束。与模板相比,这种机制的好处在于可以更好的支持反射和元编程,但其缺点是泛型的表达能力不如模板。幸好C#4.0中引入 了动态类型机制,我们可以通过动态类型来实现基于duck typing的泛型参数约束。

//C#

static class Calculator {
public static T Add<T>(T t1, T t2) {
dynamic d1 = t1;
dynamic d2 = t2;

return (T)(d1 + d2);
}
}

public static void Main(string[] args){
int i = Calculator.Add(1, 2);
double d = Calculator.Add(1.1, 2.2);
string s = Calculator.Add(“abc”, “def”);

Console.WriteLine(i + ” ” + d + ” ” + s);

}

输出:

>>3 3.3 abcdef

除了运算符重载,对于普通的方法调用也是适用的。这种方法是一种动态duck typing的泛型参数约束机制,依赖于运行时的方法查找,与模板编译时的检查不同,它需要使用者保证传入的对象符合相应要求。

参考:

wikipedia: Generic Programming

Dynamic Objects in C# 4.0

where (generic type constraint) (C# Reference)

[转载]通过源代码研究ASP.NET MVC中的Controller和View(五)

mikel阅读(1025)

[转载]通过源代码研究ASP.NET MVC中的Controller和View(五) – Ivony… – 博客园.

通过源代码研究ASP.NET MVC中的Controller和View(一)

通过源代码研究ASP.NET MVC中的Controller和View(二)

通过源代码研究ASP.NET MVC中的Controller和View(三)

通过源代码研究ASP.NET MVC中的Controller和View(四)

第五篇,从这一篇开始,将研究ASP.NET的Controller,IController接口是这个样子的:

  public interface IController
  {
    void Execute( RequestContext requestContext );
  }

IController是控制器的抽象,由资料可 知,当ASP.NET MVC捕获到HTTP请求时,便会通过一系列的机制确定处理当前请求的Controller,创建IController的实例来处理这个请求 (RequestContext)。在IController之前的东西,其实是个Routing,或者说请求分发。具体的分发机制与ASP.NET Routing相关,不在我这一次的研究范畴。我们现在假设已经通过分发处理来到了IController,来看看IController的实例是如何处 理请求的。

首先通过Reflector看这个接口的实现情况:

image

很干净的继承链,没有什么旁系和分支,IAsyncController和AsyncController这两个类型从名称来看已经知道大体上应该 是用异步处理实现的Controller或IController(就像是IHttpAsyncHandler),不妨看看 IAsyncController接口长啥样:

  public interface IAsyncController : IController
  {
    IAsyncResult BeginExecute( RequestContext requestContext, AsyncCallback callback, object state );
    void EndExecute( IAsyncResult asyncResult );
  }

显然事实就是这样,那么我们只需要关心同步处理的实现(Controller)便可以了,异步处理的逻辑不可能有很大的偏差。

按照一贯的传统,IController接口应该会被抽象基类ControllerBase实现,来看看:


    #region IController Members
    void IController.Execute( RequestContext requestContext )
    {
      Execute( requestContext );
    }
    #endregion

    protected virtual void Execute( RequestContext requestContext )
    {
      if ( requestContext == null )
      {
        throw new ArgumentNullException( "requestContext" );
      }
 
      VerifyExecuteCalledOnce();
      Initialize( requestContext );
      ExecuteCore();
    }

要说明一下这里兜了一个圈子,IController.Execute是一个显示接口实现,当我们将实例当作IController来调用时,会调用到这个方法,但旋即这个方法就调用了ControllerBase.Execute。那么来看Execute方法的实现。

VerifyExecuteCalledOnce,大意是验证Execute是否只被调用一次,一会儿来研究这个方法的实现。然后是初始化(Initialize),最后调用派生类的ExecuteCore方法(因为ExecuteCore是抽象方法)。

初始化的工作非常简单:

    protected virtual void Initialize( RequestContext requestContext )
    {
      ControllerContext = new ControllerContext( requestContext, this );
    }

从这里也能看出,ControllerContext = RequestContext + ControllerBase

同时我发现Initialize方法是个虚的,看看派生类是否有篡改,果然:

    protected override void Initialize( RequestContext requestContext )
    {
      base.Initialize( requestContext );
      Url = new UrlHelper( requestContext );
    }

不过逻辑也非常简单,也只是创建了一个UrlHelper的实例。Execute方法虽然也是虚的,但是Controller并没有篡改,而是老老 实实的实现了ExecuteCore。这个一会儿再看,先来研究一下这个VerifyExecuteCalledOnce的实现。话说研究源代码的好处就 在于你可以收获许多研究结论之外的东西:

    internal void VerifyExecuteCalledOnce()
    {
      if ( !_executeWasCalledGate.TryEnter() )
      {
        string message = String.Format( CultureInfo.CurrentUICulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType() );
        throw new InvalidOperationException( message );
      }
    }

调用了一个TryEnter方法,从方法名来看,似乎是进入一个什么状态?临界区?暂时不清楚这个方法和只调用一次的逻辑有什么关系,继续查看源代码:

    private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate();
  // used to synchronize access to a single-use consumable resource
  internal sealed class SingleEntryGate
  {
 
    private const int NOT_ENTERED = 0;
    private const int ENTERED = 1;
 
    private int _status;
 
    // returns true if this is the first call to TryEnter(), false otherwise
    public bool TryEnter()
    {
      int oldStatus = Interlocked.Exchange( ref _status, ENTERED );
      return (oldStatus == NOT_ENTERED);
    }
 
  }

_executeCalledGate是一个SingleEntryGate的实例,SingleEntryGate的代码也一并列出了。从名称和代码基本上已经可以搞清楚是怎么一回事儿了。

这里的Interlocked.Exchange方法其实就是赋值,只不过是一个原子操作(就是说这个操作只有完成和未完成两种状态,不存在进行中状态),你可以简单的理解为这样的伪代码:

      lock ( _status )
      { 
        oldStatus = _status;
        _status = ENTERED;
      }

当然这个代码是不正确的,因为值类型是不能被lock的,明白大体上是这个意思就行。

其实TryEnter方法上的注释已经写的非常明白了,意思是:如果TryEnter是第一次被调用,那么返回true,否则返回false。

当TryEnter方法第一次被调用时,oldStatus是_status没有被修改之前的默认值也就是0,而_status则会被修改为1(ENTERED),然后比较oldStatus和0(NOT_ENTERED)得到一个true的结果,从而实现这个功能。

那么ControllerBase的Execute逻辑已经清楚了,主要就干了两件事儿,确保Execute方法只被调用一次和准备ControllerContext,然后就把工作交给派生类的ExecuteCore:

    protected override void ExecuteCore()
    {
      // If code in this method needs to be updated, please also check the BeginExecuteCore() and
      // EndExecuteCore() methods of AsyncController to see if that code also must be updated.
 
      PossiblyLoadTempData();
      try
      {
        string actionName = RouteData.GetRequiredString( "action" );
        if ( !ActionInvoker.InvokeAction( ControllerContext, actionName ) )
        {
          HandleUnknownAction( actionName );
        }
      }
      finally
      {
        PossiblySaveTempData();
      }
    }

方法一开头的注释大体上是告诉开发人员不要忘了还有BeginExecuteCore和EndExecuteCore这回事儿(如果这个方法的代码 需要更新,也请检查AsyncController的Begin和EndExecuteCore方法,看看代码是否也必须更新)。

猜测一下,由于IAsyncController的入口不再是Execute,这样ExecuteCore也就不会被调用到,写在 ExecuteCore里面的逻辑就应当被写到Begin和EndExecute中去。同样的,ControllerBase的Execute也不会被执 行,这部分逻辑恐怕也要写在Begin和EndExecute里面,看了一下源代码,果然不出所料。因为源代码太长,也与今天的研究没啥关系。就不贴了。

看完了注释,接下来是尽可能的(?)加载TempData,最后又有一个尽可能的(?)保存TempData。暂时不明白这个Possibly是咩 意思,但加载和保存临时数据还是能明白的,应该就是像ViewState一样的东西,这个与主线逻辑无关,暂时不去探究其实现。

然后是从路由数据中找出actionName,接着InvokeAction,如果返回false(我猜是找不到Action),则处理未知Action。

逻辑非常简单,可以看出来这里又把工作外包给了ActionInvoker去干,总结一下ExecuteCore的逻辑就是:

  • 加载临时数据
  • 调用action
  • 保存临时数据

由于我要追溯的是主线逻辑,所以继续来看ActionInvoker.InvokeAction。ActionInvoker是一个属性:

    public IActionInvoker ActionInvoker
    {
      get
      {
        if ( _actionInvoker == null )
        {
          _actionInvoker = CreateActionInvoker();
        }
        return _actionInvoker;
      }
      set
      {
        _actionInvoker = value;
      }
    }
    protected virtual IActionInvoker CreateActionInvoker()
    {
      return new ControllerActionInvoker();
    }

兜了一个圈子,我发现ActionInvoker属性的类型是IActionInvoker,而默认实例是一个ControllerActionInvoker类型的。

IActionInvoker只有一个方法:

  public interface IActionInvoker
  {
    bool InvokeAction( ControllerContext controllerContext, string actionName );
  }

那么职责显然是通过actionName调用Action,IActionInvoker的实现类型情况如下:

image

AsyncControllerActionInvoker和IAsyncActionInvoker应该是异步版本,那么看看ControllerActionInvoker的实现:

    public virtual bool InvokeAction( ControllerContext controllerContext, string actionName )
    {
      if ( controllerContext == null )
      {
        throw new ArgumentNullException( "controllerContext" );
      }
      if ( String.IsNullOrEmpty( actionName ) )
      {
        throw new ArgumentException( MvcResources.Common_NullOrEmpty, "actionName" );
      }
 
      ControllerDescriptor controllerDescriptor = GetControllerDescriptor( controllerContext );
      ActionDescriptor actionDescriptor = FindAction( controllerContext, controllerDescriptor, actionName );
      if ( actionDescriptor != null )
      {
        FilterInfo filterInfo = GetFilters( controllerContext, actionDescriptor );
 
        try
        {
          AuthorizationContext authContext = InvokeAuthorizationFilters( controllerContext, filterInfo.AuthorizationFilters, actionDescriptor );
          if ( authContext.Result != null )
          {
            // the auth filter signaled that we should let it short-circuit the request
            InvokeActionResult( controllerContext, authContext.Result );
          }
          else
          {
            if ( controllerContext.Controller.ValidateRequest )
            {
              ValidateRequest( controllerContext );
            }
 
            IDictionary<string, object> parameters = GetParameterValues( controllerContext, actionDescriptor );
            ActionExecutedContext postActionContext = InvokeActionMethodWithFilters( controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters );
            InvokeActionResultWithFilters( controllerContext, filterInfo.ResultFilters, postActionContext.Result );
          }
        }
        catch ( ThreadAbortException )
        {
          // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
          // the filters don't see this as an error.
          throw;
        }
        catch ( Exception ex )
        {
          // something blew up, so execute the exception filters
          ExceptionContext exceptionContext = InvokeExceptionFilters( controllerContext, filterInfo.ExceptionFilters, ex );
          if ( !exceptionContext.ExceptionHandled )
          {
            throw;
          }
          InvokeActionResult( controllerContext, exceptionContext.Result );
        }
 
        return true;
      }
 
      // notify controller that no method matched
      return false;
    }

好家伙,大量的代码都在这里了,我们慢慢来分析。

跳过一开始的入口检查,首先是获取两个Descriptor,ControllerDescriptor和ActionDescriptor,如果 ActionDescriptor是null,那么返回false,由于ActionDescriptor是由FindAction方法返回,结合调用方 的行为,有理由相信这里的逻辑是找不到Action的话就返回false,return false上方的注释也佐证了这一点。

然后从ActionDescriptor获取FilterInfo,从方法名GetFilters来看,FilterInfo应该是一个筛选器的集合。

紧接着进入一个try块,下面的catch逻辑首先是忽略ThreadAbortException(这个对于HTTP处理程序要说是必须的,因为 Response.End或Redirect就会产生这个异常),接着其他任何异常都会被捕获,然后InvokeExceptionFilters,这里 应该是异常筛选器(关于所有的Filter的内容,主线逻辑完成后我会来做一个总结)。如果异常没有被异常筛选器处理 (ExceptionHandled),那么继续抛出,否则InvokeActionResult(猜测这个方法就是调用 ActionResult.ExecuteResult)。

核实InvokeActionResult这个猜测很简单,看看源代码:

    protected virtual void InvokeActionResult( ControllerContext controllerContext, ActionResult actionResult )
    {
      actionResult.ExecuteResult( controllerContext );
    }

OK,枝节不继续深入,看try里面的情况,首先是调用授权筛选器(InvokeAuthorizationFilters),如果筛选器有结果(推测多半是授权失败之类),那么执行这个结果(InvokeActionResult)。

如果授权部分没有任何结果,那么看看Controller.ValidateRequest是不是true,决定是否进行ValidateRequest,这个ValidateRequest应该是检查XSS威胁之类的,实现如下:

    internal static void ValidateRequest( ControllerContext controllerContext )
    {
      if ( controllerContext.IsChildAction )
      {
        return;
      }
 
      // DevDiv 214040: Enable Request Validation by default for all controller requests
      // 
      // Note that we grab the Request's RawUrl to force it to be validated. Calling ValidateInput()
      // doesn't actually validate anything. It just sets flags indicating that on the next usage of
      // certain inputs that they should be validated. We special case RawUrl because the URL has already
      // been consumed by routing and thus might contain dangerous data. By forcing the RawUrl to be
      // re-read we're making sure that it gets validated by ASP.NET.
 
      controllerContext.HttpContext.Request.ValidateInput();
      string rawUrl = controllerContext.HttpContext.Request.RawUrl;
    }

果然,HttpContext.Request.ValidateInput()。最后的那个rawUrl赋值并不是闲着蛋疼的,上面的注释说了这个原因,大意是:如果不获取RawUrl的值,那么请求验证其实不会真正的被执行,可以认为这是ASP.NET的一个Bug。

继续研究,ValidateRequest之后,调用GetParameterValues方法来获取一个IDictionary<string, object>,这个从名称上来看是获取参数。

然后InvokeActionMethodWithFilters,接着InvokeActionResultWithFilters

InvokeActionResultWithFilters看起来就是InvokeActionResult的WithFilters版本:

    protected virtual ResultExecutedContext InvokeActionResultWithFilters( ControllerContext controllerContext, IList<IResultFilter> filters, ActionResult actionResult )
    {
      ResultExecutingContext preContext = new ResultExecutingContext( controllerContext, actionResult );
      Func<ResultExecutedContext> continuation = delegate
      {
        InvokeActionResult( controllerContext, actionResult );
        return new ResultExecutedContext( controllerContext, actionResult, false /* canceled */, null /* exception */);
      };
 
      // need to reverse the filter list because the continuations are built up backward
      Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate( continuation,
          ( next, filter ) => () => InvokeActionResultFilter( filter, preContext, next ) );
      return thunk();
    }

相当的复杂,但我们看到的确是调用了InvokeActionResult,其他的代码大体上是筛选期的逻辑,这些在以后再铺展来谈。我们还是看看 InvokeActionMethodWithFilters是不是也调用了InvokeActionMethod然后应用筛选器的逻辑:

    protected virtual ActionExecutedContext InvokeActionMethodWithFilters( ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters )
    {
      ActionExecutingContext preContext = new ActionExecutingContext( controllerContext, actionDescriptor, parameters );
      Func<ActionExecutedContext> continuation = () =>
          new ActionExecutedContext( controllerContext, actionDescriptor, false /* canceled */, null /* exception */)
          {
            Result = InvokeActionMethod( controllerContext, actionDescriptor, parameters )
          };
 
      // need to reverse the filter list because the continuations are built up backward
      Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate( continuation,
          ( next, filter ) => () => InvokeActionMethodFilter( filter, preContext, next ) );
      return thunk();
    }

这两个方法的代码几乎是如出一辙(似乎问到了DRY的味道)。好,暂且不管复杂的Filter逻辑,我们赶紧来总结一下ActionInvoker.InvokeAction的流程:

  • 获取Controller的描述(Descriptor)
  • 查找Action(FindAction)
    • 如果找不到Action,那么返回false
  • 获取所有的筛选器
  • 进入try
    • 调用授权筛选
      • 如果授权筛选有结果,那么调用授权结果(猜测是授权失败之类)。
    • 获取参数
    • 调用ActionMethod(InvokeActionMethodWithFilters)
      • InvokeActionMethod
    • 调用ActionResult(InvokeActionResultWithFilters)
      • InvokeActionResult
        • ActionResult.ExecuteResult()
  • 如果try块内有任何不是ThreadAbortedException的异常
    • 调用异常筛选

如果我们把这些筛选的逻辑都去掉,则看起来像是这样:

  • 查找Action(FindAction)
  • 获取参数
  • InvokeActionMethod
  • InvokeActionResult

这里面 InvokeActionResult我们已经知道是干什么的了,而InvokeActionMethod从名称上来看应该是调用我们写在 Controller里面的被称之为Action的方法(例如HomeController.Index等),结合起来上面的 GetParameterValues方法就应该是获取这个方法的参数。

[转载]jQuery boxy弹出层对话框插件扩展应用--弹出层选择器

mikel阅读(979)

[转载]jQuery boxy弹出层对话框插件扩展应用–弹出层选择器 – 挫折中成长 – 博客园.

当大家进行复杂功能设计的时候,在对多级联选择进行设计,为了获得更好的用户体验和节省页面空间,往往会使用弹出层的方法。我们使用热门的JQuery进行设计,同时我们选择效果比较优秀的boxy弹出插件进行扩展(关于boxy的相关资料,请参照张鑫旭博客http://www.zhangxinxu.com/wordpress/?p=318)。下面介绍boxy作为选择器框架的应用。

对于选择器,相信用过招聘网站的人都不会陌生(就是那个点击就弹出的,选择行业、职位和地区的东西),选择器难点就在于样式调试,主要针对的是IE6。这里介绍行业、职位和地区选择器,下载的Demo包含这三个选择器。

行业选择器:无关联,调用语句为Boxy.industry(value, callback, options),参数value为已选定值编号集合(字符串类型,编号间以逗号分割),callback为可定义回调函数,传入callback的值为 行业选择器选中项的编号集合(字符串类型,编号间以逗号分割),options为boxy插件可选参数。

调用例子:选择编号为1和2的行业,并且定义选择器标题为”行业类别选择器”

1 $(#industry).click(function() {
2 Boxy.industry(1,2, function(val) {
3 alert(你选择的是: + val);
4 }, { title: 行业类别选择器 });
5 return false;
6 });

职位选择器:二级联,调用语句为Boxy.job(value, shown, callback, options),参数value为已选定值编号集合(字符串类型,编号间以逗号分割,以b开头表示选择职位大类,以s开头表示选择职位小类),参数 shown表示展示的职位大类的编号,参数callback为可定义回调函数,传入callback的值为职位选择器选中项的编号集合(字符串类型,编号 间以逗号分割),options为boxy插件可选参数。

调用例子:选择编号为1的职位小类,编号为2的职位大类,定义选择器的名称为职位类别选择器

1 $(#job).click(function() {
2 Boxy.job(s1,b2, 2, function(val) {
3 alert(你选择的是: + val);
4 }, { title: 职位类别选择器 });
5 return false;
6 });

职位选择器:三级联,调用语句为Boxy.area(value, shown, callback, options),参数value为已选定值编号集合(字符串类型,编号间以逗号分割,以p开头表示选择省份,以c开头表示选择城市,以d开头表示选择县 区),参数shown表示展示的地区的编号,参数callback为可定义回调函数,传入callback的值为地区选择器选中项的编号集合(字符串类 型,编号间以逗号分割),options为boxy插件可选参数。

调用例子:选择编号为1和2的县或区,定义选择器的名称为工作地区选择器

1 $(#city).click(function() {
2 Boxy.area(d1,c7, 1,724, function(val) {
3 alert(你选择的是: + val);
4 }, { title: 工作地区选择器 });
5 return false;
6 });

存在Bug:

1.IE6 checkbox的margin设置会走样,IE6的checkbox边框清空是无效的,但对其它浏览器是有效的,而且很多人喜欢全局设定input的margin/padding为零,为了统一样式,唯有不管IE6了。

2.IE8 环境下,css设置选项<a>的hover效果迟钝甚至没反应,其它浏览器不存在这个问题(包括IE6),js代码解决也不是可取之道,代码hover反应还是有点慢。实在搞不懂IE8为什么会出现这种问题,知道的那位告诉我吧。

3.IE6下超长的选中项,显示的区域不够,在父标签中不会自动换行,而是在显示的选中项自身内部换行输出文字,造成样式走样。这个问题我也不知道怎么解决,谁知道告诉我吧。

4.暂时还没发现(可能大家会说IE6下确定按钮的hover效果,这个也不是什么bug,只是无关紧要的我懒得改,把标签该<a>就行了,不过实在不喜欢<a>,整天为IE6做无用功)。

优点:

1.当然是漂亮呗!

2.三个选择器分别表示三种级联关系的选择器,大家可以直接修改Demo中的数据源,及主体框架中的文字,即可变作其它的选择器。

3.还有改进的空间,谁去把demo的css样式整合一下,发上来吧。

悄悄告诉大家,选择器的外框圆角效果是使用png图片实现的,如果想更改透明程度,唯有重做图片了,如果愿意撇开IE6的话,可以使用另一种常 用的圆角方法(不用图片只用css样式),方法已经写进boxy插件当中了(被我注释掉,包括样式文件),还有一种圆角方法在demo的round- corner.html文件中也介绍了,ExtendedBoxy.html为选择器的demo文件。其它的两个关于Boxy的html文件可是张鑫旭同 学写的哦,张同学博客http://www.zhangxinxu.com/php/上可是有很多好东西的,大家有空上去看看吧。

Demo下载地址:/Files/butongren/jquery-plugin-ExtendedBoxy.rar

[转载]打造自己的JQUERY柱状图插件JQUBar

mikel阅读(1042)

[转载]打造自己的JQUERY柱状图插件【JQUBar】 – RyanDing – 博客园.

一、JQUBAR(V1.0)JQuery插件简介

1.支持.net、java、php等平台。

2.用户可以通过鼠标拖拽柱状图从而改变每根柱子的高度,最终达到通过鼠标拖拽图形界面来修改服务器数据的目的。

3.支持柱状图缩放。

4.目前支持浏览器:IE7、 IE8、 Firefox、Chrome。

二、HTML

1<div id="con"><%--JQUBAR容器--%> 2 </div> 3<input type="checkbox" id="cbZoom" checked="checked" /> 4 <label for="cbZoom">缩放</label> 5<input type="checkbox" id="cbDragable" checked="checked" /> 6<label for="cbDragable">拖拽</label> 7 <br /> 8姓名模糊查询:<input type="text" id="txtName" /> 9<br /> 10<input type="button" id="btnReloadBar" value="重新加载" />

截图如下:

三、JavaScript及CSS 文件引入

1 <script src="<%=Url.Content("~/Scripts/jquery-1.4.1.js")%>" type="text/javascript"charset="utf-8"></script> 2  <script src="<%=Url.Content("~/Scripts/JQUBar/wz_jsgraphics.js")%>" type="text/javascript"></script> 3 <script src="<%=Url.Content("~/Scripts/JQUBar/jquery-ui.min.js")%>" type="text/javascript"></script> 4  <script src="<%=Url.Content("~/Scripts/JQUBar/JQUBar.js")%>" type="text/javascript"></script> 5 <link href="<%=Url.Content("~/Scripts/JQUBar/JQUBar.css")%>" rel="stylesheet" type="text/css" />

注:以上文件请引入至html <head></head>内。

四、加入JavaScript 代码

1 <script type="text/javascript"> 2 $(function () { 3 $("#con").jQUBar({ 4 zoom: true, 5 drag: true, 6 url: '<%=Url.Action("LoadData") %>' 7 }); 8 9 $("#btnReloadBar").click(function () { 10 $("#con").setBarParam({ 11 zoom: $("#cbZoom").attr("checked"), 12 drag: $("#cbDragable").attr("checked"), 13 //提供json数据,方便 .net java php 调用。本例在Asp.net MVC2.0下演示 14   url: '<%=Url.Action("LoadData") %>/?name=' + $("#txtName").val() 15 }).reload(); 16 }); 17 }); 18 </script>

注:以上js脚本同样请加入html <head></head>内。

五、ASP.NET MVC2.0 服务端代码

1 private decimal[] GetPricesByEmployeeId(int employeeId) 2 { 3 decimal[] result = null; 4 result = _Context.Orders.Where(o => o.EmployeeID == employeeId) 5 .Take(5) 6 .Select(oo => (decimal)oo.ShipVia).ToArray(); 7 return result; 8 } 9 10  public JsonResult LoadData(string name) 11 { 12 13 var data = (from e in _Context.Employees.Take(10).ToList() 14 select new 15 { 16 EmployeeID = e.EmployeeID, 17 Orders = GetPricesByEmployeeId(e.EmployeeID), 18 Name = e.FirstName, 19 }).Distinct(); 20 21 if (!string.IsNullOrEmpty(name)) 22 { 23 data = data.Where(d => d.Name.IndexOf(name) >= 0); 24 } 25 26 return Json(new { Success = true, Msg = data }, JsonRequestBehavior.AllowGet); 27 }

注:为方便阅读使用NORTHWIND 数据库。

六、程序运行截图

缩放:

移动:

最后:由于时间仓促未能对JQUBAR1.0插件系统测试,如果您感兴趣可以在这里下载JQUBAR1.0插件。

衷心的感谢各位园友对该插件提出您的宝贵意见,根据大家的意见我将抽出时间对JQUBAR1.0插件进行升级。

同时也希望本篇文章可以帮您解决开发中碰到的问题。