《ASP.NET Core 6框架揭秘》实例演示[27]:ASP.NET Core 6 Minimal API的模拟实现

Minimal API仅仅是在基于IHost/IHostBuilder的服务承载系统上作了小小的封装而已,它利用WebApplication和WebApplicationBuilder这两个类型提供了更加简洁的API,同时提供了与现有API的兼容。[本文节选《ASP.NET Core 6框架揭秘》第17章]

一、基础模型
二、WebApplication
三、WebApplication的构建
     1. BootstrapHostBuilder
     2. ConfigureHostBuilder
     3. ConfigureWebHostBuilder
     4. WebApplicationBuilder
四、 工厂方法

一、基础模型

对于由WebApplication和WebApplicationBuilder构建的承载模型,我们没有必要了解其实现的每一个细节,知道其大致的设计和实现原理就可以了,所以本节会采用最简洁的代码模拟这两个类型的实现。如图1所示,代表承载应用的WebApplication对象是对一个IHost对象的封装,而且该类型自身也实现了IHost接口, WebApplication对象其实还是作为一个IHost对象被启动的。作为构建这的WebApplicationBuilder则是对一个IHostBuilder对象的封装,它对WebApplication对象的构建体现在利用封装的IHostBuilder对象构建一个对应的IHost对象,最终利用后者将WebApplication对象创建出来。

177895a79fdf3f7aca2cd8e48e009e1f.png

图17-8 完整的请求处理管道

二、WebApplication

WebApplication类型不仅仅实现了IHost接口,还同时实现IApplicationBuilder接口,所以中间件可以直接注册到这个对象上的。该类型还实现了IEndpointRouteBuilder接口,所以我们还能利用它进行路由注册,我们在20章才会涉及到路由,所以我们现在先忽略针对该接口的实现。下面的代码模拟WebApplication类型的实现。如代码片段所示,WebApplication的构造函数定义了一个IHost类型的参数,它利用这个对象完成了对IHost接口所有成员的实现,针对IApplicationBuilder接口成员的实现则利用创建的ApplicationBuilder对象来完成。WebApplication还提供了一个BuildRequestDelegate方法利用这个ApplicationBuilder对象完成了对中间件管道的构建。

public class WebApplication : IApplicationBuilder, IHost
{private readonly IHost _host;private readonly ApplicationBuilder _app;public WebApplication(IHost host){_host = host;_app = new ApplicationBuilder(host.Services);}IServiceProvider IHost.Services => _host.Services;Task IHost.StartAsync(CancellationToken cancellationToken) => _host.StartAsync(cancellationToken);Task IHost.StopAsync(CancellationToken cancellationToken) => _host.StopAsync(cancellationToken);IServiceProvider IApplicationBuilder.ApplicationServices { get => _app.ApplicationServices; set => _app.ApplicationServices = value; }IFeatureCollection IApplicationBuilder.ServerFeatures => _app.ServerFeatures;IDictionary<string, object?> IApplicationBuilder.Properties => _app.Properties;RequestDelegate IApplicationBuilder.Build() => _app.Build();IApplicationBuilder IApplicationBuilder.New() => _app.New();IApplicationBuilder IApplicationBuilder.Use(Func<RequestDelegate, RequestDelegate> middleware) => _app.Use(middleware);void IDisposable.Dispose() => _host.Dispose();public IServiceProvider Services => _host.Services;internal RequestDelegate BuildRequestDelegate() => _app.Build();...
}

WebApplication额外定义了如下的RunAsync和Run方法,它们分别以异步和同步方式启动承载的应用。调用这两个方法的时候可以指定监听地址,指定的地址被添加到IServerAddressesFeature特性中,而服务器正式利用这个特性来提供监听地址的。

public class WebApplication : IApplicationBuilder, IHost
{private readonly IHost _host;public ICollection<string> Urls => _host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");public Task RunAsync(string? url = null){Listen(url);return HostingAbstractionsHostExtensions.RunAsync(this);}public void Run(string? url = null){Listen(url);HostingAbstractionsHostExtensions.Run(this);}private void Listen(string? url){if (url is not null){var addresses = _host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");addresses.Clear();addresses.Add(url);}}...
}

三、WebApplication的构建

