1. ASP.NET框架概述

ASP.NET是微软推出的一个用于构建Web应用程序的开发框架,它是.NET平台的一部分。ASP.NET提供了强大的工具和库,使开发者能够快速构建功能丰富、性能优越的Web应用程序。

ASP.NET主要有两种开发模型:

  • ASP.NET Core:跨平台、开源、模块化的框架,支持Windows、macOS和Linux
  • ASP.NET Framework:仅支持Windows的传统框架

本教程将主要聚焦于ASP.NET Core,因为它是微软未来的发展方向,具有更好的性能和更灵活的部署选项。

1.1 ASP.NET Core的优势

  • 跨平台支持:可以在Windows、macOS和Linux上开发和部署
  • 高性能:经过优化的核心管道,比传统ASP.NET更快
  • 模块化设计:只包含需要的功能,减少应用程序的体积
  • 开源:源代码公开,社区活跃
  • 统一编程模型:MVC、Web API和Web Pages合并到一个框架中

2. 开发环境搭建

在开始开发之前,我们需要搭建合适的开发环境。

2.1 安装必要软件

  1. 安装.NET SDK: 访问https://dotnet.microsoft.com/download下载并安装最新的.NET SDK。本教程基于.NET 6.0 LTS版本。

  2. 安装代码编辑器

    • Visual Studio 2022(推荐):功能最全面的IDE
    • Visual Studio Code:轻量级跨平台编辑器
  3. 安装其他工具

    • Git:版本控制工具
    • SQL Server/Express:数据库(可选)

2.2 验证安装

打开命令行工具,运行以下命令验证.NET SDK安装:

dotnet --version 

如果安装成功,命令将返回已安装的.NET版本号。

3. 创建第一个ASP.NET Core项目

3.1 使用命令行创建项目

打开命令行工具,运行以下命令创建一个新的ASP.NET Core MVC项目:

dotnet new mvc -n MyWebApp cd MyWebApp 

这将创建一个名为”MyWebApp”的新ASP.NET Core MVC项目。

3.2 项目结构解析

创建的项目包含以下主要文件和文件夹:

  • Program.cs:应用程序的入口点,配置主机和中间件管道
  • Startup.cs:配置服务和应用程序请求管道(在.NET 6及更高版本中,这些配置已合并到Program.cs)
  • Controllers文件夹:包含处理HTTP请求的控制器类
  • Views文件夹:包含Razor视图文件
  • Models文件夹:包含表示应用程序数据的模型类
  • wwwroot文件夹:包含静态文件,如CSS、JavaScript和图像
  • appsettings.json:应用程序配置文件

3.3 运行应用程序

在项目根目录下运行以下命令启动应用程序:

dotnet run 

应用程序启动后,打开浏览器并访问https://localhost:5001(或http://localhost:5000)查看默认页面。

4. 基础配置与依赖注入

4.1 依赖注入简介

依赖注入(DI)是一种设计模式,用于实现控制反转(IoC)。ASP.NET Core内置了依赖注入容器,使我们可以轻松管理应用程序的服务。

4.2 注册服务

在Program.cs中,我们可以使用以下方法注册服务:

// 单例模式,整个应用程序生命周期内只创建一个实例 builder.Services.AddSingleton<IMyService, MyService>(); // 作用域模式,每个请求创建一个实例 builder.Services.AddScoped<IMyService, MyService>(); // 瞬时模式,每次请求都创建一个新实例 builder.Services.AddTransient<IMyService, MyService>(); 

4.3 使用服务

在控制器或其他类中,我们可以通过构造函数注入来使用已注册的服务:

public class HomeController : Controller { private readonly IMyService _myService; public HomeController(IMyService myService) { _myService = myService; } public IActionResult Index() { var data = _myService.GetData(); return View(data); } } 

5. MVC模式深入理解

5.1 模型(Model)

模型代表应用程序的数据和业务逻辑。在ASP.NET Core MVC中,模型通常是一个简单的类,用于表示数据实体。

public class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } } 

5.2 视图(View)

视图负责呈现用户界面。在ASP.NET Core MVC中,视图使用Razor语法编写,可以包含C#代码和HTML。

