aspdotnet_fundamentals.md - brainchildservices/curriculum GitHub Wiki
The Startup class is where:
- Services required by the app are configured.
- The app's request handling pipeline is defined, as a series of middleware components.
Here's a sample Startup class:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<RazorPagesMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("RazorPagesMovieContext")));
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
The Startup class configures services and the app's request pipeline.
- Optionally includes a ConfigureServices method to configure the app's services. A service is a reusable component that provides app functionality. Services are registered in ConfigureServices and consumed across the app via dependency injection (DI) or ApplicationServices.
- Includes a Configure method to create the app's request processing pipeline.
ConfigureServices and Configure are called by the ASP.NET Core runtime when the app starts:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
The preceding sample is for Razor Pages; the MVC version is similar.
The Startup class is specified when the app's host is built. The Startup class is typically specified by calling the WebHostBuilderExtensions.UseStartup method on the host builder:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
The host provides services that are available to the Startup class constructor. The app adds additional services via ConfigureServices. Both the host and app services are available in Configure and throughout the app.
Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):
-
IWebHostEnvironment
-
IHostEnvironment
-
IConfiguration
public class Startup { private readonly IWebHostEnvironment _env; public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; _env = env; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { if (_env.IsDevelopment()) { } else { } } }
Most services are not available until the Configure method is called.
When the app defines separate Startup classes for different environments (for example, StartupDevelopment), the appropriate Startup class is selected at runtime. The class whose name suffix matches the current environment is prioritized. If the app is run in the Development environment and includes both a Startup class and a StartupDevelopment class, the StartupDevelopment class is used. For more information, see Use multiple environments.
See The host for more information on the host. For information on handling errors during startup, see Startup exception handling.
The ConfigureServices method is:
- Optional.
- Called by the host before the Configure method to configure the app's services.
- Where configuration options are set by convention.
The host may configure some services before Startup methods are called. For more information, see The host.
For features that require substantial setup, there are Add{Service} extension methods on IServiceCollection. For example, AddDbContext, AddDefaultIdentity, AddEntityFrameworkStores, and AddRazorPages:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(
options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
}
Adding services to the service container makes them available within the app and in the Configure method. The services are resolved via dependency injection or from ApplicationServices.
The Configure method is used to specify how the app responds to HTTP requests. The request pipeline is configured by adding middleware components to an IApplicationBuilder instance. IApplicationBuilder is available to the Configure method, but it isn't registered in the service container. Hosting creates an IApplicationBuilder and passes it directly to Configure.
The ASP.NET Core templates configure the pipeline with support for:
-
Developer Exception Page
-
Exception handler
-
HTTP Strict Transport Security (HSTS)
-
HTTPS redirection
-
Static files
-
ASP.NET Core MVC and Razor Pages
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } }
The preceding sample is for Razor Pages; the MVC version is similar.
Each Use extension method adds one or more middleware components to the request pipeline. For instance, UseStaticFiles configures middleware to serve static files.
Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the chain, if appropriate.
Additional services, such as IWebHostEnvironment, ILoggerFactory, or anything defined in ConfigureServices, can be specified in the Configure method signature. These services are injected if they're available.
For more information on how to use IApplicationBuilder and the order of middleware processing, see ASP.NET Core Middleware.
To configure services and the request processing pipeline without using a Startup class, call ConfigureServices and Configure convenience methods on the host builder. Multiple calls to ConfigureServices append to one another. If multiple Configure method calls exist, the last Configure call is used.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureServices(services =>
{
services.AddControllersWithViews();
})
.Configure(app =>
{
var loggerFactory = app.ApplicationServices
.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<Program>();
var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
var config = app.ApplicationServices.GetRequiredService<IConfiguration>();
logger.LogInformation("Logged in Configure");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
var configValue = config["MyConfigKey"];
});
});
});
}
ASP.NET Core includes a built-in dependency injection (DI) framework that makes configured services available throughout an app. For example, a logging component is a service.
Code to configure (or register) services is added to the Startup.ConfigureServices method. For example:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<RazorPagesMovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("RazorPagesMovieContext")));
services.AddControllersWithViews();
services.AddRazorPages();
}
Services are typically resolved from DI using constructor injection. With constructor injection, a class declares a constructor parameter of either the required type or an interface. The DI framework provides an instance of this service at runtime.
The following example uses constructor injection to resolve a RazorPagesMovieContext from DI:
public class IndexModel : PageModel
{
private readonly RazorPagesMovieContext _context;
public IndexModel(RazorPagesMovieContext context)
{
_context = context;
}
// ...
public async Task OnGetAsync()
{
Movies = await _context.Movies.ToListAsync();
}
}
If the built-in Inversion of Control (IoC) container doesn't meet all of an app's needs, a third-party IoC container can be used instead.