要创建一个WebApplication对象,只需要提供一个对应的IHost对象即可。IHost对象是通过IHostBuilder对象构建的,所以WebApplicationBuilder需要一个IHostBuilder对象,具体来说是一个HostBuilder对象。我们针对WebApplicationBuilder对象所作的一切设置最终都需要转移到这个HostBuilder对象上才能生效。为了提供更加简洁的API,WebApplicationBuilder类型提供了一系列的属性。比如它利用Serrvices属性提供了可以直接进行服务注册的IServiceCollection集合,利用Environment属性提供了表示当前承载环境的IWebHostEnvironment对象,利用Configuration属性提供的ConfigurationManager对象不仅可以作为IConfigurationBuilder对象帮助我们完成对配置系统的一切设置,它自身也可以作为IConfiguration对象为我们提供配置。

WebApplicationBuilder还定义了Host和WebHost属性,对应类型为ConfigureHostBuilder和ConfigureWebHostBuilder,它们分别实现了IHostBuilder和IWebHostBuilder接口,其目的是为了复用IHostBuilder和IWebHostBuilder接口承载的API(主要是扩展方法)。为了会尽可能使用现有方法对IHostBuilder对象进行初始化设置,它还使用了一个实现了IHostBuilder接口的BootstrapHostBuilder类型。有这些对象组成了WebApplicationBuilder针对HostBuilder的构建模型。如图2所示,WebApplicationBuilder的所有工作都是为了构建它封装的HostBuilder对象。

当WebApplicationBuilder初始化的时候,它除了会创建这个HostBuilder对象,还会创建存储服务注册的IServiceCollection对象,以及用来对配置进行设置的ConfigurationManager对象。接下来它会创建一个BootstrapHostBuilder对象,并将它参数调用相应的方法(比如ConfigureWebHostDefaults方法)将初始化设置收集起来,收集的服务注册和针对配置系统的设置分别转移到创建的IServiceCollection和ConfigurationManager对象中,其他设置直接应用到封装的HostBuilder对象上。

327520af23a4487458171c2f8ac9b559.png

图2 HostBuilder构建模型

WebApplicationBuilder在此之后会创建出代表承载环境的IWebHostEnvironment对象,并对Environment属性进行初始化。在得到表示承载上下文的WebHostBuilderContext对象之后,上述的ConfigureHostBuilder和ConfigureWebHostBuilder对象被创建出来,并赋值给Host和WebHost属性。与BootstrapHostBuilder作用类似,我们利用这两个对象所作的设置最终都会转移到上述的三个对象中。当WebApplicationBuilder进行WebApplication对象构建的时候,IServiceCollection对象存储的服务注册和ConfigurationManager对象承载配置最终转移到HostBuilder对象上。此时再利用后者构建出对应的IHost对象,代表承载应用的WebApplication对象最终由该对象构建出来。

1. BootstrapHostBuilder

如下所示的是我们模拟的BootstrapHostBuilder类型的定义。正如上面所说,这个它的作用是收集初始化IHostBuilder对象提供的设置并将它们分别应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上。这一使命体现在BootstrapHostBuilder的Apply方法上,该方法还通过一个输出参数返回创建的HostBuilderContext上下文。

public class BootstrapHostBuilder : IHostBuilder
{private readonly List<Action<IConfigurationBuilder>> _configureHostConfigurations = new();private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigurations = new();private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServices = new();private readonly List<Action<IHostBuilder>> _others = new();public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();public IHost Build() => throw new NotImplementedException();public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate){_configureHostConfigurations.Add(configureDelegate);return this;}public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate){_configureAppConfigurations.Add(configureDelegate);return this;}public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate){_configureServices.Add(configureDelegate);return this;}public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory){_others.Add(builder => builder.UseServiceProviderFactory(factory));return this;}public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory){_others.Add(builder => builder.UseServiceProviderFactory(factory));return this;}public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate){_others.Add(builder => builder.ConfigureContainer(configureDelegate));return this;}internal void Apply(IHostBuilder hostBuilder, ConfigurationManager configuration,IServiceCollection services, out HostBuilderContext builderContext){// 初始化针对宿主的配置var hostConfiguration = new ConfigurationManager();_configureHostConfigurations.ForEach( it => it(hostConfiguration));// 创建承载环境var environment = new HostingEnvironment(){ApplicationName = hostConfiguration[HostDefaults.ApplicationKey],EnvironmentName = hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,ContentRootPath = HostingPathResolver.ResolvePath(hostConfiguration[HostDefaults.ContentRootKey])};environment.ContentRootFileProvider = new PhysicalFileProvider(environment.ContentRootPath);// 创建HostBuilderContext上下文var hostContext = new HostBuilderContext(Properties){Configuration = hostConfiguration,HostingEnvironment = environment,};// 将针对宿主的配置添加到ConfigurationManager中configuration.AddConfiguration(hostConfiguration, true);// 初始化针对应用的配置_configureAppConfigurations.ForEach( it => it(hostContext, configuration));// 收集服务注册_configureServices.ForEach( it => it(hostContext, services));// 将针对依赖注入容器的设置应用到指定的IHostBuilder对象上_others.ForEach(it => it(hostBuilder));// 将自定义属性转移到指定的IHostBuilder对象上foreach (var kv in Properties){hostBuilder.Properties[kv.Key] = kv.Value;}builderContext = hostContext;}
}