@model IEnumerable<Product> <h1>Products</h1> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Price</th> <th>Category</th> </tr> </thead> <tbody> @foreach (var product in Model) { <tr> <td>@product.Id</td> <td>@product.Name</td> <td>@product.Description</td> <td>@product.Price.ToString("C")</td> <td>@product.Category</td> </tr> } </tbody> </table> 

5.3 控制器(Controller)

控制器处理用户输入,与模型交互,并选择视图来呈现用户界面。

public class ProductController : Controller { private readonly IProductRepository _productRepository; public ProductController(IProductRepository productRepository) { _productRepository = productRepository; } // GET: Product public IActionResult Index() { var products = _productRepository.GetAllProducts(); return View(products); } // GET: Product/Details/5 public IActionResult Details(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } return View(product); } // GET: Product/Create public IActionResult Create() { return View(); } // POST: Product/Create [HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(Product product) { if (ModelState.IsValid) { _productRepository.AddProduct(product); return RedirectToAction(nameof(Index)); } return View(product); } // GET: Product/Edit/5 public IActionResult Edit(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } return View(product); } // POST: Product/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public IActionResult Edit(int id, Product product) { if (id != product.Id) { return NotFound(); } if (ModelState.IsValid) { _productRepository.UpdateProduct(product); return RedirectToAction(nameof(Index)); } return View(product); } // GET: Product/Delete/5 public IActionResult Delete(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } return View(product); } // POST: Product/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public IActionResult DeleteConfirmed(int id) { _productRepository.DeleteProduct(id); return RedirectToAction(nameof(Index)); } } 

6. 数据访问与Entity Framework Core

6.1 Entity Framework Core简介

Entity Framework Core (EF Core) 是一个轻量级、可扩展、跨平台版本的Entity Framework数据访问技术。它是ASP.NET Core中推荐的数据访问方式。

6.2 安装EF Core

在项目中安装EF Core相关NuGet包:

dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools 

6.3 创建数据模型

public class Product { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } } public class Order { public int Id { get; set; } public DateTime OrderDate { get; set; } public string CustomerName { get; set; } public string CustomerEmail { get; set; } public string ShippingAddress { get; set; } public List<OrderDetail> OrderDetails { get; set; } } public class OrderDetail { public int Id { get; set; } public int OrderId { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public Product Product { get; set; } public Order Order { get; set; } } 

6.4 创建DbContext

public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<OrderDetail>() .HasKey(od => new { od.OrderId, od.ProductId }); modelBuilder.Entity<OrderDetail>() .HasOne(od => od.Order) .WithMany(o => o.OrderDetails) .HasForeignKey(od => od.OrderId); modelBuilder.Entity<OrderDetail>() .HasOne(od => od.Product) .WithMany() .HasForeignKey(od => od.ProductId); } } 

6.5 配置连接字符串

在appsettings.json文件中添加数据库连接字符串:

{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=MyWebAppDb;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } 

6.6 注册DbContext

在Program.cs中注册DbContext:

builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); 

6.7 创建和应用数据库迁移

# 创建初始迁移 dotnet ef migrations add InitialCreate # 应用迁移到数据库 dotnet ef database update 

6.8 创建仓储模式

public interface IProductRepository { IEnumerable<Product> GetAllProducts(); Product GetProductById(int id); void AddProduct(Product product); void UpdateProduct(Product product); void DeleteProduct(int id); bool SaveChanges(); } public class ProductRepository : IProductRepository { private readonly AppDbContext _context; public ProductRepository(AppDbContext context) { _context = context; } public IEnumerable<Product> GetAllProducts() { return _context.Products.ToList(); } public Product GetProductById(int id) { return _context.Products.Find(id); } public void AddProduct(Product product) { _context.Products.Add(product); } public void UpdateProduct(Product product) { _context.Entry(product).State = EntityState.Modified; } public void DeleteProduct(int id) { var product = _context.Products.Find(id); if (product != null) { _context.Products.Remove(product); } } public bool SaveChanges() { return (_context.SaveChanges() >= 0); } } 

在Program.cs中注册仓储:

builder.Services.AddScoped<IProductRepository, ProductRepository>(); 

