求助asp.net获得远程sql服务是否启动?

扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
基于ASP.NET+SQL校友录网站设计与实现—免费毕业设计论文
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口& &在学习ASP.NET 过程用到新建数据集并远程连接sql server 2008 数据库,出现下面的错误:
& & &--在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。& & 未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 & & (provider: SQL Network Interfaces, error: 26 - 定位指定的服务器/实例时出错)--&
& &因为sql server 2008默认是不允许远程连接的,sa帐户也是默认禁用的,如果想要在本地用SSMS(SQL Server Management Studio Express)
& &连接远程服务器上的SQL Server 2008,需要做两个部分的配置:
& & & 1,SQL Server Management Studio Express(简写SSMS)
  2,SQL Server 配置管理器/SQL Server Configuration Manager(简写SSCM)
下面是具体的操作步骤:
(1:& &打开SSMS,用windows身份连接数据库,登录后,右键选择&属性&
& & & & &左侧选择&安全性&,选中右侧的&SQL Server 和 Windows 身份验证模式&以启用混合登录模式
(2:选择&连接&,勾选&允许远程连接此服务器&,然后点&确定&
&(3:展开&安全性&-&&登录名&-&&sa&,右键选择&属性& &,然后左侧选择&常规&,右侧选择&SQL Server 身份验证&,并设置密码;选择&状态&,选中&启用&,点击&确定&
(4:右击数据库选择&方面&,将&RemoteAccessEnabled&属性设为&True&,点&确定&
(5:至此SSMS已设置完毕,先退出,再用sa登录,成功即表示sa帐户已经启用
   & 下面开始配置SSCM,打开SSCM:
& & & & & & & & & & & & & & &&& & & & & &
(6:选中左侧的&SQL Server服务&,确保右侧的&SQL Server&以及&SQL Server Browser&正在运行
(7:右侧的TCP/IP:双击打开设置面板将其修改为&Enabled& ,
& & & & 选择&IP Addersses&选项卡,设置端口&1433&,
& & & & 将"Client Protocols"的"TCP/IP"也修改为&Enabled&
(8:配置完成,重新启动SQL Server 2008,如果是visual studio 2010 则是:
(9:此时应该可以使用了,但是还是要确认一下防火墙:打开防火墙设置。将SQLServr.exe(C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\Binn\sqlservr.exe)添加到允许的列表中。
阅读(...) 评论()Chrome是个非常好用的浏览器,拥有丰富的扩展资源库,能够满足网民各种各样的需求,对于网民来说,通过Chrome扩展来增强上网体验是 一个基本需求,但是安装过多的扩展有容易耗费大量系统资源,今天月光博客就给大量挑选一些常用的上网增强类Chrome扩展,供大家参考。
:用于管理大量的密码,给不同设置不同的密码,支持自动登录,支持手机两步验证。建议在普通和隐身模式下都启用这个扩展。
:广告屏蔽扩展,可以屏蔽不少的广告,包括屏蔽Youtube视频广告、Facebook广告、横幅广告以及其他广告,还可以自定义规则,在网络广告泛滥的年代里,可以做到眼不见心不烦。该扩展是一个社区驱动的开源项目 ,有不少志愿者为其作出了贡献。
:Google提供的网页截图工具,该扩展可以轻松截取当前标签页的可见区域,当前网页的指定区域,或是整张网页的页面,截图后,可以利用图片编辑工具编辑图片,然后将编辑后的图片保存为PNG格式的图片文件。
:显示Google Mail收件箱中的未读邮件数,点击该按钮还可以打开Gmail收件箱。
:Google提供的显示网站的RSS地址的扩展,可以在工具栏上显示RSS图标,用于一键订阅。
:RSS阅读器网站Feedly自带扩展。
:自动翻页扩展,在Google搜索网页或浏览论坛帖子时,可以自动翻页并将每一页的内容接在现有内容的末尾,支持自定义规则。
:设置浏览器的UserAgent,将浏览器伪装成为其他系统的浏览器,例如iPhone等。
:EPUB阅读扩展,支持在Chrome里阅读EPUB书籍。
:天朝必备,用于安全上网的扩展,你懂的。
:这个工具其实主要用于科学上网,遇到看不了的文章,点一下,就可以在Readability里面看到。
:在Chrome标签页中以IE内核显示网页,平时没啥用,使用中国的网银系统时必备。
:Google提供的举报垃圾网页的工具,可以举报Google搜索结果里的垃圾网页,提升Google搜索质量和搜索体验。
:这是Chrome上的Greasemonkey(油猴),Tampermonkey通过加载第三方的脚本文件,可以让Chrome支持更多UserScript的Chrome 扩展,例如改变页面中的CSS和JS元素,或在网页中增加额外的功能等,高手必备。
雷军一直强调,小米非常注重用户参与感,很多企业都在学习做好用户参与感,有那么一段时间,小米思维火到烂大街的程度,不管做个什么都讲小米思维。卖茶叶怎么做用户参与感呢?乡土乡亲CEO赵翼也推崇雷军参与感的方法论,而且确实在用户参与度上玩出了花样,不少大佬买他的帐。赵翼目前将参与感的重心放在沉淀用户上,淡化本身商业色彩,并且取得不错效果。卖茶叶的参与度究竟怎么玩呢,赵翼是这么向品途记者讲述的:
微信服务号受到一些限制,没办法每天给用户发信息,和用户的互动怎么完成,内容从哪里来,是一个蛮大的挑战。服务号虽然开通了支付,但是和用户关系的建设,需要建构一个新的情感关系。
用户微信自发组织茶会
早年的时候我们办过很多茶会,有很多明星参加。但是那时候是我们主办,我们找场地、邀请人、主持,投入很多资源做这个事情,但是做出来遇到很多问题,新的潜在的来参加活动的人哪里来。现在微信来做就不一样了,我们发起了城市茶会,我们把自己做小,不是乡土乡亲的茶会,而是城市茶会。像张小龙讲的,少用微信,多和朋友见见面,用一杯好茶的名义去认识这个城市中的新朋旧友。让用户自动发起这个茶会,用户发起,他来写招募,他来选地方,茶品是我们赞助的,他在我们微信平台发起之后,我们传出去,一方面接受我们用户的报名。我们还是一个相对新创的品牌,比如今天我们在深圳有一个茶会,那些用户本身还是少数,茶会让用户有一个交流的机会。我们今年的计划是在全球500个城市办500场,现在大概6、7十场。把自己变小,让用户参与,能自己不干的坚决不干。茶会给我们带来了几个好处,那些参加的朋友也都关注了我们的微信,每期我们都会建一个微信群,原本是一个人发起,但是他会带领身边2、3十个人来参与,这就是用户的力量,其实我们没有付出多少成本。我们不去会场,把东西寄过去就可以。我们有一个茶会的包,有夹层,第一层里面有二维码,有茶会的秘籍,往年办茶会的视频,整个流程,当然也有一些宣传,用户会给我们反馈回来一些照片、新闻。怎么让他们加我们的微信号呢?报名是要通过微信的,我们活动是从微信上发起的,必须从微信上填一个表才能参与活动,每场茶会我们都有一个群,就会把他拉进来。茶会现场我们的角色是比较淡的,不希望去讲我们品牌,但是都在一个群里面,活动办之前就会有很多我们信息传递给他们,各种好玩的信息,让他们对这个品牌有好感。
封测要满足用户自我肯定
我们遇到一个挑战,是一款台湾的乌龙茶,有两种工艺,一种是清香的,一种是碳焙的,无法确定到底做哪一个口味,没有数据来支撑这个东西。我们发起了活动,寻找99位美食的大众评审,不见得对茶有多了解,但是我们说是吃货、老饕。封测包是免费的,但是不能让用户感觉到他是贪小便宜才过来的,所以要创造一个特殊的心理收入。我们的门槛是你要回答几个问题,类似一句话说明你是吃货这种,这样他会觉得我是被你认可,我是个美食家,参与到你的封测当中。我们封测有非常严谨的一张督查笔记加封测表,他就会很有意识感,很正经找个日子,找朋友来品这个茶,打完分会在朋友圈晒给我们这个评价,我们依据这个来判断到底用哪一款茶。我们每一款茶上市前都会有封测。我也去参加过一个非常有名的餐饮品牌的封测,核心做卤味的业务,那天吃了大概50种卤味,坦率讲味道到底怎么样,我都已经忘掉了。但是我自己感觉是,从那一刻开始,我就进入京城美食家的节奏,这件事情我会讲10年,对于消费者而言,就是这样子。雷军的整个方法论,灵魂就是参与感。这个品牌和我有关,我们很多用户,朋友圈里面有一半的内容是我们。我们还有志愿者,愿意奉献时间来为我们搞活动。
鲜活故事带来口碑传播
另外还有一件超酷的事情,我们叫土人世界天使会员专访。我们会员年费2999,他所获得的东西是不确定的,我只告诉他一年8个节气当中我会给你寄茶,到底什么茶不知道,会给你很多特殊权利的。当时招募叫史上最土的会员招募。后来陆陆续续发现,我们会员来源都是会员带会员,很清楚就是我们怎么让用户参与到我们品牌建设中。传统来说我请梁朝伟,我很牛,很有钱,请了梁朝伟,所以你信任我们。现在不一样了,梁朝伟又如何,电视上都是明星,我信谁呢,谁都不信,而是说那些真实鲜活怒放的生命才能真正发挥出力量,如何把他们的力量发挥出来,所以我们做了天使会员专访。今年会做1000期,现在做了十几期。我们的会员故事是什么,他们为什么选择乡土乡亲,昨天那个女会员搞过黑社会,前天那个会员开过轰炸机,每个人都有自己的故事,还有一个原来是中央电视台的记者编导,她自己说我做这么多年记者,这是第一次被人家采访。对很多人而言,作为一个普通用户,被品牌重视、热爱、倾听,是个很罕见的事情,他就会在朋友圈甚至很多地方去发。我相信在他背后有10个甚至20个跟他有同等收入水平、审美趣味的人,他说一句比我们说十句都管用,这就是我们的逻辑。
碉堡奔放派茶园游学
清明节前后,有很多茶的品牌组织用户或者媒体去茶园活动,我们这个活动是史上最碉堡的茶园游学。最近做传播是,你想喝他为你采的茶吗?比如你想喝雕刻时光CEO为你采的茶吗,想喝吴稼祥为你采的茶吗,前提是你要去微信后台喊:我爱吴稼祥,我爱谁谁谁,然后他采完之后在迷恋他的粉丝里挑哪几个送。车旅他们自理,我们只负责当地地陪,他们不是给我们付费的公关活动。要把他们吸引过来,就得有好的活动,本身还是内容。
互动需要调戏与被调戏
我们还给会员送猪肉。我们去中科院送了一头猪,把它全程透明溯源,谱系、整个生长履历搞得极清楚。这件事情本身就是超用户期望的事情,后来会员吃猪肉极其有意识感。它是一个社群,社群最重要的是爱与恨,旗帜鲜明地喜欢什么反对什么。有一天我微信代班,后台有人说老赵你唱个歌吧,本来别人只是调侃,后来我们真的去唱歌了,一下午都变成点歌台。第二天我们还去旁边一个专门的录音棚录了一个demo。罗胖的魅力人格体验我是很认同的,就是玩,调戏与被调戏。
1. 手机需要先获得root权限。一种是否获得root权限的检验方法:安装并打开终端模拟器(可通过安卓市场等渠道获得)。在终端模拟器界面输入su并回车,若报错则说明未root,若命令提示符从$变#则为rooted;
2. 如果手机尚未root,可通过superoneclick或其它方法进行root处理(需要先安装Microsoft .NET Framework)。Superoneclick刷root权限教程:(
3. 需要先获得 Android SDK
4. 需要获得tcpdump软件,获取地址()
1. 将Android手机与电脑USB相连,打开windows命令提示符窗口
2. 将tcpdump程序copy至android手机(该命令前面那个目录文件为本地地址,后面那个目录为目的手机端地址)
C:\android-sdk-windows\platform-tools&adb push c:/tcpdump /data/local/tcpdump
3. 修改tcpdump的权限
C:\android-sdk-windows\platform-tools&adb shell
#chmod 777 /data/local/tcpdump
4. 进入root权限
C:\android-sdk-windows\platform-tools&adb shell
在运行su指令后,手机终端桌面会出现相应提示信息以确认您对root操作的认可。
5. 运行tcpdump,输入以下命令启动抓包。
/data/local/tcpdump -p -vv -s 0 -w /sdcard/capture.pcap
6. 在手机端执行相应需要进行抓包分析的操作,执行完成后在命令提示符窗口执行Ctrl+C中断抓包进程
7. 将抓包结果复制至本地(前面那个目录为手机端地址,后面那个目录为本地地址)
C:\android-sdk-windows\platform-tools&adb pull /sdcard/capture.pcap c:/
8. 使用Wireshark等工具查看抓包文件capture.pcap
问题描述:
开发过程中,遇到了在listview里面的每个item都有可能显示图片,并且需要显示的图片的数量不确定,需要自动换行。
如图:第一行显示三张图片,第二行显示四张图片。数量0—正无穷(内存支持的情况下)
解决办法:
最初就是直接从网上找自动换行的控件,再此感谢eoe论坛里** **分享的源码。
   eoe论坛原文地址:
  但是我直接拿来用的时候出现了个问题,就是当有四张图片的时候只显示三行。
具体解决办法如下:
a.修改values下attrs.xml文件
增加一个每行显示多少列的属性,类似gridview
&resources&
&declare-styleable name=&FlowLayout&&
&attr name=&horizontalSpacing& format=&dimension& /&
&attr name=&verticalSpacing& format=&dimension& /&
&attr name=&numColumns& format=&integer& /&&!--这个属性为新加的--&
&/declare-styleable&
&declare-styleable name=&FlowLayout_LayoutParams&&
&attr name=&layout_breakLine& format=&boolean& /&
&attr name=&layout_horizontalSpacing& format=&dimension& /&
&/declare-styleable&
&/resources&
  b.修改FlowLayout.java 源文件
    1.在构造方法里得到用户在cml文件里设置的numColumns
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
numColumns = a.getInt(R.styleable.FlowLayout_numColumns, 3);//得到用户在布局文件中设置的没行显示的列数
} finally {
a.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xffff0000);
mPaint.setStrokeWidth(2.0f);
2.修改onMeasure方法,由于对这块还不太了解,所以只是简单的改了一下,测试之后效果是实现了,暂未发现其他问题。应该还有更好的解决方案,希望有人指正。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingLeft();
int currentHeight = 0;
boolean breakLine =
boolean newLine =
int spacing = 0;
final int count = getChildCount();
for (int i = 0; i & i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
spacing = mHorizontalS
if (lp.horizontalSpacing &= 0) {
spacing = lp.horizontalS
if (growHeight && (i == numColumns||breakLine || currentWidth + child.getMeasuredWidth() & widthSize)) {
height += currentHeight + mVerticalS
width = Math.max(width, currentWidth - spacing);
currentHeight = 0;
currentWidth = getPaddingLeft();
if (i&numColumns&&i%numColumns==0) {//主要修改的是这个判断语句,原版的判断语句是29,38,39,40行的被隐掉的。我自己用原版的判断语句有问题,
lp.x = currentW
currentWidth += child.getMeasuredWidth() +
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
breakLine = lp.breakL
if (!newLine) {
height += currentH
width = Math.max(width, currentWidth - spacing);
width += getPaddingRight();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec),
resolveSize(height, heightMeasureSpec));
至此,就修改完毕了。
具体使用办法如下。
a.在listview的item布局文件中使用framelayout自定义控件
&?xml version=&1.0& encoding=&utf-8&?&
&LinearLayout xmlns:android=&/apk/res/android&
xmlns:f=&/apk/res/你的androidmanifest.xml文件中的package属性值&
android:layout_width=&fill_parent&
android:layout_height=&wrap_content&
&com.xingyunhudong.view.FlowLayout
android:id=&@+id/flowlaytou&
android:layout_width=&fill_parent&
android:layout_height=&wrap_content&
android:layout_marginTop=&8dp&
     
f:numColumns=&3&&!--也可以不指定,如果不指定,在FrameLayout的构造函数里,默认取值为3列--&
&/com.xingyunhudong.view.FlowLayout&
&/LinearLayout&
b.在adapter中设值
public class XXXAdapterextends BaseAdapter {
private LayoutI
private List&XXX& xxxL
private ViewGroup.LayoutParams paramsImg, paramsV
public HuaTiAdapter(Context context, List&xxx& xxxList) {
this.xxxList = xxxL
this.context =
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
sw = CommonUtils.getScreenWidth((Activity) context);
int w = context.getResources().getDimensionPixelSize(
R.dimen.xxx_img_total_width);
paramsImg = new ViewGroup.LayoutParams(sw / 3, sw / 3);
paramsVideo = new ViewGroup.LayoutParams(sw, 0);//为了图片适配
public int getCount() {
// TODO Auto-generated method stub
return xxxList.size();
public Object getItem(int position) {
// TODO Auto-generated method stub
return xxxList.get(position);
public long getItemId(int position) {
// TODO Auto-generated method stub
class ViewHolder {
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder =
xxxBean huati = xxxList.get(position);
if (convertView == null
|| ((ViewHolder) convertView.getTag()).flag != position) {//第一个判断是优化listview加载速度及内存消耗,第二个判断是为了防止图片错位
holder = new ViewHolder();
holder.flag =
convertView = inflater.inflate(R.layout.xxx_item_layout, null);
holder.ll = (FlowLayout) convertView.findViewById(R.id.flowlaytou);
ImageBean video = xxx.getVideoImg();
if (video != null && video.getUrl() != null
&& !&&.equals(video.getUrl().trim())) {
paramsVideo.height = video.getHeight() * sw / video.getWidth();
addVideoView(holder.ll, video.getUrl(), paramsVideo, inflater);
List&ImageBean& imgList = huati.getImgList();
if (imgList != null && imgList.size() & 0) {
for (int i = 0; i & imgList.size(); i++) {
addImageView(holder.ll, imgList.get(i).getUrl(), paramsImg,
inflater, imgList, i);
convertView.setTag(holder);
holder = (ViewHolder) convertView.getTag();
return convertV
private void addVideoView(FlowLayout ll, final String url,
LayoutParams params, LayoutInflater inflater) {
ImageView v = (ImageView) inflater.inflate(
R.layout.yyy_image_layout, null);//这个layout里面就只有一个imageview空间,特别简单
v.setLayoutParams(params);
ImageUtil.display(url, v);
v.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
//这里的点击事件也完全没有问题,不会错位,不会点击失效
ll.addView(v);
private void addImageView(FlowLayout ll, String url, LayoutParams params,
LayoutInflater inflater) {
// TODO Auto-generated method stub
ImageView v = (ImageView) inflater.inflate(
R.layout.weixiu_image_layout, null);
v.setLayoutParams(params);
ImageUtil.display(url, v);
v.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
//这里的点击事件也完全没有问题,不会错位,不会点击失效
} }); ll.addView(v); } }
至此,就就结束了,希望可以帮到一部分人。
Web API调用请求的目标是定义在某个HttpController类型中的某个Action方法,所以消息处理管道最终需要激活目标 HttpController对象。调用请求的URI会携带目标HttpController的名称,该名称经过路由解析之后会作为路由变量保存到一个 HttpRouteData对象中,而后者会被添加到代表当前请求的HttpRequestMessage对象的属性字典中。 Web API据此解析出目标HttpController的类型,进而实现针对目标HttpController实例的激活。[本文已经同步到《》]
一、程序集的解析
二、HttpController类型的解析
1、DefaultHttpControllerTypeResolver
2、HttpController类型的缓存
三、HttpController的选择
1、DefaultHttpControllerSelector
2、获取目标HttpController的名称
3、建立HttpController名称与HttpControllerDescriptor之间的映射
4、根据请求选择HttpController
四、HttpController的创建
1、HttpControllerActivator
2、DefaultHttpControllerActivator
3、DependencyResolver
4、HttpRequestMessage中的DependencyResolver
5、DependencyResolver在DefaultHttpControllerActivator中的应用
一、程序集的解析
在 Web API的HttpController激活系统中,AssembliesResolver为目标HttpController类型解析提供候选的程序集。换句话说,候选HttpController类型的选择范围仅限于定义在由AssembliesResolver提 供的程序集中的所有实现了IHttpController接口的类型。所有的AssembliesResolver均实现了接口 IAssembliesResolver,该接口定义在命名空间“System.Web.Http.Dispatcher”下,如果未作特别说明,本节新 引入的类型均定义在此命名空间下。如下面的代码片断所示,IAssembliesResolver接口中仅仅定义了一个唯一的GetAssemblies 方法,该方法返回的正是提供的程序集列表。
1: public interface IAssembliesResolver
ICollection&Assembly& GetAssemblies();
默认使用的AssembliesResolver类型为DefaultAssembliesResolver。如下面的代码片断所示,DefaultAssembliesResolver在实现的GetAssemblies方法中直接返回当前应用程序域加载的所有程序集列表。
1: public class DefaultAssembliesResolver : IAssembliesResolver
public virtual ICollection&Assembly& GetAssemblies()
return AppDomain.CurrentDomain.GetAssemblies().ToList&Assembly&();
DefaultAssembliesResolver是默认使用的AssembliesResolver,那么默认的AssembliesResolver类型在ASP.NET Web API是如何确定的呢?要回答这个问题,需要涉及到另一个重要的类型ServicesContainer,它定义在命名空间“System.Web.Http.Controllers”下。
由于DefaultAssembliesResolver在 为HttpController类型解析提供的程序集仅限于当前应用程序域已经加载的程序集,如果目标HttpController定义在尚未加载的程序 集中,我们不得不预先加载它们。但是这样的问题只会发生在Self Host寄宿模式下,如果采用Web Host寄宿模式则无此困扰,原因在于后者默认使用的是另一个AssembliesResolver类型。我们知道在Web Host寄宿模式下用于配置ASP.NET Web API消息处理管道的是通过类型GlobalConfiguration的静态只读属性Configuration返回的 HttpConfiguration对象。从如下的代码片断我们可以发现,当GlobalConfiguration的Configuration属性被 第一次访问的时候,在ServicesContainer中注册的AssembliesResolver会被替换成一个类型为 WebHostAssembliesResolver的对象。
1: public static class GlobalConfiguration
//其他成员
static GlobalConfiguration()
_configuration = new Lazy&HttpConfiguration&(delegate
HttpConfiguration configuration = new HttpConfiguration( new HostedHttpRouteCollection(RouteTable.Routes));
configuration.Services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
//其他操作
//其他操作
public static HttpConfiguration Configuration
return _configuration.V
WebHostAssembliesResolver是一个定义在程序集“System.Web.Http.WebHost.dll”中的内部类 型。从如下的代码片断可以看出WebHostAssembliesResolver在实现的GetAssemblies方法中直接通过调用 BuildManager的GetReferencedAssemblies方法来获取最终提供的程序集。
1: internal sealed class WebHostAssembliesResolver : IAssembliesResolver
ICollection&Assembly& IAssembliesResolver.GetAssemblies()
return BuildManager.GetReferencedAssemblies().OfType&Assembly&().ToList&Assembly&();
由于BuildManager的GetReferencedAssemblies方法几乎返回了在运行过程中需要的所有程序集,如果我们将 HttpController类型定义在单独的程序集中,我们只要确保该程序集已经正常部属就可以了。如果有人对此感兴趣,可以试着将上面演示的实例从 Self Host寄宿模式转换成Web Host寄宿模式,看看ASP.NET Web API的HttpController激活系统能否正常解析出分别定义在Foo.dll、Bar.dll和Baz.dll中的 HttpController类型。
二、HttpController类型的解析
注册在当前ServicesContainer上的AssembliesResolver对象为HttpController类型的解析提供了可供选择的程序集,真正用于解析HttpController类型的是一个名为HttpControllerTypeResolver的 对象。所有的HttpControllerTypeResolver类型均实现了接口IHttpControllerTypeResolver,如下面的 代码片断所示,定义其中的唯一方法GetControllerTypes借助于提供的AssembliesResolver解析出所有的 HttpController类型。
1: public interface IHttpControllerTypeResolver
ICollection&Type& GetControllerTypes(IAssembliesResolver assembliesResolver);
与AssembliesResolver注册方式类似,默认使用的HttpControllerTypeResolver同 样是注册在当前HttpConfiguration的ServicesContainer对象上。我们可以通过ServicesContainer具有如 下定义的扩展方法GetHttpControllerTypeResolver得到这个注册的HttpControllerTypeResolver对 象。
1: public static class ServicesExtensions
//其他成员
public static IHttpControllerTypeResolver GetHttpControllerTypeResolver(this ServicesContainer services);
我们同样可以通过HttpConfiguration默认采用的DefaultServices的构造函数得到默认注册的 HttpControllerTypeResolver对象的类型。如下面的代码片断所示,这个默认注册的 HttpControllerTypeResolver是一个类型为DefaultHttpControllerTypeResolver的对象。
1: public class DefaultServices : ServicesContainer
//其他成员
public DefaultServices(HttpConfiguration configuration)
//其他操作
this.SetSingle&IHttpControllerTypeResolver&(new DefaultHttpControllerTypeResolver());
1、DefaultHttpControllerTypeResolver
如下面的代码片断所示, DefaultHttpControllerTypeResolver具有一个Predicate&Type&类型的只读属性 IsControllerTypePredicate,返回的委托对象用于判断指定的类型是否是一个有效的HttpController类型。
1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
public DefaultHttpControllerTypeResolver();
public DefaultHttpControllerTypeResolver(Predicate&Type& predicate);
public virtual ICollection&Type& GetControllerTypes(IAssembliesResolver assembliesResolver);
protected Predicate&Type& IsControllerTypePredicate { }
如果我们具有特别的HttpController类型有效性验证规则,可以在调用构造函数实例化 DefaultHttpControllerTypeResolver对象时通过参数指定这个Predicate&Type&委托对象。在默 认情况下,这个自动初始化的Predicate&Type&对象体现了默认采用的HttpController类型有效验证规则。具体来说, 默认情况下一个给定的类型必须同时满足如下的条件才是一个有效的HttpController类型。
是一个外部可见(IsVisible = true)的实例(IsAbstract = false)类(IsClass = true)。
类型直接或者间接实现了接口IHttpController。
类型名称必须以“Controller”为后缀,但是不区分大小写(可以使用“controller”作为后缀)。
用于提供所有有效HttpController类型的GetControllerTypes方法的实现逻辑其实很简单。它通过指定的 AssembliesResolver得到一个程序集列表,对于定义在这些程序集中的所有类型,如果满足上述的要求就是返回的 HttpController类型之一。定义在类型DefaultHttpControllerTypeResolver中的针对有效 HttpController类型的解析逻辑基本上体现在如下所示的代码中。
1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
//其他成员
public virtual ICollection&Type& GetControllerTypes(IAssembliesResolver assembliesResolver)
List&Type& types = new List&Type&();
foreach (Assembly assembly in assembliesResolver.GetAssemblies())
foreach (Type type in assembly.GetTypes())
if (this.IsControllerTypePredicate(type))
types.Add(type);
2、HttpController类型的缓存
由于针对所有HttpController类型的解析需要大量使用到反射,这是一个相对耗时的过程,所以ASP.NET Web API会对解析出来的HttpController类型进行缓存。具体的缓存实现在具有如下定义的HttpControllerTypeCache类型 中,这是一个定义在程序集“System.Web.Http.dll”中的内部类型。
1: internal sealed class HttpControllerTypeCache
//其他成员
internal Dictionary&string, ILookup&string, Type&& Cache { }
缓存的HttpController类型通过只读属性Cache获取,这是一个类型为Dictionary&string, ILookup&string, Type&&的字典对象。该字典的Key表示HttpController的名称(HttpController类型名称去除 “Controller”后缀),其Value返回的ILookup&string, Type&对象包含一组具有相同名称的HttpController类型列表,自身的Key表示HttpController类型的命名空间。
三、目标HttpController的选择
AssembliesResolver仅仅是将所有合法的HttpController类型解析出来,针对具体的调用请求,系统必须从中选择一个与 当前请求匹配的HttpController类型出来。HttpController的选择通过HttpControllerSelector对象来完 成,所有的HttpControllerSelector类型均实现了具有如下定义的接口IHttpControllerSelector。
1: public interface IHttpControllerSelector
IDictionary&string, HttpControllerDescriptor& GetControllerMapping();
HttpControllerDescriptor SelectController(HttpRequestMessage request);
如上面的代码片断所示,该接口中定义了GetControllerMapping和SelectController两个方法。GetControllerMapping返回一个描述所有HttpController类型的HttpControllerDescriptor对象与对应的HttpController名称之间的映射关系。针对请求的HttpController选择实现在SelectController方法中,它返回描述目标HttpController的HttpControllerDescriptor对象。
1、DefaultHttpControllerSelector
默认使用HttpControllerSelector依然注册到当前的ServicesContainer对象中,我们可以调用ServicesContainer如下所示的扩展方法GetHttpControllerSelector得到注册的HttpControllerSelector对象。
1: public static class ServicesExtensions
//其他成员
public static IHttpControllerSelector GetHttpControllerSelector(this ServicesContainer services);
如下的代码片断所示,默认使用的DefaultServices在初始化的过程中会根据指定的HttpConfiguration对象创建一个 DefaultHttpControllerSelector对象,并将其注册为默认的HttpControllerSelector。
1: public class DefaultServices : ServicesContainer
//其他成员
public DefaultServices(HttpConfiguration configuration)
//其他操作
this.SetSingle&IHttpControllerSelector&(new DefaultHttpControllerSelector(configuration));
如下面的代码片断所示,DefaultHttpControllerSelector不仅仅实现了IHttpControllerSelector 接口中定义的两个方法,还定义了另一个名为GetControllerName方法,我们可以调用此方法根据指定HttpRequestMessage对 象得到该请求访问的目标HttpController的名称。
1: public class DefaultHttpControllerSelector : IHttpControllerSelector
public DefaultHttpControllerSelector(HttpConfiguration configuration);
public virtual IDictionary&string, HttpControllerDescriptor& GetControllerMapping();
public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request);
public virtual string GetControllerName(HttpRequestMessage request);
2、获取目标HttpController的名称
如果采用Web Host寄宿模式,消息管道的缔造者HttpControllerHandler在根据当前HTTP上下文创建用于表示请求的 HttpRequestMessage对象后,会将ASP.NET路由系统解析当前请求得到的RouteData对象转换成HttpRouteData对 象并添加到HttpRequestMessage的属性字典中。对于Self Host寄宿模式来说,处于消息处理管道末端的HttpRoutingDispatcher会利用ASP.NET Web API的路由系统对当前请求进行路由解析并直接得到封装了路由数据的HttpRouteData对象,此HttpRouteData同样会被添加到表示当 前请求的HttpRequestMessage对象的属性字典之中。
由于被附加到当前请求的HttpRouteData已经包含了目标HttpController的名称(对应的变量名为 “controller”),所以我们可以从HttpRequestMessage中直接获取目标HttpController的名称。如下面的代码片断 所示,DefaultHttpControllerSelector的GetControllerName方法也是按照这样的逻辑从指定的HttpMessageMessage中提取目标HttpController的名称。
1: public class DefaultHttpControllerSelector : IHttpControllerSelector
//其他成员
public virtual string GetControllerName(HttpRequestMessage request)
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
return null;
string str = null;
routeData.Values.TryGetValue&string&("controller", out str);
3、建立HttpController名称与HttpControllerDescriptor之间的映射
DefaultHttpControllerSelector 的GetControllerMapping方法会返回一个类型为IDictionary&string, HttpControllerDescriptor&的字典,它包含了描述所有HttpController的 HttpControllerDescriptor对象与对应HttpController名称之间的映射关系。
1: public class DefaultHttpControllerSelector : IHttpControllerSelector
//其他成员
private readonly HttpControllerTypeCache _controllerTypeC
public virtual IDictionary&string, HttpControllerDescriptor& GetControllerMapping();
GetControllerMapping方法的实现逻辑其实很简单。如上面的代码片断所 示,DefaultHttpControllerSelector具有一个HttpControllerTypeCache类型的只读字段,通过它可以得 到HttpController类型与名称之间的关系,GetControllerMapping方法只需要根据HttpController类型生成对 应的HttpControllerDescriptor对象即可。但是有个问题必须要考虑,由于同名的HttpController类型可能定义在不同的 命名空间下,而且这里所指的“HttpController名称”是不区分大小写的,所以一个HttpController名称可能对应着多个 HttpController类型,这也是为何HttpControllerTypeCache缓存的数据是一个类型为 Dictionary&string, ILookup&string, Type&&的字典对象的原因。
4、根据请求选择HttpController
其实HttpControllerSelector的终极目标还是根据请求实现对目标HttpController的选择,这体现在它的SelectController方法上。对于默认注册的DefaultHttpControllerSelector 来说,其SelectController方法的实现逻辑非常简单,它只需要调用GetControllerName方法从给定的 HttpRequestMessage提取目标HttpController的名称,然后根据此名称从GetControllerMapping方法的返 回值中提取对应的HttpControllerDescriptor对象即可。实现在SelectController方法中针对请求的 HttpController选择机制虽然简单,但是针对几种特殊情况的处理机制我们不应该忽视。
首先,如果调用GetControllerName方法返回的HttpController名称为Null或者是一个空字符串,意味着 ASP.NET路由系统(针对Web Host寄宿模式)或者ASP.NET Web API路由系统(针对Self Host寄宿模式)在对请求的解析过程中并没有得到表示目标HttpController名称的路由变量。这种情况下 DefaultHttpControllerSelector会直接抛出一个响应状态为HttpStatusCode.NotFound的 HttpResponseException异常,客户端自然就会接收到一个状态为“404, Not Found”的响应。
其次,如果在调用GetControllerMapping方法返回的字典中并没有一个与目标HttpController名称相匹配的HttpControllerDescriptor对象,通过上面的分析我们知道如下两种情况会导致这样的问题。
在通过AssembliesResolver提供的程序集中并不曾定义这么一个有效的HttpController类型。
在通过AssembliesResolver提供的程序集中定义了多个同名的HttpController类型,可能是多个HttpController类型在不区分大小写情况下同名,或者是完全同名的多个HttpController类型定义在不同的命名空间下。
这两种情况下自然不能通过GetControllerMapping方法返回的字典对象来判断,但是却可以通过用于缓存 HttpController类型的HttpControllerTypeCache对象来判断。对于第一种情 况,DefaultHttpControllerSelector依然会抛出一个响应状态为HttpStatusCode.NotFound的 HttpResponseException异常。在第二种情况下,它会抛出一个InvalidOperationException异常,并提示“具有 多个匹配的HttpController”。
四、HttpController的创建
通过上面的介绍我们知道利用注册的HttpControllerSelector对象可以根据表示当前请求的HttpRequestMessage 得到描述目标HttpController的HttpControllerDescriptor对象。在前面介绍 HttpControllerDescriptor的时候我们提到过它自身就具有创建对应HttpController的能力。 HttpControllerDescriptor创建被描述HttpController的能力体现在它的CreateController方法上。接 下来我们就来着重介绍实现在这个CreateController方法中的HttpController创建机制。
1: public class HttpControllerDescriptor
//其他成员
public virtual IHttpController CreateController(HttpRequestMessage request);
1、HttpControllerActivator
针对请求对目标HttpController的激活机制最终落实到一个名为HttpControllerActivator的对象上,所有的 HttpControllerActivator类型均实现了IHttpControllerActivator接口。如下面的代码片断所示,定义其中的 唯一方法Create会根据表示当前请求的HttpRequestMessage对象、描述目标HttpController的 HttpControllerDescriptor对象以及目标HttpController的类型来创建对应的HttpController对象。
1: public interface IHttpControllerActivator
IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
我们已经知道了像这样的“标准化组件”一定是注册到当前ServicesContainer上被HttpController激活系统使用的。我们可以通过ServicesContainer具有如下定义的扩展方法GetHttpControllerActivator直接获取注册的HttpControllerActivator对象。
1: public static class ServicesExtensions
//其他成员
public static IHttpControllerActivator GetHttpControllerActivator(this ServicesContainer services);
实际上HttpControllerDescriptor的CreateController方法就是调用这个扩展方法得到注册的 HttpControllerActivator对象,并调用它的Create方法来创建目标HttpController的。如下的代码体现了 CreateController方法真正的实现逻辑。
1: public class HttpControllerDescriptor
//其他成员
public virtual IHttpController CreateController(HttpRequestMessage request)
return this.Configuration.Services.GetHttpControllerActivator().Create(request, this, this.ControllerType);
2、DefaultHttpControllerActivator
我们照例利用通过DefaultServices的构造函数定义分析出默认注册的HttpControllerActivator是个怎样的对象。 如下面的代码片断所示,当DefaultServices被初始化的时候它会创建并注册一个类型为 DefaultHttpControllerActivator对象。
1: public class DefaultServices : ServicesContainer
//其他成员
public DefaultServices(HttpConfiguration configuration)
//其他操作
this.SetSingle&IHttpControllerActivator&(new DefaultHttpControllerActivator());
接下来我们就来分析一下在DefaultHttpControllerActivator类 型的Create方法中是如何激活目标HttpController实例的,不过要真正了解实现在 DefaultHttpControllerActivator的HttpController激活机制之前,我们需要认识另一个名为 DependencyResolver的对象。
3、DependencyResolver
说到DependencyResolver,我们又不得不谈到IoC的概念。我们知道IoC常和另一个术语“依赖注入(DI,Dependency Injection)”联系在一起。通过IoC容器激活的对象可能具有针对其他对象的依赖,而且被依赖的对象可能具有针对另一个对象的依赖,所以IoC容 器需要在提供所需对象之前帮助我们解决这些依赖。从命名也可以看出来,这里介绍DependencyResolver与依赖注入有关,我们可以将它视为 ASP.NET Web API内部使用的IoC容器。所有的DependencyResolver实现了具有如下定义的接口IDependencyResolver,它定义在命 名空间“System.Web.Http.Dependencies”下。这个接口的定义有点特别,它具有唯一个返回类型为 IDependencyScope的BeginScope方法,IDependencyResolver接口本身同时也继承 IDependencyScope这个接口,并且这两个接口又都继承自IDisposable接口。
1: public interface IDependencyResolver : IDependencyScope, IDisposable
IDependencyScope BeginScope();
6: public interface IDependencyScope : IDisposable
object GetService(Type serviceType);
IEnumerable&object& GetServices(Type serviceType);
通过DependencyResolver的BeginScope方法创建的IDependencyScope对象可以视为一个用于激活目标对象的 上下文,我们可以通过调用它的GetService和GetServices方法根据指定的“服务接口类型”获取对应的服务实例。由于 IDependencyScope继承自IDisposable,所以与此上下文关联的资源释放工作可以通过实现的Dispose方法来完成。
与上面我们介绍的那些“标准化组件”不同,默认使用的DependencyResolver并未注册到当前的ServicesContainer对象上,而是直接注册到了当前HttpConfiguration上面。如下面的代码片断所示,当前使用的DependencyResolver直接通过HttpConfiguration的DependencyResolver属性来获取和设置。
1: public class HttpConfiguration : IDisposable
//其他成员
public HttpConfiguration(HttpRouteCollection routes)
this._dependencyResolver = EmptyResolver.I
public IDependencyResolver DependencyResolver
return this._dependencyR
this._dependencyResolver = value;
从上面的代码片断我们还可以看出默认注册到HttpConfiguration上的DependencyResolver是通过类型 EmptyResolver的静态属性Instance返回的EmptyResolver对象。EmptyResolver是一个定义在程序集 “System.Web.Http.dll”中的内部类型,其成员定义如下。之所以将它如此命名,原因在于它仅仅是一个“空”的IoC容器。它的 BeginScope返回的是它自身,GetService和GetServices方法分别返回Null和一个空对象集合,Dispose方法也没有任 何资源释放工作要做。
1: internal class EmptyResolver : IDependencyResolver, IDependencyScope,
IDisposable
public IDependencyScope BeginScope();
public void Dispose();
public object GetService(Type serviceType);
public IEnumerable&object& GetServices(Type serviceType);
public static IDependencyResolver Instance { }
4、HttpRequestMessage中的DependencyResolver
虽然当前使用的DependencyResolver是注册到当前HttpConfiguration上的,但是我们可以直接从表示当前请求的 HttpRequestMessage对象中获取由它创建的DependencyScope对象。如下面的代码片断所 示,HttpRequestMessage具有一个返回类型为IDependencyScope接口的扩展方法GetDependencyScope。
1: public static class HttpRequestMessageExtensions
//其他成员
public static IDependencyScope GetDependencyScope(this HttpRequestMessage request);
其实这个扩展方法实现逻辑很简单,因为DependencyScope对象也存放于HttpRequestMessage的属性字典中。如果此 DependencyScope对象尚未添加,该方法则会通过当前的HttpConfiguration得到注册的DependencyResolver 对象,然后利用它创建一个新的DependencyScope对象并添加到HttpRequestMessage对象的属性字典中,后续过程如果需要使用 到此DependencyScope就可以直接从HttpRequestMessage中提取了。
5、DependencyResolver在DefaultHttpControllerActivator中的应用
在对DependencyResolver有了基本了解后,我们再来讨论DefaultHttpControllerActivator的 Create方法是如何根据当前请求来激活目标HttpController对象的。其实实现机制非常简 单,DefaultHttpControllerActivator先通过调用表示当前请求的HttpRequestMessage对象的扩展方法 GetDependencyScope得到通过当前DependencyResolver创建的DependencyScope对象,然后将目标 HttpController的类型作为参数调用其GetService方法。如果该方法返回一个具体的HttpController对象,该对象就是 Create方法的返回值,否则直接根据目标HttpController的类型进行反射创建一个HttpController对象并返回。如下所示的代 码片断基本上体现了DefaultHttpControllerActivator的HttpController激活机制。
1: public class DefaultHttpControllerActivator : IHttpControllerActivator
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
IDependencyScope depedencyScope = request.GetDependencyScope();
object httpController = depedencyScope.GetService(controllerType)?? Activator.CreateInstance(controllerType);
return httpController as IHttpC
由于默认请求下注册到当前HttpConfiguration上的DependencyResolver是一个EmptyResolver对象,它 的GetService方法总是返回Null,所以默认情况下对HttpController的激活总是利用针对目标HttpController类型的 反射实现的。关于HttpController的激活,我还想强调一点,在默认情况下解析出来的所有HttpController类型会被缓存,创建的用 于描述HttpController的HttpControllerDescriptor对象也会被缓存,但是HttpController激活系统并不 会对创建的HttpController对象实施缓存。换言之,对于多个针对相同的HttpController类型的请求来说,最终被激活的 HttpController实例都是不同的。
据专家透露,运行特定版本 OpenSSL 的 web 服务器均存在一个名为“Heartbleed”的漏洞,黑客利用此漏洞可盗走用于加密在线交易和 web 连接的密钥,并导致用户在进行搜索或邮箱登录时个人信息被泄露。
SSL(安全套接层)协议是使用最为普遍加密技术,而 OpenSSL 则是开源的 SSL 套件,为全球成千上万的 web 服务器所使用。Web 服务器正是通过它来将密钥发送给访客然后在双方的连接之间对信息进行加密。URL 中使用 https 打头的连接都采用了 SSL 加密技术。在线购物、网银等活动均采用 SSL 技术来防止窃密及避免。
Heartbleed 漏洞之所以得名,是因为用于安全传输层协议(TLS)及数据包传输层安全协议(DTLS)的 Heartbeat 扩展存在漏洞。Heartbeat 扩展为 TLS/DTLS 提供了一种新的简便的连接保持方式,但由于 OpenSSL 1.0.2-beta 与 OpenSSL 1.0.1 在处理 TLS heartbeat 扩展时的边界错误,攻击者可以利用漏洞披露连接的客户端或服务器的存储器内容,导致攻击者不仅可以读取其中机密的加密数据,还能盗走用于加密的密钥。
据估计受影响的服务器数量可能多达几十万。其中已被确认受影响的网站包括 Imgur、OKCupid、Eventbrite 以及 FBI 网站等,不过 Google 未受影响。
此外,漏洞还可能导致用户信息的泄露。比方说黑客已经可以利用此漏洞通过查看最近访问受影响服务器的用户的 cookie 来获取其个人信息。已有开发者说发现可利用此漏洞查看到以保护用户隐私出名的搜素引擎 DuckDuckGo 上的用户搜索记录,Yahoo 也被发现存在此漏洞导致用户凭证的。
该漏洞 2011 年就已经被引入,但直到最近才被人发现。受影响服务器必须给自己的 OpenSSL 打上补丁,同时还需要更改密钥才能避免进一步受到影响。专家建议,为了免受此漏洞影响,用户最安全的应对措施是最近几天都不要参与敏感的网上活动,如购物、使用网银等。
漏洞的介绍视频:
[消息来源:]
曾经在公司做过一个比价系统,就是抓取其它上商品的价格并和自己公司的商品进行对应,然后展示出来,给pm提供一个定价的参考。后来,有同 事的朋友在找工作的时候,猎头让其做一个抓取去哪网最低价机票的程序,然后,我就帮忙整了一下。本文的目的在于提供这个程序的源码,然后和大家探讨一下网 页信息抓取的相关点。Demo使用并在vs2012环境下运行。
项目结构一览
下面是Demo的项目结构图:
下面是Demo的运行结果图:
思路&问题分析
个人以为,网页信息的获取分为两个阶段:1 知道目标网页和相关参数,并获取网页的源码 2 将获取到的源码抽取出我们需要的信息,并转换成对象
在Demo中的HttpHelper.cs文件下的类的职责就是设定目标网页地址和相关参数,该类是在网上找到的,据说可以无视cookie、证书等验证,很牛,推荐小伙伴们使用,所以,第一个目标是比较容易能够完成的
难点在于第二个目标,我们如何抓取html源码(json数据)中的有效信息并转换成我们需要的c#对象呢?Demo中获取的是json数据,然 后用正则抓出了其中的一部分,再转换成一个实体类的列表。Demo中的AsyncRegexHelper是异步的正则匹配帮助类,在使用正则匹配的过程 中,经常遇到无限回溯的问题,使用这个帮助类可以异步地执行匹配并且有一个超时时间。现在碰到的问题是正则匹配比较不靠谱,难度较大且不易扩展,目前打算 想用Html Agility Pack来进行数据的匹配,希望伙伴们能指点下,谢谢大家。
本人文笔拙劣,感谢大家的支持。提供源码,大家分享一下,希望能做一个通用点的系统,只需要输入网址和一些简单的规则,就能够获取我们所需要的信息。
MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效 性,我们将针对参数的验证成为Model绑定。总地来说,我们可以采用4种不同的编程模式来进行针对绑定参数的验证。
一、手工验证绑定的参数
在 定义具体Action方法的时候,对已经成功绑定的参数实施手工验证无疑是一种最为直接的编程方式,接下来我们通过一个简单的实例来演示如何将参数验证逻 辑实现在对应的Action方法中,并在没有通过验证的情况下将错误信息响应给客户端。我们在一个 MVC应用中定义了如下一个Person类作为被验证的数据类型,它的Name、Gender和Age三个属性分别表示一个人的姓名、性别和年龄。
public class Person
[DisplayName("姓名")]
public string Name { }
[DisplayName("性别")]
public string Gender { }
[DisplayName("年龄")]
public int? Age { }
接下来我们定义了如下一个HomeController。在针对GET请求的Action方法Index中,我们创建了一个Person对象并将其 作为Model呈现在对应的View中。另一个支持POST请求的Index方法具有一个Person类型的参数,我们在该Action方法中先调用 Validate方法对这个输入参数实施验证。如果验证成功(ModeState.IsValid属性返回True),我们返回一个内容为“输入数据通过 验证”的ContentResult,否则将此参数作为Model呈现在对应的View中。
public class HomeController : Controller
public ActionResult Index()
return View(new Person());
[HttpPost]
public ActionResult Index(Person person)
Validate(person);
if (!ModelState.IsValid)
return View(person);
return Content("输入数据通过验证");
private void Validate(Person person)
if (string.IsNullOrEmpty(person.Name))
ModelState.AddModelError("Name", "'Name'是必需字段");
if (string.IsNullOrEmpty(person.Gender))
ModelState.AddModelError("Gender", "'Gender'是必需字段");
else if (!new string[] { "M", "F" }.Any(
g =& string.Compare(person.Gender, g, true) == 0))
ModelState.AddModelError("Gender",
"有效'Gender'必须是'M','F'之一");
if (null == person.Age)
ModelState.AddModelError("Age", "'Age'是必需字段");
else if (person.Age & 25 || person.Age & 18)
ModelState.AddModelError("Age", "有效'Age'必须在18到25周岁之间");
如上面的代码片断所示,我们在Validate该方法中我们对作为参数的Person对象的3个属性进行逐条验证,如果提供的数据没有通过验证,我 们会调用当前ModelState的AddModelError方法将指定的验证错误消息转换为ModelError保存起来。我们采用的具体的验证规则 如下。
Person对象的Name、Gender和Age属性均为必需字段,不能为Null(或者空字符串)。
表示性别的Gender属性的值必需是“M”(Male)或者“F”(Female),其余的均为无效值。
Age属性表示的年龄必须在18到25周岁之间。
如下所示的是Action方法Index对应View的定义,这是一个Model类型为Person的强类型View,它包含一个用于编辑人员信息 的表单。我们直接调用HtmlHelper&TModel& 的扩展方法EditorForModel将作为Model的Person对象以编辑模式呈现在表单之中。
@model Person
&title&编辑人员信息&/title&
@using (Html.BeginForm())
@Html.EditorForModel()
&input type="submit" value="保存"/&
直接运行该程序后,一个用于编辑人员基本信息的页面会被呈现出来,如果我们在输入不合法的数据并提交后,相应的验证信息会以图1所示的形式呈现出来。
二、使用ValidationAttribute特性
将针对输入参数的验证逻辑和业务逻辑定义在Action方法中并不是一种值得推荐的编程方式。在大部分情况下,同一个数据类型在不同的应用场景中具 有相同的验证规则,如果我们能将验证规则与数据类型关联在一起,让框架本身来实施数据验证,那么最终的开发者就可以将关注点更多地放在业务逻辑的实现上 面。实际上这也是的Model验证系统默认支持的编程方式。当我们在定义数据类型的时候,可以在类型及其数据成员上面应用相应的 ValidationAttribute特性来定义默认采用的验证规则。
“ponentModel.DataAnnotations”命名空间定义了一系列具体的 ValidationAttribute特性类型,它们大都可以直接应用在自定义数据类型的某个属性上对目标数据成员实施验证。这些预定义验证特性不是本 章论述的重点,我们会在“下篇”中对它们作一个概括性的介绍。
常规验证可以通过上面列出的这些预定义ValidationAttribute特性来完成,但是在很多情况下我们需要通过创建自定义的 ValidationAttribute特性来解决一些特殊的验证。比如上面演示实例中针对Person对象的验证中,我们要求Gender属性指定的表 示性别的值必须是“M/m”和“F/f”两者之一,这样的验证就不得不通过自定义的ValidationAttribute特性来实现。
针对 “某个值必须在指定的范围内”这样的验证规则,我们定义一个DomainAttribute特性。如下面的代码片断所示,DomainAttribute 具有一个IEnumerable&string&类型的只读属性Values提供了一个有效值列表,该列表在构造函数中被初始化。具体的验证 实现在重写的IsValid方法中,如果被验证的值在这个列表中,则视为验证成功并返回True。为了提供一个友好的错误消息,我们重写了方法 FormatErrorMessage。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,
AllowMultiple = false)]
public class DomainAttribute : ValidationAttribute
public IEnumerable&string& Values { private }
public DomainAttribute(string value)
this.Values = new string[] { value };
public DomainAttribute(params string[] values)
this.Values =
public override bool IsValid(object value)
if (null == value)
return true;
return this.Values.Any(item =& value.ToString() == item);
public override string FormatErrorMessage(string name)
string[] values = this.Values.Select(value =& string.Format("'{0}'",
value)).ToArray();
return string.Format(base.ErrorMessageString, name,string.Join(",",
由于在进行参数绑定的时候会自动提取应用在目标参数类型或者数据成员上的ValidationAttribute特性,并利用它们对提供的数据实施验 证,所以我们不再需要像上面演示的实例一样自行在Action方法中实施验证,而只需要在定义参数类型Person的时候应用相应的 ValidationAttribute特性将采用的验证规则与对应的数据成员相关联。
如下所示的是属性成员上应用了相关ValidationAttribute特性的Person类型的定义。我们在三个属性上均应用了 RequiredAttribute特性将它们定义成必需的数据成员,Gender和Age属性上则分别应用了DomainAttribute和 RangeAttribute特性对有效属性值的范围作了相应限制。
public class Person
[DisplayName("姓名")]
[Required(ErrorMessageResourceName = "Required",
ErrorMessageResourceType = typeof(Resources))]
public string Name { }
[DisplayName("性别")]
[Required(ErrorMessageResourceName = "Required",
ErrorMessageResourceType = typeof(Resources))]
[Domain("M", "F", "m", "f", ErrorMessageResourceName = "Domain",
ErrorMessageResourceType = typeof(Resources))]
public string Gender { }
[DisplayName("年龄")]
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources))]
[Range(18, 25, ErrorMessageResourceName = "Range",
ErrorMessageResourceType = typeof(Resources))]
public int? Age { }
三个ValidationAttribute特性采用的错误消息均定义在项目默认的资源文件中(我们可以采用这样的步骤创建这个资源文件:右键选择 Solution Exploror中的项目,并在上下文菜单中选择“属性”选项打开“项目属性”对象框。最后在对话框中选择“资源”Tab页面,通过点击页面中的链接创建 一个资源文件),具体定义如图2所示。
由于会自动提取应用在绑定参数类型上的ValidationAttribute特性对绑定的参数实施自动化验证,所以我们根本不需要在具体的 Action方法中来对参数作手工验证。如下面的代码片断所示,我们在Action方法Index中不再显式调用Validate方法,但是运行该程序并 在输入不合法数据的情况下提交表单后依然会得到如图1所示的输出结果。
public class HomeController : Controller
//其他成员
[HttpPost]
public ActionResult Index(Person person)
if (!ModelState.IsValid)
return View(person);
return Content("输入数据通过验证");
三、让数据类型实现IValidatableObject接口
除了将验证规则通过ValidationAttribute特性直接定义在数据类型上并让ASP.NET MVC在进行参数绑定过程中据此来验证参数之外,我们还可以将验证操作直接定义在数据类型中。既然我们将验证操作直接实现在了数据类型上,意味着对应的数 据对象具有“自我验证”的能力,我们姑且将这些数据类型称为“自我验证类型”。这些自我验证类型是实现了具有如下定义的接口 IValidatableObject,该接口定义在“ponentModel.DataAnnotations”命名空间下。
public interface IValidatableObject
IEnumerable&ValidationResult& Validate(
ValidationContext validationContext);
如上面的代码片断所示,IValidatableObject接口具有唯一的方法Validate,针对自身的验证就实现在该方法中。对于上面演示实例中定义的数据类型Person,我们可以按照如下的形式将它定义成自我验证类型。
public class Person: IValidatableObject
[DisplayName("姓名")]
public string Name { }
[DisplayName("性别")]
public string Gender { }
[DisplayName("年龄")]
public int? Age { }
public IEnumerable&ValidationResult& Validate( ValidationContext validationContext)
Person person = validationContext.ObjectInstance as P
if (null == person)
yield break;
if(string.IsNullOrEmpty(person.Name))
yield return new ValidationResult("'Name'是必需字段", new string[]{"Name"});
if (string.IsNullOrEmpty(person.Gender))
yield return new ValidationResult("'Gender'是必需字段", new string[] { "Gender" });
else if (!new string[]{"M","F"}.Any( g=&string.Compare(person.Gender,g, true) == 0))
yield return new ValidationResult("有效'Gender'必须是'M','F'之一",
new string[] { "Gender" });
if (null == person.Age)
yield return new ValidationResult("'Age'是必需字段",
new string[] { "Age" });
else if (person.Age & 25 || person.Age & 18)
yield return new ValidationResult("'Age'必须在18到25周岁之间",
new string[] { "Age" });
如上面的代码片断所示,我们让Person类型实现了IValidatableObject接口。在实现的Validate方法中,我们从验证上下 文中获取被验证的Person对象,并对其属性成员进行逐个验证。如果数据成员没有通过验证,我们通过一个ValidationResult对象封装错误 消息和数据成员名称(属性名),该方法最终返回的是一个元素类型为ValidationResult的集合。在不对其他代码作任何改动的情况下,我们直接 运行该程序并在输入不合法数据的情况下提交表单后依然会得到如图1所示的输出结果。
四、让数据类型实现IDataErrorInfo接口
上面我们让数据类型实现IValidatableObject接口并将具体的验证逻辑定义在实现的Validate方法中,这样的类型能够被 ASP.NET MVC所识别,后者会自动调用该方法对绑定的数据对象实施验证。如果我们让数据类型实现IDataErrorInfo接口也能实现类似的自动化验证效果。
IDataErrorInfo接口定义在“ponentModel”命名空间下,它提供了一种标准的错误信息定制方式。如下面 的代码片段所示,IDataErrorInfo具有两个成员,只读属性Error用于获取基于自身的错误消息,而只读索引用于返回指定数据成员的错误消 息。
public interface IDataErrorInfo
string Error { }
string this[string columnName] { }
同样是针对上面演示的实例,现在我们对需要被验证的数据类型Person进行了重新定义。如下面的代码片断所示,我们让Person实现了 IDataErrorInfo接口。在实现的索引中,我们将索引参数columnName视为属性名称,根据它按照上面的规则对相应的属性成员实施验证, 并在验证失败的情况下返回相应的错误消息。在不对其他代码作任何改动的情况下,我们直接运行该程序并在输入不合法数据的情况下提交表单后依然会得到如图1 所示的输出结果。
public class Person : IDataErrorInfo
[DisplayName("姓名")]
public string Name { }
[DisplayName("性别")]
public string Gender { }
[DisplayName("年龄")]
public int? Age { }
[ScaffoldColumn(false)]
public string Error { private }
public string this[string columnName]
switch (columnName)
case "Name":
if(string.IsNullOrEmpty(this.Name))
return "'姓名'是必需字段";
return null;
case "Gender":
if (string.IsNullOrEmpty(this.Gender))
return "'性别'是必需字段";
else if (!new string[] { "M", "F" }.Any(
g =& string.Compare(this.Gender, g, true) == 0))
return "'性别'必须是'M','F'之一";
return null;
case "Age":
if (null == this.Age)
return "'年龄'是必需字段";
else if (this.Age & 25 || this.Age & 18)
return "'年龄'必须在18到25周岁之间";
return null;
default: return null;
穷人的恶性循环:
穷 -& 需要努力工作 -& 没有时间去交际 -& 人脉越来越狭窄 -& 工作越来越难做 -& 越需要努力去工作 -& 越没有时间去发展人脉 -& 越穷富人的良性循环:
有钱 -& 工作很轻松 -& 很多时间都在交际上 -& 人脉越来越广 -& 工作越来越不用努力 -& 越有更多的时间精力去发展人脉 -& 越富有 程序员的恶性循环:
加班 -& 没空学习 -& 老是写同等水平代码 -& 无法提升代码质量 -& 老是出BUG -& 老是需要修改 -& 加班 -& ….1.想到个事情,IP5都出来的时候,我还是在用那种只能打电话接电话的直板手机,每次公司聚会的时候,老总给每个人发邮件,大家都拿出触屏的来收邮件,唯独自己一个人还是那种最老的手机 —— 三星E110C,当时自己真恨不得找个地洞钻下去,完全来错了地方一样。上司都说你每个月工资也五六K了,怎么不换个好一点的手机?穷惯了,舍不得,所受的教育一定要节俭,思想斗争,还是坚持节俭。舍不得花三四K买个好的手机。。。从小穷惯了节俭惯了,思想迂腐,只知道省钱不知道投资。2.还 想到一个事情,我在广州天河太古汇那上班,中午吃饭,每次都不敢进那种装修好一点的餐馆吃饭。总觉得那种地方贵吧,具体有多贵自己也说不出来也不知道,反 正就是一想到就觉得贵,舍不得心疼钱。然后我每次中午要跑很远去离工作地点很远的石牌城中村吃午餐,十多二十几块钱的一份盒饭,又不卫生人有超级多,但是 自己一直忍着,没办法没钱,穷命穷受罪。有一次忙一个东西实在是太远,一狠心就在上班的楼下那些餐馆吃饭吧,结果一看菜单,才发现哇靠原来这么便宜,10元一份的比比皆是,而且还有座位,环境比起城中村的那些没座位还脏兮兮的好不知道多少倍。突然之间我似乎明白出一些道理。
3.第三件事,我以前总是没有鞋子穿,不信可以看我以前在论坛水区发的贴,提问什么鞋子耐穿。那时候我每次都是找那种15元 25元一双的“亏本甩卖”的鞋店去买鞋,里面都是15 25一双,但是我总会挑选50、99一双的,为的是希望可以穿得久一点,不过很遗憾,每次都是最多2个月就破了报废了。然后每天都是没鞋子穿,每天都是穿着破鞋去上班,而屋里总是一大堆鞋子,但是都破了,每隔一两个月就要去这样的店铺买鞋子。后来偶然一次我算了一下,每月几乎要买一双鞋子,花费50到99,3个月就是150,还不如买一双好一点的名牌鞋子试试。但由于一直穿的鞋子不管是25 还是 50 还是99都是不到2个月就坏了,所以更是不敢去买几百一双的鞋子。恶性循环!最后一次铤而走险,花了几百块去专卖店买了一双某牌子的鞋子(这里还是隐藏牌号,免得广告)。发现居然穿了3个月都没坏掉现在还一直穿着很好。从此之后我再也不进那种25元一双的鞋店买鞋子4.第四件事,我用的第一部智能手机是HTC的,G13。当时在车上、外面看到每个人用的都是HTC,认为HTC应该是非常不错的吧,ZOL手机上都拍第二了,很牛逼吧!于是花了将近2000块在国美买了一国行HTC。不过用了几个月就越来越卡,越来越慢,512M内存。一年保修期之后 刷机了,删除自带的软件了还是就只能打电话接电话了,根本算不上智能机了。之后对只能手机产生了严重的怀疑,科技这么发达,怎么一个排名第二的智能手机这么差,不说运行游戏就连QQ都运行不了了!最后想过换三星的手机,因为都是安卓的,担心又会像HTC这样,完全就只能打电话发短信。咬牙买苹果。其实我很高心自己当时能做这样的决定,用了才发现对比之下HTC根本就不能算智能手机!苹果512M内存都可以安装无数个软件应用,而 HTC G13安装了QQ QQ空间 QQ同步助手 UC浏览器 搜狗输入法就什么都装不了,且一运行就黑屏第五件事,我在广州一家网络公司做程序员,月薪4K5,是我在武汉2K工资的2倍还多,心里非常哈皮,所以工作非常努力卖命。公司就我一个PHP程序员,一开始不怎么加班,但到最后我却弄得每天都加班,,,,撑了2年我最后还是累的主动辞职了,,,出来之后才发现这公司给的工资比行情低至少2K,,,,但我2年间根本从没去打探过行情,也没时间精力去打探,,,
一、函数参数与泛型比较
泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型。泛型的作用是什么,这里与函数参数做一个比较:
无参数的函数:
public int[] newIntArray(){
return new int[6];
函数功能即返回一个大小为6的数组,但是这个函数只能返回固定大小为6的数组,如果想返回不同size的数组则还要重新编写函数。解决方法就是使用函数参数,传入一个代表数组大小的size。
有参数的函数:我们在写一个函数时,往往需要向函数传入一些参数,使得函数具有一定的通用性,完成某些特定的功能,如:
public int[] newIntArray(int size){
return new int[size];
这是一个简单的函数,传入参数size,函数会生成相应大小的整型数组返回给我们。但是这里有一个限制,我们只能得到整型的数组,这就大大限制了这个函数的功能和可重用性,那么当我们想要得到一个浮点型数组时又要编写一个非常类似的函数。这里的解决办法就是使用泛型:
有参数且使用泛型的函数
@SuppressWarnings("unchecked")
public static &T& T[] newArray(int size) {
Object array = new Object[size];
return (T[])
在函数内部我们使用了Object来新建数组,而不是直接用类型参数T直接声明(由于Java泛型的擦除所致,见【Java心得总结四】Java泛型中——万恶的擦除)。通过这个函数,我们就能够得到“任意大小任意类型”的数组。
总结:比较上面三个示例,我们就能理解为什么要有泛型这个概念了。在上面的示例中,每一级的例子都比前一级的例 子功能上更加扩展,可重用性更加的高。第一个示例我们只能得到固定大小的数组;为了更加泛化,我们使用参数传入size来得到不同大小的数组;同样为了得 到不同类型的数组,我们甚至将类型也作为参数传递入函数,从而得到任意大小任意类型的数组。
综上,我们利用泛型来解耦类或方法与所使用的类型之间的约束,将类型也作为一种参数传入,使得类或方法的可重用性大大提高。
二、泛型类
声明格式:
public class Generics&K, V& {
public Generics(K k, V v) {
泛型类的声明一般放在类名之后,可以有多个泛型参数,用尖括号括起来形成类型参数列表。
泛型类应用最广泛的就是我们在平时Java编程中最最常用到的容器类(博文后续),举一个容器的简单例子:
import java.util.ArrayL
import java.util.D
import java.util.R
public class RandomList&T& {
private ArrayList&T& storage = new ArrayList&T&();
private Random rand = new Random(new Date().getTime());
public void add(T item) {
storage.add(item);
public T select() {
return storage.get(rand.nextInt(storage.size()));
这段代码将原有的ArrayList容器进行了封装,调用select函数会随机的返回一个列表中的元素。
三、泛型接口
声明格式:
public interface Generator&T& {
public T next();
同泛型类的声明类似,在接口名之后,用尖括号将所有类型参数括起来。注:这里声明的是一个工厂设计模式常用的生成器接口。
我们平时编程最常见的泛型接口就是Iterable接口,即迭代器接口(博文后续),举一个简单的例子:
import java.util.ArrayL
import java.util.D
import java.util.I
import java.util.R
public class RandomList&T& implements Iterable&T& {
private ArrayList&T& storage = new ArrayList&T&();
private Random rand = new Random(new Date().getTime());
public void add(T item) {
storage.add(item);
public T select() {
return storage.get(rand.nextInt(storage.size()));
/* 实现Iterable接口 */
public Iterator&T& iterator() {
return storage.iterator();
还是上面泛型类举得容器例子,这次我们给RandomList类部署了Iterable接口,这样我们就可以利用foreach语句对RandomList中的元素进行遍历。
四、泛型方法
声明格式:
public &T& T genericMethod(T t){
泛型方法的声明和泛型类的声明略有不同,它是在返回类型之前用尖括号列出类型参数列表,而函数传入的形参类型可以利用泛型来表示。
见文章开头的例子。
五、Java泛型的局限性
1.基本类型()无法作为类型参数即ArrayList&int&这样的代码是不允许的,如果为我们想要使用必须使用基本类型对应的包装器类型ArrayList&Integer&
2.在泛型代码内部,无法获得任何有关泛型参数类型的信息(见【Java心得总结四】Java泛型中——万恶的擦除)。换句话说,如果传入的类型参数为T,即你在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了(类型信息,博文后续)。
3.注,在能够使用泛型方法的时候,尽量避免使整个类泛化。
备案信息:

我要回帖

更多关于 aspsql数据库连接 的文章

 

随机推荐