除了Build方法,IHostBuilder接口中定义的所有方法的参数都是委托,所以实现的这些方法将提供的委托收集起来。在Apply方法中,我们通过执行这些委托对象,将初始化设置应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上,并根据初始化宿主配置构建出代表承载环境的HostingEnvironment对象。该方法最后根据承载环境结合配置将HostBuilderContext上下文创建出来,并以输出参数的形式返回。

internal static class HostingPathResolver
{public static string ResolvePath(string? contentRootPath) => ResolvePath(contentRootPath, .BaseDirectory);public static string ResolvePath(string? contentRootPath, string basePath) => string.IsNullOrEmpty(contentRootPath)? Path.GetFullPath(basePath): Path.IsPathRooted(contentRootPath)? Path.GetFullPath(contentRootPath): Path.GetFullPath(Path.Combine(Path.GetFullPath(basePath), contentRootPath));
}

2. ConfigureHostBuilder

ConfigureHostBuilder是在应用了BootstrapHostBuilder收集的初始化设置之后创建的,在创建该对象时提供了HostBuilderContext上下文, ConfigurationManager和IServiceCollection对象。提供的服务注册直接添加到IServiceCollection对象中,针对配置的设置已经应用到ConfigurationManager对象,直接针对IHostBuilder对象的设置则利用_configureActions字段暂存起来。

public class ConfigureHostBuilder : IHostBuilder
{private readonly ConfigurationManager _configuration;private readonly IServiceCollection _services;private readonly HostBuilderContext _context;private readonly List<Action<IHostBuilder>> _configureActions = new();internal ConfigureHostBuilder(HostBuilderContext context, ConfigurationManager configuration, IServiceCollection services){_configuration = configuration;_services = services;_context = context;}public IDictionary<object, object> Properties => _context.Properties;public IHost Build() => throw new NotImplementedException();public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)=> Configure(() => configureDelegate(_context, _configuration));public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate){var applicationName = _configuration[HostDefaults.ApplicationKey];var contentRoot = _context.HostingEnvironment.ContentRootPath;var environment = _configuration[HostDefaults.EnvironmentKey];configureDelegate(_configuration);// 与环境相关的三个配置不允许改变Validate(applicationName, HostDefaults.ApplicationKey, "Application name cannot be changed.");Validate(contentRoot, HostDefaults.ContentRootKey, "Content root cannot be changed.");Validate(environment, HostDefaults.EnvironmentKey, "Environment name cannot be changed.");return this;void Validate(string previousValue, string key, string message){if (!string.Equals(previousValue, _configuration[key], StringComparison.OrdinalIgnoreCase)){throw new NotSupportedException(message);}}}public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)=> Configure(() => configureDelegate(_context, _services));public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)=> Configure(() => _configureActions.Add(b => b.UseServiceProviderFactory(factory)));public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)=> Configure(() => _configureActions.Add(b => b.UseServiceProviderFactory(factory)));public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)=> Configure(() => _configureActions.Add(b => b.ConfigureContainer(configureDelegate)));private IHostBuilder Configure(Action configure){configure();return this;}internal void Apply(IHostBuilder hostBuilder) => _configureActions.ForEach(op => op(hostBuilder));
}

WebApplicationBuilder对象一旦被创建出来后,针对承载环境的配置是不能改变的,所以ConfigureHostBuilder的ConfigureHostConfiguration方法针对此添加了相应的验证。两个UseServiceProviderFactory方法和ConfigureContainer方法针对依赖注入容器的设置最终需要应用到IHostBuilder对象上,所以我们将方法中提供的委托对象利用configureActions字段存起来,并最终利用Apply方法应用到指定的IHostBuilder对象上。