7. 身份认证与授权

7.1 ASP.NET Core Identity简介

ASP.NET Core Identity是一个成员资格系统,它为ASP.NET Core应用程序提供登录功能。我们可以使用Identity来注册用户、登录用户、管理用户和角色等。

7.2 添加Identity服务

在项目中安装Identity相关NuGet包:

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore dotnet add package Microsoft.AspNetCore.Identity.UI 

7.3 创建Identity用户和角色类

public class ApplicationUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } } public class ApplicationRole : IdentityRole { public string Description { get; set; } } 

7.4 更新DbContext以支持Identity

public class AppDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string> { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<OrderDetail>() .HasKey(od => new { od.OrderId, od.ProductId }); modelBuilder.Entity<OrderDetail>() .HasOne(od => od.Order) .WithMany(o => o.OrderDetails) .HasForeignKey(od => od.OrderId); modelBuilder.Entity<OrderDetail>() .HasOne(od => od.Product) .WithMany() .HasForeignKey(od => od.ProductId); } } 

7.5 配置Identity服务

在Program.cs中添加Identity服务:

builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options => { options.Password.RequireDigit = true; options.Password.RequiredLength = 8; options.Password.RequireNonAlphanumeric = true; options.Password.RequireUppercase = true; options.Password.RequireLowercase = true; options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15); }) .AddEntityFrameworkStores<AppDbContext>() .AddDefaultTokenProviders(); builder.Services.AddRazorPages(); 

7.6 添加身份验证中间件

在Program.cs中添加身份验证中间件:

app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.MapRazorPages(); 

7.7 创建和应用Identity迁移

# 创建Identity迁移 dotnet ef migrations add Identity # 应用迁移到数据库 dotnet ef database update 

7.8 使用授权属性

在控制器或操作方法上使用[Authorize]属性来限制访问:

[Authorize] public class OrderController : Controller { // 只有经过身份验证的用户才能访问此控制器 } public class ProductController : Controller { // 所有用户都可以访问 public IActionResult Index() { // ... } // 只有经过身份验证的用户才能访问 [Authorize] public IActionResult Create() { // ... } // 只有属于"Admin"角色的用户才能访问 [Authorize(Roles = "Admin")] public IActionResult Delete(int id) { // ... } } 

8. 高级功能实现

8.1 API开发

ASP.NET Core支持创建Web API,我们可以使用它来构建RESTful服务。

8.1.1 创建API控制器

[ApiController] [Route("api/[controller]")] public class ProductsApiController : ControllerBase { private readonly IProductRepository _productRepository; public ProductsApiController(IProductRepository productRepository) { _productRepository = productRepository; } // GET: api/ProductsApi [HttpGet] public ActionResult<IEnumerable<Product>> GetProducts() { var products = _productRepository.GetAllProducts(); return Ok(products); } // GET: api/ProductsApi/5 [HttpGet("{id}")] public ActionResult<Product> GetProduct(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } return Ok(product); } // POST: api/ProductsApi [HttpPost] public ActionResult<Product> PostProduct(Product product) { _productRepository.AddProduct(product); _productRepository.SaveChanges(); return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product); } // PUT: api/ProductsApi/5 [HttpPut("{id}")] public IActionResult PutProduct(int id, Product product) { if (id != product.Id) { return BadRequest(); } _productRepository.UpdateProduct(product); _productRepository.SaveChanges(); return NoContent(); } // DELETE: api/ProductsApi/5 [HttpDelete("{id}")] public IActionResult DeleteProduct(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } _productRepository.DeleteProduct(id); _productRepository.SaveChanges(); return NoContent(); } } 

8.1.2 配置JSON格式

在Program.cs中配置JSON序列化选项:

builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; options.JsonSerializerOptions.WriteIndented = true; }); 

8.2 缓存

8.2.1 内存缓存

在Program.cs中添加内存缓存服务:

builder.Services.AddMemoryCache(); 

在控制器中使用内存缓存:

public class ProductController : Controller { private readonly IProductRepository _productRepository; private readonly IMemoryCache _memoryCache; public ProductController(IProductRepository productRepository, IMemoryCache memoryCache) { _productRepository = productRepository; _memoryCache = memoryCache; } // GET: Product public IActionResult Index() { const string cacheKey = "ProductList"; if (!_memoryCache.TryGetValue(cacheKey, out IEnumerable<Product> products)) { products = _productRepository.GetAllProducts(); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)) .SetSlidingExpiration(TimeSpan.FromMinutes(5)); _memoryCache.Set(cacheKey, products, cacheEntryOptions); } return View(products); } } 

8.2.2 分布式缓存(Redis)

首先安装Redis缓存NuGet包:

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis 

在Program.cs中配置Redis缓存:

builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost:6379"; options.InstanceName = "MyWebApp_"; }); 

在控制器中使用分布式缓存:

public class ProductController : Controller { private readonly IProductRepository _productRepository; private readonly IDistributedCache _distributedCache; public ProductController(IProductRepository productRepository, IDistributedCache distributedCache) { _productRepository = productRepository; _distributedCache = distributedCache; } // GET: Product public async Task<IActionResult> Index() { const string cacheKey = "ProductList"; var productsJson = await _distributedCache.GetStringAsync(cacheKey); IEnumerable<Product> products; if (productsJson != null) { products = JsonConvert.DeserializeObject<IEnumerable<Product>>(productsJson); } else { products = _productRepository.GetAllProducts(); productsJson = JsonConvert.SerializeObject(products); var options = new DistributedCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)) .SetSlidingExpiration(TimeSpan.FromMinutes(5)); await _distributedCache.SetStringAsync(cacheKey, productsJson, options); } return View(products); } } 

8.3 会话状态

在Program.cs中添加会话服务:

builder.Services.AddDistributedMemoryCache(); // 使用内存存储会话数据 builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); 

添加会话中间件:

app.UseSession(); 

在控制器中使用会话:

public class CartController : Controller { private readonly IProductRepository _productRepository; public CartController(IProductRepository productRepository) { _productRepository = productRepository; } // POST: Cart/Add/5 [HttpPost] public IActionResult Add(int id) { var product = _productRepository.GetProductById(id); if (product == null) { return NotFound(); } List<Product> cart; if (HttpContext.Session.Get<List<Product>>("Cart") == null) { cart = new List<Product>(); } else { cart = HttpContext.Session.Get<List<Product>>("Cart"); } cart.Add(product); HttpContext.Session.Set("Cart", cart); return RedirectToAction(nameof(Index)); } // GET: Cart public IActionResult Index() { List<Product> cart = HttpContext.Session.Get<List<Product>>("Cart") ?? new List<Product>(); return View(cart); } // POST: Cart/Remove/5 [HttpPost] public IActionResult Remove(int id) { List<Product> cart = HttpContext.Session.Get<List<Product>>("Cart") ?? new List<Product>(); var product = cart.FirstOrDefault(p => p.Id == id); if (product != null) { cart.Remove(product); HttpContext.Session.Set("Cart", cart); } return RedirectToAction(nameof(Index)); } } 

8.4 文件上传

8.4.1 创建文件上传视图

@{ ViewData["Title"] = "Upload File"; } <h1>Upload File</h1> <div class="row"> <div class="col-md-4"> <form asp-action="Upload" enctype="multipart/form-data"> <div class="form-group"> <label for="file" class="control-label">Select File</label> <input type="file" name="file" class="form-control" /> </div> <div class="form-group"> <input type="submit" value="Upload" class="btn btn-primary" /> </div> </form> </div> </div> 

8.4.2 实现文件上传控制器

public class FileController : Controller { private readonly IWebHostEnvironment _hostEnvironment; public FileController(IWebHostEnvironment hostEnvironment) { _hostEnvironment = hostEnvironment; } // GET: File/Upload public IActionResult Upload() { return View(); } // POST: File/Upload [HttpPost] public async Task<IActionResult> Upload(IFormFile file) { if (file == null || file.Length == 0) { ModelState.AddModelError("", "Please select a file to upload."); return View(); } // 验证文件类型 var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif" }; var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant(); if (!allowedExtensions.Contains(fileExtension)) { ModelState.AddModelError("", "Only image files are allowed."); return View(); } // 验证文件大小(限制为5MB) if (file.Length > 5 * 1024 * 1024) { ModelState.AddModelError("", "File size cannot exceed 5MB."); return View(); } // 创建上传目录 string uploadsFolder = Path.Combine(_hostEnvironment.WebRootPath, "uploads"); if (!Directory.Exists(uploadsFolder)) { Directory.CreateDirectory(uploadsFolder); } // 创建唯一的文件名 string uniqueFileName = Guid.NewGuid().ToString() + fileExtension; string filePath = Path.Combine(uploadsFolder, uniqueFileName); // 保存文件 using (var fileStream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(fileStream); } // 可以在这里保存文件信息到数据库 // _fileRepository.SaveFile(new FileModel { FileName = uniqueFileName, OriginalFileName = file.FileName, FilePath = filePath }); ViewBag.Message = "File uploaded successfully!"; return View(); } } 

8.5 发送电子邮件

8.5.1 配置电子邮件服务

首先安装电子邮件NuGet包:

dotnet add package MailKit 

创建电子邮件服务接口和实现:

public interface IEmailService { Task SendEmailAsync(string to, string subject, string body, bool isHtml = false); } public class EmailService : IEmailService { private readonly IConfiguration _configuration; public EmailService(IConfiguration configuration) { _configuration = configuration; } public async Task SendEmailAsync(string to, string subject, string body, bool isHtml = false) { var emailSettings = _configuration.GetSection("EmailSettings"); var host = emailSettings["Host"]; var port = int.Parse(emailSettings["Port"]); var username = emailSettings["Username"]; var password = emailSettings["Password"]; var from = emailSettings["From"]; using (var client = new SmtpClient()) { client.Connect(host, port, SecureSocketOptions.StartTls); client.Authenticate(username, password); var message = new MimeMessage(); message.From.Add(new MailboxAddress(from)); message.To.Add(new MailboxAddress(to)); message.Subject = subject; var bodyBuilder = new BodyBuilder(); if (isHtml) { bodyBuilder.HtmlBody = body; } else { bodyBuilder.TextBody = body; } message.Body = bodyBuilder.ToMessageBody(); await client.SendAsync(message); await client.DisconnectAsync(true); } } } 

在appsettings.json中添加电子邮件配置:

{ "EmailSettings": { "Host": "smtp.example.com", "Port": "587", "Username": "your-email@example.com", "Password": "your-password", "From": "your-email@example.com" } } 

在Program.cs中注册电子邮件服务:

builder.Services.AddTransient<IEmailService, EmailService>(); 

8.5.2 在控制器中使用电子邮件服务

public class ContactController : Controller { private readonly IEmailService _emailService; public ContactController(IEmailService emailService) { _emailService = emailService; } // GET: Contact public IActionResult Index() { return View(); } // POST: Contact [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Index(ContactFormModel model) { if (ModelState.IsValid) { string subject = $"Contact Form: {model.Subject}"; string body = $"You have received a new message from {model.Name} ({model.Email}):nn{model.Message}"; await _emailService.SendEmailAsync("admin@example.com", subject, body); ViewBag.Message = "Your message has been sent successfully!"; return View(); } return View(model); } } 

9. 企业级开发技巧

9.1 日志记录

ASP.NET Core内置了强大的日志记录功能,支持多种日志提供程序。

9.1.1 配置日志记录

在Program.cs中配置日志记录:

builder.Logging.AddConsole(); builder.Logging.AddDebug(); builder.Logging.AddEventSourceLogger(); 

9.1.2 在代码中使用日志记录

public class ProductController : Controller { private readonly IProductRepository _productRepository; private readonly ILogger<ProductController> _logger; public ProductController(IProductRepository productRepository, ILogger<ProductController> logger) { _productRepository = productRepository; _logger = logger; } // GET: Product/Details/5 public IActionResult Details(int id) { try { _logger.LogInformation("Getting product details for ID: {ProductId}", id); var product = _productRepository.GetProductById(id); if (product == null) { _logger.LogWarning("Product not found for ID: {ProductId}", id); return NotFound(); } return View(product); } catch (Exception ex) { _logger.LogError(ex, "Error getting product details for ID: {ProductId}", id); return StatusCode(500, "An error occurred while processing your request."); } } } 

9.2 异常处理

9.2.1 全局异常处理

创建一个全局异常处理中间件:

public class ErrorHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ErrorHandlingMiddleware> _logger; public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError(ex, "An unhandled exception occurred during the request."); await HandleExceptionAsync(context, ex); } } private static Task HandleExceptionAsync(HttpContext context, Exception exception) { var result = JsonConvert.SerializeObject(new { error = "An error occurred while processing your request." }); context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; return context.Response.WriteAsync(result); } } 

在Program.cs中使用中间件:

app.UseMiddleware<ErrorHandlingMiddleware>(); 

9.2.2 开发环境异常页面

在Program.cs中添加开发环境异常页面:

if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } 

9.3 单元测试

9.3.1 创建单元测试项目

dotnet new xunit -n MyWebApp.Tests cd MyWebApp.Tests dotnet add reference ../MyWebApp/MyWebApp.csproj dotnet add package Moq dotnet add package Microsoft.AspNetCore.Mvc.Testing 

9.3.2 编写单元测试

public class ProductControllerTests { private readonly Mock<IProductRepository> _mockProductRepository; private readonly ProductController _controller; public ProductControllerTests() { _mockProductRepository = new Mock<IProductRepository>(); _controller = new ProductController(_mockProductRepository.Object); } [Fact] public void Index_ReturnsViewResult_WithAListOfProducts() { // Arrange var mockProducts = new List<Product> { new Product { Id = 1, Name = "Product 1", Price = 10.99m }, new Product { Id = 2, Name = "Product 2", Price = 20.99m } }; _mockProductRepository.Setup(repo => repo.GetAllProducts()).Returns(mockProducts); // Act var result = _controller.Index(); // Assert var viewResult = Assert.IsType<ViewResult>(result); var model = Assert.IsAssignableFrom<IEnumerable<Product>>(viewResult.Model); Assert.Equal(2, model.Count()); } [Fact] public void Details_ReturnsNotFoundResult_WhenIdIsNull() { // Act var result = _controller.Details(null); // Assert Assert.IsType<NotFoundResult>(result); } [Fact] public void Details_ReturnsNotFoundResult_WhenProductNotFound() { // Arrange int testId = 1; _mockProductRepository.Setup(repo => repo.GetProductById(testId)).Returns((Product)null); // Act var result = _controller.Details(testId); // Assert Assert.IsType<NotFoundResult>(result); } [Fact] public void Details_ReturnsViewResult_WithProduct() { // Arrange int testId = 1; var mockProduct = new Product { Id = testId, Name = "Test Product", Price = 10.99m }; _mockProductRepository.Setup(repo => repo.GetProductById(testId)).Returns(mockProduct); // Act var result = _controller.Details(testId); // Assert var viewResult = Assert.IsType<ViewResult>(result); var model = Assert.IsType<Product>(viewResult.Model); Assert.Equal(testId, model.Id); Assert.Equal("Test Product", model.Name); } } 

9.4 集成测试

9.4.1 创建集成测试

public class BasicIntegrationTests : IClassFixture<WebApplicationFactory<Program>> { private readonly WebApplicationFactory<Program> _factory; public BasicIntegrationTests(WebApplicationFactory<Program> factory) { _factory = factory; } [Theory] [InlineData("/")] [InlineData("/Home/Index")] [InlineData("/Home/Privacy")] public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url) { // Arrange var client = _factory.CreateClient(); // Act var response = await client.GetAsync(url); // Assert response.EnsureSuccessStatusCode(); // Status Code 200-299 Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString()); } } 

9.5 性能优化

9.5.1 响应压缩

在Program.cs中添加响应压缩服务:

builder.Services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<GzipCompressionProvider>(); }); builder.Services.Configure<BrotliCompressionProviderOptions>(options => { options.Level = CompressionLevel.Fastest; }); builder.Services.Configure<GzipCompressionProviderOptions>(options => { options.Level = CompressionLevel.Fastest; }); 

添加响应压缩中间件:

app.UseResponseCompression(); 

9.5.2 静态文件优化

在Program.cs中配置静态文件:

app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => { ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600"); } }); 

9.5.3 捆绑和缩小

创建一个捆绑和缩小配置类:

public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate*")); bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( "~/Scripts/modernizr-*")); bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( "~/Scripts/bootstrap.js")); bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/bootstrap.css", "~/Content/site.css")); } } 

