ASP.NET网页编程之剖析Asp.net中资本当地化的完成
一个很大的类库。应用程序之所以难以跨平台,在于直接调用了特定平台的接口,而一个巨大的类库,就能极大地减少应用程序对平台的依赖。asp.net本文将从Asp.net完成资本环球化和当地化的基础观点动手,论述在Asp.net1.1和Asp.net2.0中完成环球化和当地化的步骤、办法。一.基础观点1.为何要完成资本的当地化?
我们的站点大概为环球各个国度和区域的人所扫瞄,每一个国度和区域的人都有本身的言语文明特性。就拿我们巨大的故国为例,中国年夜陆用简体中文,港澳台则利用繁体中文。别的各个国度关于泉币、数字、日历等信息的表达格局各有分歧,我们国度多利用年代日的格局,而美国则是月日年。诸云云类的区分各式各样,我也就未几举例了。为了给我们的网站扫瞄者更好的用户体验,我们应当供应一个环球化的办理计划,只需用户选择了他的言语和地区,站点就依照他的言语文明习气来展示页面信息,这个历程能够叫做当地化。
2.地区性、流动地区性、非特定地区性、特定地区性
地区性称号遵守RFC1766尺度,格局为“<languagecode2>-<country/regioncode2>”,个中<languagecode2>是从ISO639-1派生的由两个小写字母组成的代码,<country/regioncode2>是从ISO3166派生的由两个年夜写字母组成的代码。比方,美国英语为“en-US”。在双字母言语代码不成用的情形中,将利用从ISO639-2派生的三字母代码;比方,三字母代码“div”用于利用Dhivehi言语的地区。某些地区性称号带有指定誊写标记的后缀;比方“-Cyrl”指定西里尔语誊写标记,“-Latn”指定拉丁语誊写标记。举例:
地区性称号
地区性标识符
言语-国度/区域
zh-CN
0x0804
中文-中国
zh-TW
0x0404
中文-台湾
zh-CHS
0x0004
简体中文
zh-CHT
0x7C04
繁体中文
en
0x0009
英语
en-US
0x0409
英语-美国
en-GB
0x0809
英语-英国
uz-UZ-Cyrl
0x0843
乌兹别克语(西里尔语)-乌兹别克斯坦
uz-UZ-Latn
0x0443
乌兹别克语(拉丁)-乌兹别克斯坦
流动地区性不辨别地区性。可使用空字符串("")按称号大概按地区性标识符0x007F来指定流动地区性。流动地区性由CultureInfo类的InvariantCulture属性来代表流动地区性的实例。流动地区性仅与英语言语联系关系,不与任何国度/区域联系关系。它几近可用在请求地区性的“全局化”定名空间中的一切办法中。假如你的程序举行字符串对照或巨细写变动操纵,则应当利用InvariantCulture确保不管体系怎样设置的地区性,举动都将依照InvariantCulture所代表的英语言语的流动地区性来完成。可是,流动地区性必需仅由必要不依附地区性的了局的历程(如体系服务)利用;不然,它失掉的了局大概在言语上不准确,大概在文明上分歧适。举例:CultureInfoInvc=NewCultureInfo("");
CultureInfoInvc=CultureInfo.InvariantCulture;这两行代码的感化不异,目标是取得流动地区性实例。
好比你如今要对一个DateTime的实例dateTime实行dateTime.ToString()办法。这个办法实践是利用你以后线程的CurrentCulture作为默许的地区性,依据这个地区性将日期实例转化为响应的字符串情势。那末假如我们此时不必要它依照线程或体系的地区性举行ToString操纵,那末我们应当用这个办法dateTime.ToString(“G”,CultureInfo.InvariantCulture)大概dateTime.ToString(“G”,DateTimeFormatInfo.InvariantInfo)。
非特定地区性是与某种言语联系关系但不与国度/区域联系关系的地区性。特定地区性是与某种言语和某个国度/区域联系关系的地区性。比方,“en”长短特定地区性,而“en-US”是特定地区性。注重,“zh-CHS”(简体中文)和“zh-CHT”(繁体中文)均为非特定地区性。
地区性有条理布局,即特定地区性的父级长短特定地区性,而非特定地区性的父级是InvariantCulture。CultureInfo类的Parent属性将前往与特定地区性联系关系的非特定地区性。假如特定地区性的资本在体系中不存在,或因别的缘故原由不成用,则利用非特定地区性的资本;假如非特定地区性的资本也不成用,那末利用主程序会合嵌进的资本。
3.完成当地化经常使用的范例、属性和办法
CultureInfo类暗示有关特定地区性的信息,包含地区性的称号、誊写系统和利用的日历,和有关对经常使用操纵(如格局化日期和排序字符串)供应信息的地区性特定工具的会见。CultureInfo类的实例化一样平常有两个路子,以下所示:
CultureInfoculture=CultureInfo.CreateSpecificCulture(name);
CultureInfoculture=newCultureInfo(name);
两者的区分是,利用第一种办法,只能创立流动地区性或特定地区性的CultureInfo实例。假如name为空字符串,则创建流动地区性的实例,假如name为非特定地区性,那末创建name联系关系的默许特定地区性的CultureInfo实例。第二种办法,则是创建一个name所指定的地区性的CultureInfo实例,它能够是流动的,非特定的或特定地区性的。
Thread类的CurrentCulture属性用来猎取或设置以后线程的地区性。它必需被设置为特定地区性。Thread.CurrentThread.CurrentCulture=newCultureInfo("en-US");假如Thread.CurrentThread.CurrentCulture=newCultureInfo("en");就会报错!
Thread类的CurrentUICulture属性用来猎取或设置资本办理器利用确当前地区性以便在运转时查找地区性特定的资本。这里的资本办理器能够联系关系为ResourceManger类。
Thread.CurrentThread.CurrentUICulture=newCultureInfo("en");
Thread.CurrentThread.CurrentUICulture=newCultureInfo("en-US");
ResourceManger类能够查找地区性特定的资本,当当地化资本不存在时供应代用资本,并撑持资本序列化。经常使用的ResourceManager的机关函数是publicResourceManager(string,Assembly)。其寄义是初始化ResourceManager类的新实例,它利用指定的根称号从给定的Assembly中查找资本文件。所谓根称号是比方名为“MyResource.en-US.resources”的资本文件的根称号为“MyResource”。在根称号的表达中能够加上定名空间,如“MyWebSite.Resource.UserFolder.MyResource”。而Assembly能够是必要挪用资本文件的页面地点的Assembly,如typeof(MyPage).Assembly。ResourceManager类的GetString办法用来取得资本文件中的指定键的值。举例:当已设置了线程的CurrentUICulture属性以后按以下办法。
ResourceManagerrm=newResourceManager("items",Assembly.GetExecutingAssembly());
Stringstr=rm.GetString("welcome");
假如想依照指定的地区性来取得资本则依照以下写法:
ResourceManagerrm=newResourceManager("items",Assembly.GetExecutingAssembly());
CultureInfoci=Thread.CurrentThread.CurrentCulture;
Stringstr=rm.GetString("welcome",ci);
二.在Asp.net1.1中完成资本当地化
起首应在网站项目WebTest中创建一个Resource文件夹,在这个文件夹中寄存全部项目公用的资本文件。好比我们创建了以下三个资本文件:MyResource.en.resx,MyResource.en-US.resx,MyResource.zh-CN.res。每一个资本文件中都有两个键值对,键值为State和Address。在必要利用资本文件的页面MyPage.aspx中挪用资本文件,以下所示:
Thread.CurrentThread.CurrentCulture=CultureInfo.CreateSpecificCulture("zh-CN");
Thread.CurrentThread.CurrentUICulture=Thread.CurrentThread.CurrentCulture;
ResourceManagerrm=newResourceManager("WebTest.Resource.MyResource",typeof(MyPage).Assembly);
Label1.Text=rm.GetString("State");
Label2.Text=rm.GetString("Address");
好了,这个时分Label1和Label2就依照MyResource.zh-CN.resx文件中的划定显现“州”和“地点”。以上是一个最基础最复杂的当地化办法,这里隐含着一些成绩,我们来一一办理并优化该办法。
1.怎样取得用户的默许地区性
经由过程用户扫瞄器“属性”->“言语”选项里的设置,取最下面那条作为用户的默许言语。
CultureInfocultureInfo=CultureInfo.CreateSpecificCulture(Request.UserLanguages);
Thread.CurrentThread.CurrentCulture=cultureInfo;
Thread.CurrentThread.CurrentUICulture=cultureInfo;
一样平常情形下,设定CurrentCulture和CurrentUICulture具有不异的地区性,固然也能够不不异,好比你划定CurrentCulture为en-US,而CurrentUICulture为zh-CN。那末如许酿成的效果是,页面中泉币、日期等信息都依照美国英语的格局显现,而必要从资本文件中取值的内容,资本办理器会从MyResource.zh-CN.resx文件里取得。
假如你的站点页面上并没有供应让用户选择言语的功效,那末也就是默许依照用户扫瞄器设置的地区性举行显现,因而你就能够把上述代码放在Global.asax.cs文件的Application_BeginRequest办法中。如许每次用户对页面收回哀求时,我们的程序城市起首举行地区性设置。
2.记着用户的地区性设置
经由过程会话能够记着扫瞄者的地区性设置或选择。可是这个操纵不克不及在Global.asax.cs文件中Application_BeginRequest办法中举行,由于当时会话还处于不成用形态。假如你的站点并没有供应让用户选择言语的功效,那末你也没甚么需要记着用户的地区性设置,只需依照下面先容的在Global.asax.cs文件中Application_BeginRequest办法里设置一下就能够了,不影响功能。这次要能够制止用户在半途俄然改动了扫瞄器中言语的设置,而网站仍依照会话中存储的地区性为用户显现页面内容的抵触。
假如你提过了让用户选择言语的功效,那明显要在页面程序中利用会话来纪录用户的地区性选择。由于从客户端到服务器段的每次哀求,服务器段城市开启一个新的线程举行处置和呼应。假如你的程序没有记着客户的选择,那末只能依照默许的地区性举行呼应。
3.资本办理器怎样查找指定地区性的响应资本文件?
在实行取值操纵时,也就是实行ResourceManager类的GetString办法时,资本办理器会依照以后线程的CurrentUICulture属性往寻觅绝对应的资本文件。有以下几种情形:
(1).好比以后CurrentUICulture对应的地区性是en-US,那末起首找MyResource.en-US.resx是不是存在,如存在则从中取值;如不存在,则看MyResource.en.resx是不是存在。
(2).好比以后CurrentUICulture对应的地区性是en,由于en长短特定地区性的,那末起首找其默许联系关系的特定地区性en-US的资本文件MyResource.en-US.resx是不是存在,如存在则从中取值;如不存在,则看MyResource.en.resx是不是存在。
(3).好比以后CurrentUICulture对应的地区性是en-GB,那末起首找资本文件MyResource.en-GB.resx,如不存在,则看MyResource.en.resx是不是存在,如存在则从中取值;如也不存在,则看en联系关系的默许特定地区性en-US的资本文件MyResource.en-US.resx是不是存在,假如此时MyResource.en-US.resx不存在,可是MyResource.en-CA.resx存在,则程序仍然会抛出找不到符合资本文件的非常。
因而我们能够总结一下,以后线程CurrentUICulture对应的是特定地区性时,资本办理器优先查找此特定地区性对应的资本文件,假如没找到,则往找其非特定地区性的资本文件,假如还没找到,再往找其非特定地区性联系关系的默许地区性的资本文件。以后线程CurrentUICulture对应的长短特定地区性时,资本办理器优先查找此非特定地区性对应的默许特定地区性的资本文件是不是存在,假如不存在,则往看此非特定地区性对应的资本文件是不是存在,假如也不存在则抛出非常。
4.怎样处置未供应当地化撑持的地区性?
假如站点没有供应响应的资本文件撑持用户默许的地区性,那末必需将其以后线程的CurrentUICulture转化为你站点默许的地区性,好比en-US或zh-CN。转化的机会有两个:
一是当你在取得Request.UserLanguages时,用其与设置文件中事后设定的被撑持的地区性举行对照,假如确认其为不被撑持的,那末立即设置CurrentUICulture为默许地区性。
二是在利用ResourceManager的GetString办法举行取值的时分,利用trycatch布局,捕捉MissingManifestResourceException非常,在非常处置中,起首将CurrentUICulture设为默许地区性,以后再从头利用GetString取值。
5.经由过程Web.config设定站点默许的culture和uiCulture
<globalizationrequestEncoding="utf-8"responseEncoding="utf-8"uiCulture="zh-CN"culture="en-US"/>
如上所示:划定站点的默许culture为en-US(此处必需为特定地区性),uiCulture为zh-CN。
固然你也能够在每一个页面的Page标签中举行逐页设定:<@PageCulture=“zh-CN”UICulture=“en”>。这里就不论web.config是怎样设置的,页面会依照Page标签的设定举行显现。
三.在Asp.net2.0中完成资本当地化
Asp.net2.0中为资本当地化供应了加倍多样的完成办法。我这里侧重谈其与Asp.net1.1中的分歧的地方。
1.经由过程Web.config设定站点默许的culture和uiCulture
在Asp.net1.1中利用web.config文件举行站点地区性设定的办法已讲过了,而在Asp.net2.0中其加倍天真。一般,您会想要站点中的一切页面都切合不异的地区性设置。只需按以下所示在web.config中,为globalization元素的UICulture和Culture(地区性)属性分派一个站点局限的“auto”值,注重这个“auto”值在Asp.net1.1中是不被承受的。<globalizationuiCulture="auto"culture="auto"/>auto的意义在于ASP.NET经由过程反省扫瞄器发送的HTTP题目猎取到的用户首选地区性设置,并利用这个地区性设置站点的默许地区性,即以后线程的CurrentUICulture和CurrentCulture属性。
除主动设置之外,您还能够为Asp.net指定一个站点的默许地区性:<globalizationuiCulture="auto:zh-CN"culture="auto:zh-CN"/>注重:只要当ASP.NET没法找到HTTP题目来断定用户的首选地区性,好比扫瞄器的“属性”->“言语”中没有任何地区性设置完整是空的时分,auto前面设定的默许地区性才会失效。
在web.config中举行了globalization设置以后,你的使用程序不必要写任何代码,线程的CurrentUICulture和CurrentCulture就会依照在globalization元素中设置的uiCulture和culture属性值取得地区性设置。假如没有举行globalization设置,则线程的CurrentUICulture和CurrentCulture就会默许为en-US。
2.利用Web.config文件跟踪用户的地区性选择
在Asp.net1.1中,那些供应了地区性选择的站点,一样平常利用会话来纪录用户的选择,以便在用户每次对站点收回哀求时,都依照用户选择的地区性对显现内容举行当地化。在Asp.net2.0中供应了另外一个办法,那就是利用web.config文件来跟踪用户的地区性选择。
您能够在web.config文件中增加一个名为LanguagePreference的基于字符串的设置文件属性来撑持匿名辨认用户地区性的功效。请注重anonymousIdentification元素的enabled属性必需为“true”,不然匿名辨认功效就不成用。
<anonymousIdentificationenabled="true"/>
<profile>
<properties>
<addname="LanguagePreference"type="string"defaultValue="auto"allowAnonymous="true"/>
</properties>
</profile>
上面我将论述在Asp.net2.0中怎样针对LanguagePreference属性编程。起首,能够写一个PageBase类,它承继自System.Web.UI.Page,并作为站点中一切页面类的基类。这么做的目标实在很复杂,就是为了将各个页面中一些配合的处置历程提炼出来放到基类中,以削减代码反复,进步可保护性。然后在PageBase类中写以下代码:protectedoverridevoidInitializeCulture()
{
base.InitializeCulture();
stringLanguagePreference=((ProfileCommon)this.Context.Profile).LanguagePreference;
//该用户初次会见本站,Profile.LanguagePreference为空时,辨认用户扫瞄器的言语设置
if(string.IsNullOrEmpty(LanguagePreference))
{
if(this.Context.Request.UserLanguages!=null)
{
LanguagePreference=this.Context.Request.UserLanguages;
((ProfileCommon)Context.Profile).LanguagePreference=LanguagePreference;
}
}
else
{
Thread.CurrentThread.CurrentUICulture=newCultureInfo(LanguagePreference);
Thread.CurrentThread.CurrentCulture=CultureInfo.CreateSpecificCulture(LanguagePreference);
}
}
System.Web.UI.Page类的InitializeCulture办法是在Asp.net2.0中新加的,它为以后线程设置Culture和UICulture。页面熟命周期已被计划为InitializeCulture办法先于页面的Init和Load运转。在上述代码中,起首利用((ProfileCommon)this.Context.Profile).LanguagePreference;取得以后LanguagePreference设置文件属性的值,判别其是不是为空,也就是是不是已为用户保留了地区性设置。假如为空,则从Http头中猎取用户的首选地区性设置,并经由过程((ProfileCommon)Context.Profile).LanguagePreference=LanguagePreference;保留用户的首选地区性设置。假如不为空,申明已保留了用户的地区性设置,那末利用这个地区性设置以后线程的CurrentUICulture和CurrentCulture属性。
假如Web.config中界说了<globalizationuiCulture="auto"culture="auto"/>,那末能够将上述代码简化为:protectedoverridevoidInitializeCulture()
{
base.InitializeCulture();
stringLanguagePreference=((ProfileCommon)this.Context.Profile).LanguagePreference;
if(!string.IsNullOrEmpty(LanguagePreference))
{
Thread.CurrentThread.CurrentUICulture=newCultureInfo(LanguagePreference);
Thread.CurrentThread.CurrentCulture=CultureInfo.CreateSpecificCulture(LanguagePreference);
}
else
{
((ProfileCommon)Context.Profile).LanguagePreference=Thread.CurrentThread.CurrentCulture.Name;
}
}
假如在站点中供应了让用户选择地区性的功效,好比在站点的母版页中放了一个选择言语的列表,那末能够经由过程以下语句来记着用户对地区性的选择:
protectedvoidlstLanguage_SelectedIndexChanged(objectsender,EventArgse)
{
if(lstLanguage.SelectedValue!="Auto")//默许选项是Auto
{
Profile.LanguagePreference=lstLanguage.SelectedValue;
}
else
{
Profile.LanguagePreference=null;
}
Response.Redirect(Request.Url.AbsolutePath);
}
注重Response.Redirect(Request.Url.AbsolutePath);这行代码,由于事务处置代码是在Page_Load以后实行的,如果想让页面敏捷产生变更必需实行重定向操纵。
3.在Asp.net2.0中利用资本文件
在站点中创建全局资本文件的时分,VS.Net2005会主动创建一个App_GlobalResources文件夹专门来寄存全局资本文件。所谓全局资本文件,也就是给站点中多个页面文件或母版页利用的资本文件。假定我们创立名为MyResource.resx和MyResource.zh-cn.resx的文件。在程序中我们可使用以下代码来取得资本文件中的值:this.lblCountry.Text=Resources.MyResource.Country;
个中Country是资本文件中的键。明显,这比Asp.net1.1中从资本文件猎取值要简单良多。
这里有两个成绩必要注重:第一,在创立一组具有不异根称号的资本文件时,没有地区性标示的文件必需创建,好比MyResource.resx是必需有的,别的如MyResource.en-gb.resx和MyResource.zh-cn.resx的创建是依据必要的。假如不创建MyResource.resx只创建了MyResource.zh-cn.resx等,则上述代码中的Resources定名空间下就不会呈现MyResource,因而上述代码编译没法经由过程。MyResource.resx中应当寄存站点默许言语的内容,以备在找不到与以后线程CurrentUICulture婚配的当地化资本文件或在当地化资本文件中找不到响应键值时利用。Asp.net是以MyResource.resx文件中的键为准,假设在MyResource.resx中不存在Country键,而在MyResource.zh-cn.resx中存在Country键,那末上述代码在编译时也会报错。第二,Asp.net在找不到响应地区的当地化资本时,不会呈报任何非常,会主动从MyResource.resx文件中猎取值,但其实不改动以后线程的CurrentUICulture。
在站点中创建部分资本文件的时分,VS.Net2005会主动创建一个App_LocalResources文件夹专门来寄存部分资本文件。所谓部分资本文件,也就是给站点中单一页面文件利用的资本文件。它的定名体例通常为Default.aspx.resx和Default.aspx.zh-cn.resx。如今我在Default资本文件中增加三个键Language、lblNavigation.Text和lblNavigation.ForeColor。个中我为Default.aspx.resx的lblNavigation.ForeColor设置blue,为Default.aspx.zh-cn.resx的lblNavigation.ForeColor设置red。在页面文件中Default.aspx中从部分资本文件里取得内容的办法以下有两种:
(1).<asp:LabelID="lblLanguage"runat="server"Text="<%$Resources:Language%>"></asp:Label>
(2).<asp:LabelID="lblNavigaion"runat="server"meta:resourcekey="lblNavigation"></asp:Label>
利用第一种办法时要注重利用标记$。利用第二种办法加倍天真,它能够一次性地为控件的良多属性设定值。
在这里仍旧有成绩必要注重:页面默许的部分资本文件必需被创建,好比Default.aspx.resx是必需的,而Default.aspx.zh-cn.resx则依据必要。假如你不创建默许的部分资本文件,而在页面中却要利用部分资本文件时,当利用第一种办法举行绑准时,出编译毛病;当利用第二种办法举行绑准时,不会出编译毛病,可是这些属性的设置全都没起感化,好像没写一样。
4.显现当地化图象
显现当地化图象也是Asp.net2.0的新功效。在Asp.net2.0中资本文件已不但限于string范例的键值对组合,它能够保留多品种型的文件。使用这一功效能够完成图象的当地化。实在所谓当地化图象,不过就是将给分歧地区性筹办的图象放到分歧的当地化资本文件中往。好比将LitwareSlogan.jpg放到MyResource.resx中,把LitwareSlogan.cn.jpg放到MyResource.zh-cn.resx中。
当分歧当地化版本的全局资本文件中含有当地化版本的图象文件时,您能够自界说一个名为MyLocalImage.ashx的处置程叙文件,基于用户的言语首选项来有前提地举行加载,代码以下所示。
页面中的挪用办法:
<asp:ImageID="Image1"runat="server"ImageUrl="~/MyLocalImage.ashx"/>
MyLocalImage.ashx的处置程序的写法:
publicclassMyLocalImage:IHttpHandler
{
publicvoidProcessRequest(HttpContextcontext)
{
context.Response.ContentType="image/png";
stringLanaguageReference=((ProfileCommon)context.Profile).LanguagePreference;
if(!string.IsNullOrEmpty(LanaguageReference))
{
Thread.CurrentThread.CurrentUICulture=newCultureInfo(LanaguageReference);
}
Bitmapbm=Resources.Litware.LitwareSlogan;
MemoryStreamimage=newMemoryStream();
bm.Save(image,ImageFormat.Png);
context.Response.BinaryWrite(image.GetBuffer());
}
}
MyLocalImage.ashx中界说的自界说处置程序类可以使用您之前在自界说InitializeCulture办法中看到的相似逻辑,在从全局资本文件中检索图象文件之前,初始化以后线程的CurrentUICulture设置。您大概疑问为什么在页面的基类中已设置了以后线程的CurrentUICulture,而在这里还要从头设置,那是由于这里的线程与基类中处置的线程不是统一线程。在该自界说处置程序准确初始化了CurrentUICulture设置以后,它便可经由过程MyResource.resx的强范例化资本类来会见图象文件。然后,便只需将图象文件的数位编写到HTTP呼应流。
c语言的编译器,几乎是所有新平台都有的。因此从这点上看,c语言的程序,比其他任何语言更加容易跨平台。 Asp.net脚本的出现,为ASP空间带来了更高的稳定性,同时也为程序员建站提供更高环境! 微软又推出ASP.NET。这不是ASP的简单升级,而是全新一代的动态网页实现系统,用于一台WEB服务器建立强大的应用程序。是微软发展的新体系结构.NET的一部分,是ASP和.NET技术的结合。 由于JSP/Servlet都是基于Java的,所以它们也有Java语言的最大优点——平台无关性,也就是所谓的“一次编写,随处运行(WORA–WriteOnce,RunAnywhere)”。除了这个优点,JSP/Servlet的效率以及安全性也是相当惊人的。 但是目前在CGI中使用的最为广泛的是Perl语言。所以,狭义上所指的CGI程序一般都是指Perl程序,一般CGI程序的后缀都是.pl或者.cgi。 能产生和执行动态、交互式、高效率的站占服务器的应用程序。运用ASP可将VBscript、javascript等脚本语言嵌入到HTML中,便可快速完成网站的应用程序,无需编译,可在服务器端直接执行。容易编写。 通过这次激烈的讨论,我从大家身上学到了太多,开阔了眼界,不管是支持我的还是骂我的,都感谢你们。 如今主流的Web服务器软件主要由IIS或Apache组成。IIS支持ASP且只能运行在Windows平台下,Apache支持PHP,CGI,JSP且可运行于多种平台,虽然Apache是世界使用排名第一的Web服务器平台。
页:
[1]