3. ConfigureWebHostBuilder

ConfigureWebHostBuilder同样是在应用了BootstrapHostBuilder提供的初始化设置后创建的,创建该对象时能够提供WebHostBuilderContext上下文和承载配置和服务注册的ConfigurationManager和IServiceCollection对象。由于IWebHostBuilder接口定义的方法只涉及服务注册和针对配置的设置,所以方法提供的委托对象可以直接应用到这两个对象上。

public class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup
{private readonly WebHostBuilderContext _builderContext;private readonly IServiceCollection _services;private readonly ConfigurationManager _configuration;public ConfigureWebHostBuilder(WebHostBuilderContext builderContext, ConfigurationManager configuration, IServiceCollection services){_builderContext = builderContext;_services = services;_configuration = configuration;}public IWebHost Build() => throw new NotImplementedException();public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) => Configure(() => configureDelegate(_builderContext, _configuration));public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices) => Configure(() => configureServices(_services));public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices) => Configure(() => configureServices(_builderContext, _services));public string? GetSetting(string key) => _configuration[key];public IWebHostBuilder UseSetting(string key, string? value) => Configure(() => _configuration[key] = value);IWebHostBuilder ISupportsStartup.UseStartup(Type startupType) => throw new NotImplementedException();IWebHostBuilder ISupportsStartup.UseStartup<TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory) => throw new NotImplementedException();IWebHostBuilder ISupportsStartup.Configure(Action<IApplicationBuilder> configure) => throw new NotImplementedException();IWebHostBuilder ISupportsStartup.Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure) => throw new NotImplementedException();private IWebHostBuilder Configure(Action configure){configure();return this;}
}

我们在前面说过,传统承载方式将初始化操作定义在注册的Startup类型的编程方式在Minima API中已经不再被支持了,所以WebApplicationBuilder本不该实现ISupportsStartup接口,但是我们希望用户在采用这种编程方式时得到显式的提醒,所以依然让它实现该接口,并在实现的方法中抛出NotImplementedException类型的异常。

4. WebApplicationBuilder

如下的代码片段模拟了WebApplicationBuilder针对WebApplication的构建。它的构造函数会创建一个BootstrapHostBuilder对象,调用它的ConfigureDefaults和ConfigureWebHostDefaults扩展方法将初始化设置收集起来。ConfigureWebHostDefaults方法会利用提供的Action<IWebHostBuilder>委托进行中间件的注册,由于中间件的注册被转移到WebApplication对象上,并且它提供了一个BuildRequestDelegate方法返回由注册中间件组成的管道,所以在这里只需调用构建的WebApplication对象(通过_application字段表示,虽然此时尚未创建,但是中间件真正被注册时会被创建出来)的这个方法,并将返回的RequestDelegate对象作为参数调用IApplicationBuilder接口的Run方法将中间件管道注册为请求处理器。