在Program.cs中注册捆绑:

builder.Services.AddWebOptimizer(pipeline => { pipeline.MinifyCssFiles("css/*.css"); pipeline.MinifyJsFiles("js/*.js"); pipeline.AddCssBundle("/css/bundle.css", "css/*.css"); pipeline.AddJavaScriptBundle("/js/bundle.js", "js/*.js"); }); 

添加Web优化器中间件:

app.UseWebOptimizer(); 

10. 部署与发布

10.1 发布应用程序

使用以下命令发布应用程序:

dotnet publish -c Release -o ./publish 

10.2 部署到IIS

  1. 在Windows服务器上安装IIS和.NET Core Hosting Bundle。
  2. 在IIS中创建新的网站。
  3. 将发布的应用程序文件复制到网站目录。
  4. 配置应用程序池,确保.NET CLR版本设置为”无托管代码”。

10.3 部署到Linux

  1. 在Linux服务器上安装.NET运行时:

    sudo apt-get update sudo apt-get install -y dotnet-runtime-6.0 
  2. 将发布的应用程序文件复制到服务器。

  3. 创建一个systemd服务文件:

 sudo nano /etc/systemd/system/mywebapp.service 

添加以下内容:

 [Unit] Description=My Web App [Service] WorkingDirectory=/var/www/mywebapp ExecStart=/usr/bin/dotnet /var/www/mywebapp/MyWebApp.dll Restart=always RestartSec=10 SyslogIdentifier=mywebapp User=www-data Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy=multi-user.target 
  1. 启用并启动服务:
 sudo systemctl enable mywebapp.service sudo systemctl start mywebapp.service 
  1. 配置反向代理(如Nginx):
 sudo nano /etc/nginx/sites-available/mywebapp 

