`
xp9802
  • 浏览: 1184733 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Community Server专题九:MemberRole之Profile(转载)

 
阅读更多
上个专题我们讲到了Membership,了解了Membership可以让我们轻松的实现用户注册、登录、删除用户、用户更改密码等等一系列关于用户的基本操作,但是对于一个面向多用户的web程序,为了满足用户在访问站点时能够根据自己的喜好定制web站点的一些页面布局、皮肤,语言等等一些信息,Membership是不能满足要求的,因此,在CS中引入了Profile,Profile并不是为了实现用户个性化的机制,只是实现了个性化信息存储(在asp.net 2.0 beat2.0中页面的个性化可以用webpart实现的),目前CS的版本中还没有实现这个功能。但是我想,CS实现页面布局的个性化是迟早的事情,如果有可能,我会在CS专题结束后做一些asp.net 2.0 webpart的讲解。

CS中的Profile存储的主要是注册用户的email、timezone(时区)、日期格式、字体大小等等。通过这些信息CS就可以给注册并且登录后的用户个性化他们设置的页面,如:选择的皮肤、语言、字体大小,日期格式。

以显示日期格式为例,我们看看Profile给我们带来的效果:

我们先在CS中设置日期格式如下:



可以看到CS论坛中的日期格式如下:



接下来,设置过另外一个格式:



在论坛中可以看到格式也改变了:



如果你注销后,日期格式又会发生改变(改变为默认日期格式),也就是说这种设置完全是个人的。

当然,要实现这样的功能需要构建复杂的代码,Profile机制在这里仅仅提供数据的存储服务,这个专题我们只分析Profile机制是如何提供存储服务的。

先看看配置文件:

<profile enabled="true" >

     <providers>

         <add 

              name="CommunityServerSqlProvider" 

              type="CommunityServer.Components.CSProfileProvider, CommunityServer.Components"

              connectionStringName="SiteSqlServer"

              applicationName="dev"

            description="Stores and retrieves profile data from the local Microsoft SQL Server database"

         />

     </providers>



     <properties>

         <add name = "commonName" type = "string" />

         <add name = "birthdate" type = "DateTime" />

         <add name = "gender" type = "int" defaultValue = "0" />

         <add name = "dateFormat" type = "string" defaultValue="yyyy-MM-dd" />

         <add name = "publicEmail" type = "string" />

         <add name = "language" type = "string" />

         <add name = "webAddress" type = "string" />

         <add name = "webLog" type = "string" />

         <add name = "signature" type = "string" />

         <add name = "signatureFormatted" type = "string" />

         <add name = "location" type = "string" />

         <add name = "occupation" type = "string" />

         <add name = "interests" type = "string" />

         <add name = "msnIM" type = "string" />   

         <add name = "yahooIM" type = "string" />

         <add name = "aolIM" type = "string" />

         <add name = "icqIM" type = "string" />

         <add name = "qqIM" type = "string" />

         <add name = "enablePostPreviewPopup" type = "System.Boolean" defaultValue = "false" />

         <add name = "enableEmoticons" type = "System.Boolean" defaultValue = "true" />

         <add name = "timezone" type = "System.Double" defaultValue="0" />

         <add name = "fontsize" type = "int" defaultValue = "0" />

    </properties>

</profile>


< providers >节点下的内容我就不多说了。<properties>节点是配置的关键,该节点下面的信息就是一个注册用户可以存储的个性化信息。name是存储的名称,type是该名称保存数据的类型(其实还有一些信息可有可无,比如defaultValue等,表示默认值)。

用Reflector打开MemberRole,可以看到相比Membership,profile在类结构方面复杂很多,其实往往数据库设计的越简单,处理数据的类就越复杂。与Membership一样,通过实现 IConfigurationSectionHandler接口来读取在Web.config中配置。先实例化一个ProfileConfig,用来存储Providers节点下的信息和Properties节点下信息。

public class ProfileConfig
{
      // Methods
      public ProfileConfig(ProfileConfig parent);

      // Fields
      public bool AutomaticSaveEnabled;
      public bool Enabled;
      public string Inherits;
      public ProfilePropertySettingsCollection Properties;
      public ProfileProvider Provider;
}

分别存储在Provider与Properties属性下,看看UML图:



在图中,可以看到Properties是一个ProfilePropertySettingsCollection类的实例,该实例通过一个索引器来保存或者读取多个ProfilePropertySettings类实例,而ProfilePropertySettings实例中保存的就是<properties>节点下的<add>节点信息,如:<add name = "msnIM" type = "string" />。

由于Profile采用的也是Provider数据访问模型,所以可以看到上图中左边的三个类一路继承下来,但都是抽象类,没有具体实现。由于MemberRole中只实现了SQL Server的数据库存储实现,该实现在SqlProfileProvider类中可以看到:



(UML 中用斜体来表示抽象方法或者抽象类)

在Profile中还运用了httpModule,在web.config文件文件中,我们还看到这样一个配置文件:

<httpModules>

     ……

     <add name="Profile" type="Microsoft.ScalableHosting.Profile.ProfileModule, MemberRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7c773fb104e7562"/>

     ……

</httpModules>

这是一个

httpModule模块的配置,该模块有何用处我们具体看看。
以下类省略方法与属性具体内容:

public sealed class ProfileModule : IHttpModule
{
      // Events
      public event ProfileMigrateEventHandler MigrateAnonymous;
      public event ProfileEventHandler Personalize;
      public event ProfileAutoSaveEventHandler ProfileAutoSaving;

      // Methods
      static ProfileModule();
      [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
      public ProfileModule();
      public void Dispose();
      public void Init(HttpApplication app);
      private void OnEnter(object source, EventArgs eventArgs);
      private void OnLeave(object source, EventArgs eventArgs);
      private void OnPersonalize(ProfileEventArgs e);
      internal static void ParseDataFromDB(string[] names, string values, byte[] buf, SettingsPropertyValueCollection properties);
      internal static void PrepareDataForSaving(ref string allNames, ref string allValues, ref byte[] buf, bool binarySupported, SettingsPropertyValueCollection properties, bool userIsAuthenticated);

      // Fields
      private ProfileAutoSaveEventHandler _AutoSaveEventHandler;
      private ProfileEventHandler _eventHandler;
      private ProfileMigrateEventHandler _MigrateEventHandler;
      private static object s_Lock;
}



如果你对httpModule不了解,请看前面的专题。在Init方法中可以看到

public void Init(HttpApplication app)
{
      app.AcquireRequestState += new EventHandler(this.OnEnter);
      app.EndRequest += new EventHandler(this.OnLeave);
}


这里声明了两个事件,这两个事件分别在Http请求状态开始和结束Http请求时自动被激发。很有必要具体看看事件的处理内容:

Http请求状态开始时激发:

private void OnEnter(object source, EventArgs eventArgs)
{
      if (ProfileManager.Enabled)
      {
            HttpContext context1 = ((HttpApplication) source).Context;
            this.OnPersonalize(new ProfileEventArgs(context1));
            string text1 = AnonymousIdUtil.GetAnonymousIdInternal(context1);
            if ((context1.Request.IsAuthenticated && (text1 != null)) && ((text1.Length > 0) && (this._MigrateEventHandler != null)))
            {
                  AnonymousIdUtil.SetShowAnonymousId(context1, true);
                  ProfileMigrateEventArgs args1 = new ProfileMigrateEventArgs(context1, text1);
                  this._MigrateEventHandler(this, args1);
                  AnonymousIdUtil.SetShowAnonymousId(context1, false);
            }
      }
}


首先得到该次请求的上下文Context,这很重要,因为web请求是无状态的,要保存上下文信息来完成这次请求。接着调用OnPersonalize方法:

private void OnPersonalize(ProfileEventArgs e)
{
      if (this._eventHandler != null)
      {
            this._eventHandler(this, e);
      }
      if (e.Profile != null)
      {
            ProfileUtil.SetProfile(e.Context, e.Profile);
      }
      else
      {
            ProfileUtil.SetProfileDelayLoad(e.Context, true);
      }
}


该方法主要是初始化一个事件—Personalize(用处是在你的运用代码中如果还需要对这个请求开始的时候做一些个性化处理添加自己的方法,那么就可以通过向这个事件指定委托,委托指向的方法就会在这个时候被事件调用。)。之后判断e.Profile是否已经有内容,如果有就保存在context.Items["PRF"]中,如果没有就保存一个context.Items["PRFDL"] = true,这样后面就可以判断Profile的内容是否已经存储在请求的上下文中了。

再回到OnEnter方法中来,处理完OnPersonalize方法的调用后,判断是否是是已经验证过的匿名用户,根据判断后在看是否进行MigrateAnonymous事件的初始化,关于匿名用户的处理,后面会专门的专题讲解,这里就不多说。



看看结束Http请求时激发的方法:

private void OnLeave(object source, EventArgs eventArgs)
{
      if (ProfileManager.Enabled)
      {
            HttpApplication application1 = (HttpApplication) source;
            HttpContext context1 = application1.Context;
            if (((ProfileUtil.GetProfileInternal(context1) != null) && (ProfileUtil.GetProfileInternal(context1) != ProfileBase.SingletonInstance)) && ProfileManager.AutomaticSaveEnabled)
            {
                  if (this._AutoSaveEventHandler != null)
                  {
                        ProfileAutoSaveEventArgs args1 = new ProfileAutoSaveEventArgs(context1);
                        this._AutoSaveEventHandler(this, args1);
                        if (!args1.ContinueWithProfileAutoSave)
                        {
                              return;
                        }
                  }
                  ProfileUtil.GetProfileInternal(context1).Save();
            }
      }
}


其实只要对事件与委托了解,看懂就不成问题,主要就是根据在web.config中的设置判断是否初始化ProfileAutoSaving事件,然后ContinueWithProfileAutoSave的值来决定是否在请求结束的时候自动对Profile信息进行保存,其实这里的Profile信息就是一个ProfileBase实体,在OnPersonalize方法中被保存在context.Items["PRF"]中,当这次请求完成后,上下文context信息将自动被销毁。

在Profile中还有很重要的一部分,那就是数据的序化(串行化),通过一些方法把要存储的信息和信息的名称分别存放在数据库的两个字段中,这些信息是叠加的字符或者二进制串:







PropertyNames保存Profile的名称,PropertyValuesString保存以文本方式Profile的值,PropertyValuesBinary是二进制方式保存的Profile的值。具体分析一下:

PropertyNames内容:

“publicEmail:S:0:13:yahooIM:S:13:0:timezone:S:13:1:msnIM:S:14:13:commonName:B:0:-1:birthdate:S:27:9:gender:S:36:1:fontsize:B:0:-”

PropertyValuesString内容:

“ugoer@msn.com0ugoer@msn.com1982-8-141yyyy年M月d日, dddFalsezh-CN25694432www.ugoer.com无ugoer.cnblogs.comTrue”

“S”表示是用文本方式存储PropertyNames值,如果是“B”就表示用二进制存储。心细一点就会发现PropertyNames的值中包括所有的web.config中配置<properties>节点下的name,只是在这些name后面多了如“:S:13:1:”这样的信息,刚才说过S表示用文本方式存储,其实“13”指的是从第PropertyValuesString字段中存储信息的第13个字符开始后的1个字符长度的内容为timezone的值。这就是奥妙所在,那么PropertyValuesBinary是不是多余呢?有时你可能考虑需要用二进制的方式来保存这些信息,这样在数据库中这些信息就不容易直接看到,因此MemberRole中的Profile提供这样的选择,在asp.net 2.0 beta2中也同样提供这样的选择,毕竟众口难调嘛。

最后看看是如何对properties信息进行序化和反序化的:

数据操作的具体实现是SqlProfileProvider类中,SetPropertyValues方法实现Profile信息的保存,该方法中调用ProfileModule类的PrepareDataForSaving方法来实现数据的序化(其实我搞不明白为什么PrepareDataForSaving这个方法放在ProfileModule类下,这有点不合逻辑,不过方法是静态的,放在哪里都不影响):

internal static void PrepareDataForSaving(ref string allNames, ref string allValues, ref byte[] buf, bool binarySupported, SettingsPropertyValueCollection properties, bool userIsAuthenticated)
{
      StringBuilder builder1 = new StringBuilder();
      StringBuilder builder2 = new StringBuilder();
      MemoryStream stream1 = binarySupported ? new MemoryStream() : null;
      try
      {
            try
            {
                  bool flag1 = false;
                  foreach (SettingsPropertyValue value1 in properties)
                  {
                        if (!value1.IsDirty)
                        {
                              continue;
                        }
                        if (userIsAuthenticated || ((bool) value1.Property.Attributes["AllowAnonymous"]))
                        {
                              flag1 = true;
                              break;
                        }
                  }
                  if (!flag1)
                  {
                        return;
                  }
                  foreach (SettingsPropertyValue value2 in properties)
                  {
                        if (!userIsAuthenticated && !((bool) value2.Property.Attributes["AllowAnonymous"]))
                        {
                              continue;
                        }
                        if (value2.IsDirty || !value2.UsingDefaultValue)
                        {
                              int num1 = 0;
                              int num2 = 0;
                              string text1 = null;
                              if (value2.Deserialized && (value2.PropertyValue == null))
                              {
                                    num1 = -1;
                              }
                              else
                              {
                                    object obj1 = value2.SerializedValue;
                                    if (obj1 == null)
                                    {
                                          num1 = -1;
                                    }
                                    else
                                    {
                                          if (!(obj1 is string) && !binarySupported)
                                          {
                                                obj1 = Convert.ToBase64String((byte[]) obj1);
                                          }
                                          if (obj1 is string)
                                          {
                                                text1 = (string) obj1;
                                                num1 = text1.Length;
                                                num2 = builder2.Length;
                                          }
                                          else
                                          {
                                                byte[] buffer1 = (byte[]) obj1;
                                                num2 = (int) stream1.Position;
                                                stream1.Write(buffer1, 0, buffer1.Length);
                                                stream1.Position = num2 + buffer1.Length;
                                                num1 = buffer1.Length;
                                          }
                                    }
                              }
                              string[] textArray1 = new string[8] { value2.Name, ":", (text1 != null) ? "S" : "B", ":", num2.ToString(CultureInfo.InvariantCulture), ":", num1.ToString(CultureInfo.InvariantCulture), ":" } ;
                              builder1.Append(string.Concat(textArray1));
                              if (text1 != null)
                              {
                                    builder2.Append(text1);
                              }
                        }
                  }
                  if (binarySupported)
                  {
                        buf = stream1.ToArray();
                  }
            }
            finally
            {
                  if (stream1 != null)
                  {
                        stream1.Close();
                  }
            }
      }
      catch
      {
            throw;
      }
      allNames = builder1.ToString();
      allValues = builder2.ToString();
}

方法其实很简单,分析不难得出具体是如何工作的,我就不做具体的讲解,数据的反序化是在ParseDataFromDB方法中进行,该方法也在ProfileModule类下:

internal static void ParseDataFromDB(string[] names, string values, byte[] buf, SettingsPropertyValueCollection properties)
{
      if (((names != null) && (values != null)) && ((buf != null) && (properties != null)))
      {
            try
            {
                  for (int num1 = 0; num1 < (names.Length / 4); num1++)
                  {
                        string text1 = names[num1 * 4];
                        SettingsPropertyValue value1 = properties[text1];
                        if (value1 != null)
                        {
                              int num2 = int.Parse(names[(num1 * 4) + 2], CultureInfo.InvariantCulture);
                              int num3 = int.Parse(names[(num1 * 4) + 3], CultureInfo.InvariantCulture);
                              if ((num3 == -1) && !value1.Property.PropertyType.IsValueType)
                              {
                                    value1.PropertyValue = null;
                                    value1.IsDirty = false;
                                    value1.Deserialized = true;
                              }
                              if (((names[(num1 * 4) + 1] == "S") && (num2 >= 0)) && ((num3 > 0) && (values.Length >= (num2 + num3))))
                              {
                                    value1.SerializedValue = values.Substring(num2, num3);
                              }
                              if (((names[(num1 * 4) + 1] == "B") && (num2 >= 0)) && ((num3 > 0) && (buf.Length >= (num2 + num3))))
                              {
                                    byte[] buffer1 = new byte[num3];
                                    Buffer.BlockCopy(buf, num2, buffer1, 0, num3);
                                    value1.SerializedValue = buffer1;
                              }
                        }
                  }
            }
            catch
            {
            }
      }
}

其实数据序化和反序化运用很广泛,在一些项目中使用他可以变得很灵活,比如CRM系统中,用这种方式保存一些客户不是太重要的资料,这些资料往往不需要进行查询但是在对不同的客户定制CRM系统时经常需要添加或者修改,这个时候就可以采用。常常有人在埋怨asp.net 2.0 beta2中的membership操作并不简单,反而复杂化,比如:如果添加一个字段,需要对UI做不小的改动。我也有这样的感觉,但是当把membership与webpart结合在一起,那么可以实现一种从数据操作,到业务逻辑,再到UI的全过程实现。也就是说,如果开发一个webpart用来处理profile,当增加profile内容的时候,UI根据增加的信息,自动的在UI上生成内容的现实与设置,那就一劳永逸了。
分享到:
评论

相关推荐

    MemberRoleSystem

    asp.net2.0 用户 角色 管理操作 

    软考-考生常见操作说明-202405101400-纯图版.pdf

    软考官网--2024常见操作说明:包括如何绘制网络图、UML图、表格等 模拟作答系统是计算机技术与软件专业技术资格(水平)考试的电子化考试系统界面、作答过程的仿真系统,为各级别、各资格涉及输入和页面显示的部分题型提供体验性练习。

    setuptools-34.0.3.zip

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于遗传优化GA的三目标优化仿真【包括程序,注释,操作步骤】

    1.版本:matlab2022A。 2.包含:程序,中文注释,仿真操作步骤(使用windows media player播放)。 3.领域:遗传优化 4.仿真效果:仿真效果可以参考博客同名文章《基于遗传优化GA的三目标优化仿真》 5.内容:基于遗传优化GA的三目标优化仿真。遗传算法(Genetic Algorithm, GA)是一种模拟自然选择和遗传机制的全局搜索优化方法,广泛应用于解决复杂优化问题,包括具有多个目标的优化问题,即多目标遗传算法(Multi-Objective Genetic Algorithm, MOGA)。在这里,将三个目标函数进行统一的编码,通过单目标遗传优化的方式,同步求解三个目标函数的最优值。 6.注意事项:注意MATLAB左侧当前文件夹路径,必须是程序所在文件夹位置,具体可以参考视频录。

    基于单通道脑电信号的自动睡眠分期研究.zip

    本项目使用了Sleep-EDF公开数据集的SC数据进行实验,一共153条整晚的睡眠记录,使用Fpz-Cz通道,采样频率为100Hz 整套代码写的较为简洁,而且有添加相应的注释,因此进行分享,而且不仅仅说是睡眠分期,也可以作为学习如何使用神经网络去进行时序数据分类问题的一个入门项目,包括怎么用GRU、LSTM和Attention这些经典网络结构。 网络结构(具体可查看network.py文件): 网络整体结构类似于TinySleepNet,对RNN部分进行了修改,增加了双向RNN、GRU、Attention等网络结构,可根据参数进行调整选择。 定义了seq_len参数,可以更灵活地调整batch_size与seq_len。 数据集加载(具体可查看dataset.py文件) 直接继承自torch的Dataset,并定义了seq_len和shuffle_seed,方便调整输入,并复现实验。 训练(具体可查看train.py文件):

    setuptools-27.3.1.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-12.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-11.0.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-5.8-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    5.3.1_1二叉树的先中后序遍历.mp420240404-134540.png

    5.3.1_1二叉树的先中后序遍历.mp420240404-134540.png

    setuptools-1.3.1-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-40.1.1-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于JSP在线图书销售商城网站项目源码.zip

    这个是一个JSP在线图书销售商城网站项目,管理员角色包含以下功能:管理员登录,管理员账号管理,会员用户管理,订单管理,图书分类管理,图书管理,新闻管理,评价管理等功能。用户角色包含以下功能:用户首页,最新图书查看,会员登录注册,商城公告新闻查看,查看图书详情,查看评论以及发表评论,查看购物车,查看我的订单等功能。

    基于springboot的就业信息管理系统

    开发语言:Java JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql 5.6/5.7(或8.0) 数据库工具:Navicat 开发软件:idea 依赖管理包:Maven 代码+数据库保证完整可用,可提供远程调试并指导运行服务(额外付费)~ 如果对系统的中的某些部分感到不合适可提供修改服务,比如题目、界面、功能等等... 声明: 1.项目已经调试过,完美运行 2.需要远程帮忙部署项目,需要额外付费 3.本项目有演示视频,如果需要观看,请联系我v:19306446185 4.调试过程中可帮忙安装IDEA,eclipse,MySQL,JDK,Tomcat等软件 重点: 需要其他Java源码联系我,更多源码任你选,你想要的源码我都有! https://img-blog.csdnimg.cn/direct/e73dc0ac8d27434b86d886db5a438c71.jpeg

    setuptools-38.6.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    毕设-基于STM32的物联网农业监测与控制系统源码.zip

    毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip毕设-基于STM32的物联网农业监测与控制系统源码.zip

    基于STM32F4系列单片机,开发了一款手持热成像仪.zip

    基于单片机的系统

    JAVA五子棋手机网络对战游戏的设计与实现(源代码).zip

    JAVA五子棋手机网络对战游戏的设计与实现(源代码)

    setuptools-31.0.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-10.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

Global site tag (gtag.js) - Google Analytics