public class WebApplicationBuilder
{private readonly HostBuilder _hostBuilder = new HostBuilder();private WebApplication _application;public ConfigurationManager Configuration { get; }  = new ConfigurationManager();public IServiceCollection Services { get; }  = new ServiceCollection();public IWebHostEnvironment Environment { get; }public ConfigureHostBuilder Host { get; }public ConfigureWebHostBuilder WebHost { get; }public ILoggingBuilder Logging { get; }public WebApplicationBuilder(WebApplicationOptions options){//创建BootstrapHostBuilder并利用它收集初始化过程中设置的配置、服务和针对依赖注入容器的设置var args = options.Args;var bootstrap = new BootstrapHostBuilder();bootstrap.ConfigureDefaults(null).ConfigureWebHostDefaults(webHostBuilder => webHostBuilder .Configure(app => app.Run(_application.BuildRequestDelegate()))).ConfigureHostConfiguration(config => {// 添加命令行配置源if (args?.Any() == true){config.AddCommandLine(args);}// 将WebApplicationOptions配置选项转移到配置中Dictionary<string, string>? settings = null;if (options.EnvironmentName is not null)  (settings ??= new())[HostDefaults.EnvironmentKey]   = options.EnvironmentName;if (options.ApplicationName is not null)   (settings ??= new())[HostDefaults.ApplicationKey]   = options.ApplicationName;if (options.ContentRootPath is not null)   (settings ??= new())[HostDefaults.ContentRootKey]   = options.ContentRootPath;if (options.WebRootPath is not null)   (settings ??= new())[WebHostDefaults.WebRootKey]   = options.EnvironmentName;if (settings != null){config.AddInMemoryCollection(settings);}});// 将BootstrapHostBuilder收集到配置和服务转移到Configuration和Services上// 将应用到BootstrapHostBuilder上针对依赖注入溶质的设置转移到_hostBuilder上// 得到BuilderContext上下文bootstrap.Apply(_hostBuilder, Configuration,  Services, out var builderContext);// 如果提供了命令行参数,在Configuration上添加对应配置源if (options.Args?.Any() == true){Configuration.AddCommandLine(options.Args);}// 构建WebHostBuilderContext上下文// 初始化Host、WebHost和Logging属性var webHostContext = (WebHostBuilderContext)builderContext  .Properties[typeof(WebHostBuilderContext)];Environment = webHostContext.HostingEnvironment;Host = new ConfigureHostBuilder( builderContext, Configuration, Services);WebHost = new ConfigureWebHostBuilder( webHostContext, Configuration, Services);Logging = new LogginigBuilder(Services);}public WebApplication Build(){// 将ConfigurationManager的配置转移到_hostBuilder_hostBuilder.ConfigureAppConfiguration(builder =>{builder.AddConfiguration(Configuration);foreach (var kv in  ((IConfigurationBuilder)Configuration).Properties){builder.Properties[kv.Key] = kv.Value;}});// 将添加的服务注册转移到_hostBuilder_hostBuilder.ConfigureServices((_, services) =>{foreach (var service in Services){services.Add(service);}});// 将应用到Host属性上的设置转移到_hostBuilderHost.Apply(_hostBuilder);// 利用_hostBuilder构建的IHost对象创建WebApplicationreturn _application = new WebApplication(_hostBuilder.Build());}
}

接下来BootstrapHostBuilder的ConfigureHostConfiguration方法被调用,我们利用它将提供的WebApplicationOptions配置选项转移到BootstrapHostBuilder针对宿主的配置上。针对IHostBuilder初始化设置应用到BootstrapHostBuilder对象上之后,我们调用其Apply方法将这些设置分别转移到承载服务注册和配置的IServiceCollection和ConfigurationManager对象,以及封装的HostBuilder对象上。Apply方法利用输出参数提供了HostBuilderContext上下文,我们进一步从中提取出WebHostBuilderContext上下文(GenericWebHostBuilder会将构建的WebHostBuilderContext上下文置于HostBuilderContext对象的属性字典中)。我们利用这个上下文将ConfigureHostBuilder和ConfigureWebHostBuilder对象创建出来,并作为Host和WebHost属性。用于对日志做进一步设置的Logging属性也在这里被初始化,返回的LoggingBuilder对象仅仅是对IServiceCollection对象的简单封装而已。

构建WebApplication对象的Build方法分别调用ConfigureAppConfiguration和ConfigureServices方法将ConfigurationManager和IServiceCollection对象承载的配置和服务注册转移到HostBuilder对象上。它接下来提取出Host属性返回的ConfigureHostBuilder对象,并调用其Apply方法将应用在该对象上针对依赖注入容器的设置转移到HostBuilder对象上。至此所有的设置全部转移到了HostBuilder对象上,我们调用其Build方法构建出对应的IHost对象后,最后利用后者将代码承载应用的WebApplication对象构建出来。我们将这个对象赋值到_application字段上,前面调用ConfigureWebHostDefaults扩展方法提供的委托会将它的BuildRequestDelegate方法构建的中间件管道作为请求处理器。

四、 工厂方法

代表承载应用的WebApplication对象由WebApplicationBuilder构建的,但是我们一般不会通过调用构造函数的方式来创建WebApplicationBuilder对象,这有违“面向接口”编程的原则,所以我们都会使用WebApplication类型提供的静态工厂方法来创建它。WebApplication除了提供了三个用于创建WebApplicationBuilder的CreateBuilder方法重载,还提供了一个直接创建WebApplication对象的Create方法。

public sealed class WebApplication
{public static WebApplicationBuilder CreateBuilder() => new WebApplicationBuilder(new WebApplicationOptions());public static WebApplicationBuilder CreateBuilder(string[] args){var options = new WebApplicationOptions();options.Args = args;return new WebApplicationBuilder(options);}public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options) => new WebApplicationBuilder(options, null);public static WebApplication Create(string[]? args = null){var options = new WebApplicationOptions();options.Args = args;return new WebApplicationBuilder(options).Build();}
}