添加以下内容:

 server { listen 80; server_name example.com; location / { proxy_pass http://localhost:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 
  1. 启用站点并重启Nginx:
 sudo ln -s /etc/nginx/sites-available/mywebapp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx 

10.4 部署到Docker

  1. 创建Dockerfile:
 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyWebApp.csproj", "."] RUN dotnet restore "MyWebApp.csproj" COPY . . WORKDIR "/src" RUN dotnet build "MyWebApp.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyWebApp.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyWebApp.dll"] 
  1. 构建Docker镜像:
 docker build -t mywebapp . 
  1. 运行Docker容器:
 docker run -d -p 8080:80 --name mywebapp-container mywebapp 

11. 总结与展望

通过本教程,我们详细介绍了如何使用ASP.NET Core构建专业的Web应用程序。我们从基础配置开始,逐步深入到高级功能和企业级开发技巧,涵盖了以下主要方面:

  • ASP.NET Core框架概述和环境搭建
  • MVC模式的深入理解和应用
  • 使用Entity Framework Core进行数据访问
  • 身份认证和授权的实现
  • API开发、缓存、会话管理等高级功能
  • 文件上传、电子邮件发送等实用功能
  • 日志记录、异常处理、单元测试等企业级开发技巧
  • 应用程序的性能优化
  • 应用程序的部署和发布

ASP.NET Core是一个功能强大、灵活且高性能的框架,适用于构建各种规模的Web应用程序。随着.NET平台的不断发展,ASP.NET Core也在不断演进,为开发者提供更多的功能和更好的开发体验。

未来,你可以进一步探索以下领域:

  • 微服务架构和容器化部署
  • 实时应用程序开发(如SignalR)
  • 高级安全技术和最佳实践
  • 云原生应用程序开发和部署
  • 前端框架(如React、Angular或Vue.js)与ASP.NET Core的集成

希望本教程能够帮助你掌握ASP.NET Core开发的核心技能,并为你构建专业、高效的Web应用程序提供有力的支持。