仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 540|回复: 8
打印 上一主题 下一主题

[学习教程] ASP.NET网页设计Community Server专题三:HttpModule

[复制链接]
若相依 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-16 22:35:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
在CSDN里搜索一下“初学”两字,竟有三百余篇帖子(也许更多)。有些帖子说,有了asp的基础,只要15天就能很熟悉了,我甚感自己的愚钝。更多帖子是向大家请教初学者适合看书。两个多月的时间(当然平常杂事比较多。server从专题三入手下手剖析CommunityServer的一些详细的手艺完成,依据IIS对哀求的处置流程,从HttpModule&HttpHandler切进话题,同时你也能够经由过程一系列的专题懂得CS的运转历程,不但云云,一切的.Net1.1构架的WebApp都是以一样的按次实行的。
先懂得一下IIS体系。它是一个程序,卖力对网站的内容举行办理而且处置对客户的哀求做出反响。当用户对一个页面提出哀求时,IIS做以下反响(不思索权限成绩):
1.把对方哀求的假造路径转换成物理路径
2.依据物理路径搜刮哀求的文件
3.找到文件后,猎取文件的内容
4.天生Http头信息。
5.向客户端发送一切的文件内容:起首是头信息,然后是Html内容,最初是别的文件的内容。
6.客户端IE扫瞄器取得信息后,剖析文件内容,找出个中的援用文件,如.js.CSS.gif等,向IIS哀求这些文件。
7.IIS猎取哀求后,发送文件内容。
8.当扫瞄器猎取一切内容后,天生内容界面,客户就看到图象/文本/别的内容了。
可是IIS自己是不撑持静态页面的,也就是说它仅仅撑持静态html页面的内容,关于如.asp,.aspx,.cgi,.php等,IIS其实不会处置这些标志,它就会把它看成文本,涓滴不做处置发送到客户端。为懂得决这个成绩。IIS有一种机制,叫做ISAPI的选择器,这个器材是一个尺度组件(COM组件),当在在会见IIS所不克不及处置的文件时,如asp.net1.1中的IIS附加ISAPI选择器如图:

Asp.net服务在注册到IIS的时分,会把每一个扩大能够处置的文件扩大名注册到IIS内里(如:*.ascx、*.aspx等)。扩大启动后,就依据界说好的体例来处置IIS所不克不及处置的文件,然后把把持权跳转到专门处置代码的历程中。让这个历程入手下手处置代码,天生尺度的HTML代码,天生后把这些代码到场到原本的Html中,最初把完全的Html前往给IIS,IIS再把内容发送到客户端。
有下面对ISAPI的复杂形貌,我们把HttpModule&HttpHandler分隔会商,而且分离CS举行详细的完成剖析。
HttpModule:
HttpModule完成了ISAPIFilter的功效,是经由过程对IhttpModule接口的承继来处置。上面翻开CS中的CommunityServerComponents项面前目今的CSHttpModule.cs文件(放在HttpModule目次)

//------------------------------------------------------------------------------
//<copyrightcompany="TelligentSystems">
//Copyright(c)TelligentSystemsCorporation.Allrightsreserved.
//</copyright>
//------------------------------------------------------------------------------
usingSystem;
usingSystem.IO;
usingSystem.Web;
usingCommunityServer.Components;
usingCommunityServer.Configuration;
namespaceCommunityServer
{
//*********************************************************************
//CSHttpModule
//
/**////<summary>
///ThisHttpModuleencapsulatesalltheforumsrelatedeventsthatoccur
///duringASP.NETapplicationstart-up,errors,andendrequest.
///</summary>
//***********************************************************************/
publicclassCSHttpModule:IHttpModule
{
Membervariablesandinheritedproperties/methods#regionMembervariablesandinheritedproperties/methods
publicStringModuleName
{
get{return"CSHttpModule";}
}

//*********************************************************************
//ForumsHttpModule
//
/**////<summary>
///InitializestheHttpModuleandperformsthewireupofallapplication
///events.
///</summary>
///<paramname="application">Applicationthemoduleisbeingrunfor</param>
publicvoidInit(HttpApplicationapplication)
{
//Wire-upapplicationevents
//
application.BeginRequest+=newEventHandler(this.Application_BeginRequest);
application.AuthenticateRequest+=newEventHandler(Application_AuthenticateRequest);
application.Error+=newEventHandler(this.Application_OnError);
application.AuthorizeRequest+=newEventHandler(this.Application_AuthorizeRequest);

//settingsID=SiteSettingsManager.GetSiteSettings(application.Context).SettingsID;
Jobs.Instance().Start();
//CSExceptionex=newCSException(CSExceptionType.ApplicationStart,"AppicationStarted"+AppDomain.CurrentDomain.FriendlyName);
//ex.Log();
}
//intsettingsID;
publicvoidDispose()
{
//CSExceptionex=newCSException(CSExceptionType.ApplicationStop,"ApplicationStopping"+AppDomain.CurrentDomain.FriendlyName);
//ex.Log(settingsID);
Jobs.Instance().Stop();
}
Installer#regionInstaller

#endregion

#endregion
ApplicationOnError#regionApplicationOnError
privatevoidApplication_OnError(Objectsource,EventArgse)
{
HttpApplicationapplication=(HttpApplication)source;
HttpContextcontext=application.Context;

CSExceptioncsException=context.Server.GetLastError()asCSException;
if(csException==null)
csException=context.Server.GetLastError().GetBaseException()asCSException;
try
{
if(csException!=null)
{
switch(csException.ExceptionType)
{
caseCSExceptionType.UserInvalidCredentials:
caseCSExceptionType.AccessDenied:
caseCSExceptionType.AdministrationAccessDenied:
caseCSExceptionType.ModerateAccessDenied:
caseCSExceptionType.PostDeleteAccessDenied:
caseCSExceptionType.PostProblem:
caseCSExceptionType.UserAccountBanned:
caseCSExceptionType.ResourceNotFound:
caseCSExceptionType.UserUnknownLoginError:
caseCSExceptionType.SectionNotFound:
csException.Log();
break;
}
}
else
{
Exceptionex=context.Server.GetLastError();
if(ex.InnerException!=null)
ex=ex.InnerException;
csException=newCSException(CSExceptionType.UnknownError,ex.Message,context.Server.GetLastError());
System.Data.SqlClient.SqlExceptionsqlEx=exasSystem.Data.SqlClient.SqlException;
if(sqlEx==null||sqlEx.Number!=-2)//dontlogtimeouts
csException.Log();
}
}
catch{}//notmuchtodohere,butwewanttopreventinfiniteloopingwithourerrorhandles
CSEvents.CSException(csException);
}

#endregion

ApplicationAuthenticateRequest#regionApplicationAuthenticateRequest
privatevoidApplication_AuthenticateRequest(Objectsource,EventArgse)
{
HttpContextcontext=HttpContext.Current;
Providerp=null;
ExtensionModulemodule=null;
//Iftheinstallerismakingtherequestterminateearly
if(CSConfiguration.GetConfig().AppLocation.CurrentApplicationType==ApplicationType.Installer){
return;
}

//Onlycontinueifwehaveavalidcontext
//
if((context==null)||(context.User==null))
return;
try
{
//Logictohandlevariousauthenticationtypes
//
switch(context.User.Identity.GetType().Name.ToLower())
{
//Microsoftpassport
case"passportidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Windows
case"windowsidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Forms
case"formsidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Custom
case"customidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
default:
CSContext.Current.UserName=context.User.Identity.Name;
break;
}
}
catch(Exceptionex)
{
CSExceptionforumEx=newCSException(CSExceptionType.UnknownError,"ErrorinAuthenticateRequest",ex);
forumEx.Log();
throwforumEx;
}
////Gettherolestheuserbelongsto
////
//Rolesroles=newRoles();
//roles.GetUserRoles();
}
#endregion
ApplicationAuthorizeRequest#regionApplicationAuthorizeRequest
privatevoidApplication_AuthorizeRequest(Objectsource,EventArgse){

if(CSConfiguration.GetConfig().AppLocation.CurrentApplicationType==ApplicationType.Installer)
{
//CSContext.Create(context);
return;
}

HttpApplicationapplication=(HttpApplication)source;
HttpContextcontext=application.Context;
CSContextcsContext=CSContext.Current;
//boolenableBannedUsersToLogin=CSContext.Current.SiteSettings.EnableBannedUsersToLogin;

////Iftheinstallerismakingtherequestterminateearly
//if(csContext.ApplicationType==ApplicationType.Installer){
//return;
//}
//csContext.User=CSContext.Current.User;
CSEvents.UserKnown(csContext.User);
ValidateApplicationStatus(csContext);
//Trackanonymoususers
//
Users.TrackAnonymousUsers(context);
//Doweneedtoforcetheusertologin?
//

if(context.Request.IsAuthenticated)
{
stringusername=context.User.Identity.Name;
if(username!=null)
{
string[]roles=CommunityServer.Components.Roles.GetUserRoleNames(username);
if(roles!=null&&roles.Length>0)
{
csContext.RolesCacheKey=string.Join(",",roles);
}
}
}
}
#endregion
ApplicationBeginRequest#regionApplicationBeginRequest
privatevoidApplication_BeginRequest(Objectsource,EventArgse)
{
HttpApplicationapplication=(HttpApplication)source;
HttpContextcontext=application.Context;

CSConfigurationconfig=CSConfiguration.GetConfig();

//Iftheinstallerismakingtherequestterminateearly
if(config.AppLocation.CurrentApplicationType==ApplicationType.Installer)
{
//CSContext.Create(context);
return;
}
CheckWWWStatus(config,context);

CSContext.Create(context,ReWriteUrl(context));

}
privatevoidCheckWWWStatus(CSConfigurationconfig,HttpContextcontext)
{
if(config.WWWStatus==WWWStatus.Ignore)
return;
conststringwithWWW="http://www.";
conststringnoWWW="http://";
stringrawUrl=context.Request.Url.ToString().ToLower();
boolisWWW=rawUrl.StartsWith(withWWW);

if(config.WWWStatus==WWWStatus.Remove&&isWWW)
{
context.Response.Redirect(rawUrl.Replace(withWWW,noWWW));
}
elseif(config.WWWStatus==WWWStatus.Require&&!isWWW)
{
context.Response.Redirect(rawUrl.Replace(noWWW,withWWW));
}


}
ReWriteUrl#regionReWriteUrl
privateboolReWriteUrl(HttpContextcontext)
{
//werenowallowingeachindividualapplicationtobeturnedonandoffindividually.Sobeforeweallow
//arequesttogothroughweneedtocheckifthisproductisdisabledandthepathisforthedisabledproduct,
//ifsowedisplaythedisabledproductpage.
//
//Imalsoallowingthepagerequesttogothroughifthepagerequestisforanadminpage.Inthepastifyou
//disabledtheforumsyouwerelockedout,nowwiththischeck,evenifyourenotonthesamemachinebutyoureaccessing
//anadminpaththerequestwillbeallowedtoproceed,wheretherestofthecheckswillensurethattheuserhasthe
//permissiontoaccessthespecificurl.
//UrlRewriting
//
//RewriteUrl(context);
stringnewPath=null;
stringpath=context.Request.Path;
boolisReWritten=SiteUrls.RewriteUrl(path,context.Request.Url.Query,outnewPath);
//verywachky.ThefirstcallintoReWritePathalwaysfailswitha404.
//callingReWritePathtwiceactuallyfixestheprobelmaswell.Instead,
//weusethesecondReWritePathoverloadanditseemstowork100%
//ofthetime.
if(isReWritten&&newPath!=null)
{
stringqs=null;
intindex=newPath.IndexOf(?);
if(index>=0)
{
qs=(index<(newPath.Length-1))?newPath.Substring(index+1):string.Empty;
newPath=newPath.Substring(0,index);
}
context.RewritePath(newPath,null,qs);
}
returnisReWritten;
}
#endregion
privatevoidValidateApplicationStatus(CSContextcntx)
{
if(!cntx.User.IsAdministrator)
{
stringdisablePath=null;
switch(cntx.Config.AppLocation.CurrentApplicationType)
{
caseApplicationType.Forum:
if(cntx.SiteSettings.ForumsDisabled)
disablePath="ForumsDisabled.htm";
break;
caseApplicationType.Weblog:
if(cntx.SiteSettings.BlogsDisabled)
disablePath="BlogsDisabled.htm";
break;
caseApplicationType.Gallery:
if(cntx.SiteSettings.GalleriesDisabled)
disablePath="GalleriesDisabled.htm";
break;
caseApplicationType.GuestBook:
if(cntx.SiteSettings.GuestBookDisabled)
disablePath="GuestBookDisabled.htm";
break;
caseApplicationType.Document://新增ugoer
if(cntx.SiteSettings.DocumentDisabled)
disablePath="DocumentsDisabled.htm";
break;
}
if(disablePath!=null)
{
stringerrorpath=cntx.Context.Server.MapPath(string.Format("~/Languages/{0}/errors/{1}",cntx.Config.DefaultLanguage,disablePath));
using(StreamReaderreader=newStreamReader(errorpath))
{
stringhtml=reader.ReadToEnd();
reader.Close();
cntx.Context.Response.Write(html);
cntx.Context.Response.End();
}
}
}
}
#endregion

}
}

在Web.Config中的设置:
<httpModules>
<addname="CommunityServer"type="CommunityServer.CSHttpModule,CommunityServer.Components"/>
<addname="Profile"type="Microsoft.ScalableHosting.Profile.ProfileModule,MemberRole,Version=1.0.0.0,Culture=neutral,PublicKeyToken=b7c773fb104e7562"/>
<addname="RoleManager"type="Microsoft.ScalableHosting.Security.RoleManagerModule,MemberRole,Version=1.0.0.0,Culture=neutral,PublicKeyToken=b7c773fb104e7562"/>
</httpModules>

CSHttpModule.csUML:

要完成HttpModule功效必要以下步骤:
1.编写一个类,完成IhttpModule接口
2.完成Init办法,而且注册必要的办法
3.完成注册的办法
4.完成Dispose办法,假如必要手工为类做一些扫除事情,能够增加Dispose办法的完成,但这不是必须的,一般能够不为Dispose办法增加任何代码。
5.在Web.config文件中,注册您编写的类
到这里我们还必要懂得一个Asp.Net的运转历程:

在图中第二步能够看到当哀求入手下手的时分,即刻就进进了HttpModule,在CS中因为完成了HttpModule的扩大CSHttpModule.cs类,因而当一个web哀求收回的时分(如:一个用户会见他的blog),CS体系起首挪用CSHttpModule.cs类,而且进进
publicvoidInit(HttpApplicationapplication)
该办法举行初始化事务:
application.BeginRequest+=newEventHandler(this.Application_BeginRequest);
application.AuthenticateRequest+=newEventHandler(Application_AuthenticateRequest);
application.Error+=newEventHandler(this.Application_OnError);
application.AuthorizeRequest+=newEventHandler(this.Application_AuthorizeRequest);
有事务就要有对应的处置办法:
privatevoidApplication_BeginRequest(Objectsource,EventArgse)
privatevoidApplication_AuthenticateRequest(Objectsource,EventArgse)
privatevoidApplication_OnError(Objectsource,EventArgse)
privatevoidApplication_AuthorizeRequest(Objectsource,EventArgse)
事务被初始化后就守候体系的触发,哀求进进下一步此时体系触发Application_BeginRequest事务,事务处置内容以下:
privatevoidApplication_BeginRequest(Objectsource,EventArgse)
{
HttpApplicationapplication=(HttpApplication)source;
HttpContextcontext=application.Context;

CSConfigurationconfig=CSConfiguration.GetConfig();

//Iftheinstallerismakingtherequestterminateearly
if(config.AppLocation.CurrentApplicationType==ApplicationType.Installer)
{
//CSContext.Create(context);
return;
}
CheckWWWStatus(config,context);

CSContext.Create(context,ReWriteUrl(context));
}
privatevoidCheckWWWStatus(CSConfigurationconfig,HttpContextcontext)
{
if(config.WWWStatus==WWWStatus.Ignore)
return;
conststringwithWWW="http://www.";
conststringnoWWW="http://";
stringrawUrl=context.Request.Url.ToString().ToLower();
boolisWWW=rawUrl.StartsWith(withWWW);

if(config.WWWStatus==WWWStatus.Remove&&isWWW)
{
context.Response.Redirect(rawUrl.Replace(withWWW,noWWW));
}
elseif(config.WWWStatus==WWWStatus.Require&&!isWWW)
{
context.Response.Redirect(rawUrl.Replace(noWWW,withWWW));
}

}
ReWriteUrl#regionReWriteUrl
privateboolReWriteUrl(HttpContextcontext)
{
//werenowallowingeachindividualapplicationtobeturnedonandoffindividually.Sobeforeweallow
//arequesttogothroughweneedtocheckifthisproductisdisabledandthepathisforthedisabledproduct,
//ifsowedisplaythedisabledproductpage.
//
//Imalsoallowingthepagerequesttogothroughifthepagerequestisforanadminpage.Inthepastifyou
//disabledtheforumsyouwerelockedout,nowwiththischeck,evenifyourenotonthesamemachinebutyoureaccessing
//anadminpaththerequestwillbeallowedtoproceed,wheretherestofthecheckswillensurethattheuserhasthe
//permissiontoaccessthespecificurl.
//UrlRewriting
//
//RewriteUrl(context);
stringnewPath=null;
stringpath=context.Request.Path;
boolisReWritten=SiteUrls.RewriteUrl(path,context.Request.Url.Query,outnewPath);
//verywachky.ThefirstcallintoReWritePathalwaysfailswitha404.
//callingReWritePathtwiceactuallyfixestheprobelmaswell.Instead,
//weusethesecondReWritePathoverloadanditseemstowork100%
//ofthetime.
if(isReWritten&&newPath!=null)
{
stringqs=null;
intindex=newPath.IndexOf(?);
if(index>=0)
{
qs=(index<(newPath.Length-1))?newPath.Substring(index+1):string.Empty;
newPath=newPath.Substring(0,index);
}
context.RewritePath(newPath,null,qs);
}
returnisReWritten;
}
#endregion
这个事务次要做两个事变
a:为收回哀求的用户初始化一个Context,初始化Context用到了线程中当地数据槽(LocalDataStoreSlot),把以后用户哀求的高低文(contextb)保留在为此哀求启示的内存中。
b:判别是不是必要重写URL(反省是不是必要重写的历程是对SiteUrls.config文件中正则表达式和对应Url处置的历程),假如必要重写URL,就实行asp.net级别上的RewritePath办法取得新的路径,新的路径才是真实的哀求信息地点的路径。这个专题不是讲URLRewrite,以是只需分明URL在这里就举行Rewrite就能够了,详细的前面专题会叙说。
处置完Application_BeginRequest落后程继向下实行,随后触发了Application_AuthenticateRequest(假如有伴侣不分明这个实行历程,能够经由过程调试中设置多个断点捕捉事务实行的按次。假如你还不会调试,能够留言悄悄的告知我,嘿嘿。),Application_AuthenticateRequest事务初始化一个context的Identity,实在CS供应了良多的Identity撑持,包含Microsoftpassport,可是今朝的版本中利用的是默许值System.Web.Security.FormsIdentity。详细代码以下:
privatevoidApplication_AuthenticateRequest(Objectsource,EventArgse)
{
HttpContextcontext=HttpContext.Current;
Providerp=null;
ExtensionModulemodule=null;
//Iftheinstallerismakingtherequestterminateearly
if(CSConfiguration.GetConfig().AppLocation.CurrentApplicationType==ApplicationType.Installer){
return;
}

//Onlycontinueifwehaveavalidcontext
//
if((context==null)||(context.User==null))
return;
try
{
//Logictohandlevariousauthenticationtypes
//
switch(context.User.Identity.GetType().Name.ToLower())
{
//Microsoftpassport
case"passportidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Windows
case"windowsidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Forms
case"formsidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
//Custom
case"customidentity":
p=(Provider)CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
module=ExtensionModule.Instance(p);
if(module!=null)
module.ProcessRequest();
else
gotodefault;
break;
default:
CSContext.Current.UserName=context.User.Identity.Name;
break;
}
}
catch(Exceptionex)
{
CSExceptionforumEx=newCSException(CSExceptionType.UnknownError,"ErrorinAuthenticateRequest",ex);
forumEx.Log();
throwforumEx;
}
////Gettherolestheuserbelongsto
////
//Rolesroles=newRoles();
//roles.GetUserRoles();
}
再上去是Application_AuthorizeRequest事务被触发,事务代码以下:
privatevoidApplication_AuthorizeRequest(Objectsource,EventArgse){
if(CSConfiguration.GetConfig().AppLocation.CurrentApplicationType==ApplicationType.Installer)
{
//CSContext.Create(context);
return;
}

HttpApplicationapplication=(HttpApplication)source;
HttpContextcontext=application.Context;
<P>CSContextcsContext=CSContext.Current;
//boolenableBannedUsersToLogin=CSContext.Current.SiteSettings.EnableBannedUsersToLogin;
&nC#中有两处地方用到new关键字,第一处也是最常见的一处是用在调用构造函数的时候,这种情况也是大家见的最多的一种。另一处是用在派生类中,作用有隐藏成员,切断继承关系等,相信第二处的用法大家明显要比第一处生疏。
海妖 该用户已被删除
沙发
发表于 2015-1-19 17:58:59 | 只看该作者
代码的可重用性差:由于是面向结构的编程方式,并且混合html,所以可能页面原型修改一点,整个程序都需要修改,更别提代码重用了。
深爱那片海 该用户已被删除
板凳
发表于 2015-1-26 13:05:54 | 只看该作者
是指转换后的Servlet程序代码的行数。这给调试代码带来一定困难。所以,在排除错误时,可以采取分段排除的方法(在可能出错的代码前后输出一些字符串,用字符串是否被输出来确定代码段从哪里开始出错)。
admin 该用户已被删除
地板
发表于 2015-2-10 03:14:12 | 只看该作者
ASP.net的速度是ASP不能比拟的。ASP.net是编译语言,所以,当第一次加载的时候,它会把所有的程序进行编译(其中包括worker进程,还有对语法进行编译,形成一个程序集),当程序编译后,执行速度几乎为0。
小妖女 该用户已被删除
5#
发表于 2015-2-28 18:03:49 | 只看该作者
代码的可重用性差:由于是面向结构的编程方式,并且混合html,所以可能页面原型修改一点,整个程序都需要修改,更别提代码重用了。
若天明 该用户已被删除
6#
发表于 2015-3-10 03:43:52 | 只看该作者
ASP(ActiveServerPages)是Microsfot公司1996年11月推出的WEB应用程序开发技术,它既不是一种程序语言,也不是一种开发工具,而是一种技术框架,不须使用微软的产品就能编写它的代码。
小女巫 该用户已被删除
7#
发表于 2015-3-17 04:12:09 | 只看该作者
但是java靠开源打出的一片天地,特别是在微软的垄断下能打开今天的局面还是有它的生命力的。
老尸 该用户已被删除
8#
发表于 2015-3-17 04:12:09 | 只看该作者
众所周知,Windows以易用而出名,也因此占据不少的服务器市场。
冷月葬花魂 该用户已被删除
9#
发表于 2015-3-23 19:10:43 | 只看该作者
JSP/Servlet虽然在国内目前的应用并不广泛,但是其前途不可限量。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-18 09:42

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表