本节内容通过针对WebApplication和WebApplicationBuilder这两个类型的实现模拟来讲解Minimal API的实现原理。一方面为了让讲解更加清晰,另一方面也出于篇幅的限制,不得不省去很多细枝末节的内容,但是设计思想和实现原理别无二致。上面提供的源代码也不是伪代码,如下所示的就是在“模拟的Minimal API”构建的ASP.NET Core应用,它是可以正常运行的。如果读者朋友们对真实的实现感兴趣,可以将它作为一个“向导”去探寻“真实的Minimal API”。

var app = App.WebApplication.Create();
app.Run(httpContext => httpContext.Response.WriteAsync("Hello World!"));
app.Run();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql的关联查询语句

一 内连接( inner join&#xff09; 1、多表中同时符合某种条件的数据记录的集合 (取两表公共部分) 2、inner join 可以缩写成 join 例如: select * from A,B WHERE A.idB.id 或者 select * from A inner join B on A.idB.id 内连接分为三类:{ &#xff08;1&#xff0…

高性能Server---Reactor模型

无处不在的C/S架构 在这个充斥着云的时代,我们使用的软件可以说99%都是C/S架构的&#xff01; 你发邮件用的Outlook,Foxmail等你看视频用的优酷&#xff0c;土豆等你写文档用的Office365,googleDoc&#xff0c;Evernote等你浏览网页用的IE,Chrome等(B/S是特殊的C/S)……C/S架构…

计算机控制系统的试题,计算机控制系统练习题(1)

21. 给出多通道复用一个D/A转换器的原理示意图。 答&#xff1a;22. 什么是信号重构&#xff1f;答&#xff1a;把离散信号变为连续信号的过程&#xff0c;称为信号重构&#xff0c;它是采样的逆过程。23. 写出零阶保持器的传递函数&#xff0c;引入零阶保持器对系统开环传递函…

springmvc_3(将数据放入map中)

jsp页面 结果 转载于:https://www.cnblogs.com/mohehpc/p/6491376.html

怎样用原生js配合css的transition写个无缝滚动

之所以想要写原生js配合css转换的无缝滚动&#xff0c;是因为之前在简书上看到一哥们写的一篇文章&#xff0c;说是在网上找了一堆js配合css transition属性写的轮播插件&#xff0c;可惜没有无缝的效果&#xff0c;结果他用原生js重写了一个可以无缝滚动的。好吧&#xff0c;我…

聊聊策略模式

1、简介策略模式就是把各个平等的具体实现进行抽象、封装成为独立的算法类&#xff0c;然后通过上下文和具体的算法类来进行交互。各个策略算法都是平等的&#xff0c;地位是一样的&#xff0c;正是由于各个算法的平等性&#xff0c;所以它们才是可以相互替换的。虽然我们可以动…

小学计算机课每周几节,小学信息技术课时多少

满意答案小学信息技术课程标准一、课程任务和教学目标中小学信息技术课程的主要任务是&#xff1a;培养学生对信息技术的兴趣和意识&#xff0c;让学生了解和掌握信息技术基本知识和技能&#xff0c;了解信息技术的发展及其应用对人类日常生活和科学技术的深刻影响。通过信息技…

张旭升20162329 2006-2007-2 《Java程序设计》第一周学习总结

20162329 2006-2007-2 《Java程序设计》第一周学习总结 教材学习内容总结 通过打书上的代码熟悉了Java编程的基本过程 教材学习中的问题和解决过程 1.因为我的虚拟机不可用所以我在Windows中安装了bash和git&#xff0c;但是由于Windows下bash中没有中文而且我英语又不是很好所…

《图解 HTTP》读书笔记(未完待续)

ARP 协议&#xff08;Address Resolution Protocol&#xff09;一种以解析地址的协议&#xff0c;根据通信双方的 IP 地址就可以查出对应的 MAC 地址。MAC&#xff08; Media Access Control Address&#xff09;地址是指网卡所属的固定的地址MIME&#xff0c;多部分对象集合&a…

SQL查询的安全方案

1.使用预处理语句防sql注入 2.写入数据库的数据要进行特殊字符转义 3.错误信息不返回给用户,记录到日志 4.定期做数据备份 5.不给查询用户root权限,合理分配权限 6.关闭远程访问数据库权限 7.修改root口令,不使用默认口令,使用较复杂口令 8.删除多余的用户 9.改变root用户的名称…

.NET 实现启动时重定向程序运行路径及 Windows 服务运行模式部署

日常工作中有时候会遇到需要将程序直接在服务器上运行&#xff0c;而不依赖于 IIS 托管的情况&#xff0c;直接运行有两种方式&#xff0c;一种是部署为 服务模式&#xff0c;另一种则是 直接启动 .NET 发布之后的 exe 文件以 控制台模式运行&#xff0c;控制台模式运行主要问题…

iOS runtime实战应用:关联对象

在开始之前建议先阅读iOS runtime的基础理解篇&#xff1a;iOS内功篇&#xff1a;runtime 有筒子在面试的时候&#xff0c;遇到这样一个问题&#xff1a;“如何給NSArray添加一个属性&#xff08;不能使用继承&#xff09;”&#xff0c;筒子立马蒙逼了&#xff0c;不能用继承&…

黑龙江科技大学计算机考研复试科目,2020年黑龙江科技大学计算机应用技术考研经验分享...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼育明考研备考策略随着IT业的迅猛发展&#xff0c;各高校计算机专业报名火爆&#xff0c;甚至文科学生跨专业报考时都会选择计算机。计算机专业竞争日趋激烈&#xff0c;那么如何在充分发挥公共科目优势的同时&#xff0c;尽量缩小专…

Mysql数据库安全性问题【防注入】

一、SQL注入实例 后台的插入语句代码&#xff1a; $unsafe_variable $_POST[user_input]; mysql_query("INSERT INTO table (column) VALUES ($unsafe_variable)"); 当POST的内容为&#xff1a; value); DROP TABLE table;--以上的整个SQL查询语句变成&#xff1…

Unexpected end of JSON input while parsing near错误解决方式(网上的方法)

原本是想创建一个create-react-app来着&#xff0c;但是在创建的中间会出现Unexpected end of JSON input while parsing near... 的错误。 在网上找到了一些方法&#xff0c;首先是清空npm的缓存。 npm cache clean --force 氮素&#xff0c;然并卵。near后面的内容变化了一下…

解决Qt5 Creator无法切换输入法(fcitx),Ubuntu中不能使用搜狗输入法录入汉字问题...

2016年6月8日修正&#xff0c;ubuntu 16.04 Qt5.7.0 以及 Qt5.6.1均测试通过在Qt5.3之前&#xff0c;我发布过解决办法 解决Qt5 Creator无法切换输入法&#xff08;fcitx&#xff09;&#xff0c;不能录入汉字问题&#xff0c;Qt5.4以及Qt5.5&#xff0c;旧办法失效&#xff0c…

目前市场上用于个人计算机的硬盘尺寸是,第5章-硬盘(计算机组装与维护).docx

ADDIN CNKISM.UserStyle一、选择题1.磁盘存储器的主要技术指标有多项&#xff0c;下面不属于硬盘指标的是( )。A.存储容量B.单碟容量C.转速D.带宽2.硬盘的平均寻道时间通常以毫秒为单位测量&#xff0c;是指( )。A.磁头从一个柱面移到另一个随机距离远的柱面所需的平均时间B.…

Xmemcached学习笔记一(安装memcached)

memcached有三种java客户端 第一种&#xff1a;Com.danga 包下面的memcached&#xff0c;需引入jar(本人用的是memcached-2.5.2.jar 文末附上附件需要的可以下载) 第二种&#xff1a;spyMemcached 第三种&#xff1a;XMemcached 据说第三种是使用最简单&#xff0c;最好用的&a…

WrapPanel 实现虚拟化

WrapPanel 实现虚拟化控件名&#xff1a;VirtualizingWrapPanel作者&#xff1a;WPFDevelopersOrg原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;项目使用 MIT 开源许可协议&#xff1b;众…

pdo连接mysql数据库(简洁明了)

一 实例化pdo对象 $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root);二 数据查询 1、如果不根据用户传过来的值进行操作,可以直接query sql $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root); $sql &qu…