|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. ASP.NET概述与发展历程
ASP.NET是微软推出的用于构建Web应用程序的开发框架,自2002年首次发布以来,经历了多个版本的演进。从最初的ASP.NET Web Forms,到ASP.NET MVC,再到如今的ASP.NET Core,这个框架不断适应着Web开发的变化和需求。
1.1 ASP.NET的演进
• ASP.NET Web Forms:最初的ASP.NET版本,提供了事件驱动的编程模型,类似于Windows Forms开发。
• ASP.NET MVC:引入了模型-视图-控制器架构模式,提供了更好的控制和可测试性。
• ASP.NET Web API:专门用于构建HTTP服务的框架,便于创建RESTful服务。
• ASP.NET Core:跨平台、高性能的开源框架,整合了MVC、Web API和Web Pages的功能。
1.2 ASP.NET Core的优势
ASP.NET Core作为当前最新的版本,具有以下优势:
• 跨平台支持:可在Windows、macOS和Linux上运行
• 高性能:经过优化的运行时和请求处理管道
• 模块化设计:只包含需要的功能,减少应用体积
• 开源:社区驱动的开发模式
• 统一编程模型:同时支持MVC和Web API
2. ASP.NET项目开发的核心技能
2.1 C#编程基础
C#是ASP.NET开发的主要语言,掌握C#的基础知识是必要的:
- // 基本语法示例
- public class HelloWorld
- {
- public static void Main(string[] args)
- {
- Console.WriteLine("Hello, ASP.NET Core!");
-
- // 变量声明与使用
- string message = "Welcome to ASP.NET Core development";
- int version = 6;
-
- Console.WriteLine($"{message} - Version {version}");
- }
- }
复制代码
2.2 依赖注入
依赖注入(DI)是ASP.NET Core的核心概念,有助于实现松耦合的代码:
- // 定义服务接口
- public interface IMessageService
- {
- string GetMessage();
- }
- // 实现服务
- public class HelloWorldMessageService : IMessageService
- {
- public string GetMessage()
- {
- return "Hello from DI!";
- }
- }
- // 在Startup.cs中注册服务
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddScoped<IMessageService, HelloWorldMessageService>();
- }
- // 在控制器中使用依赖注入
- public class HomeController : Controller
- {
- private readonly IMessageService _messageService;
-
- public HomeController(IMessageService messageService)
- {
- _messageService = messageService;
- }
-
- public IActionResult Index()
- {
- ViewBag.Message = _messageService.GetMessage();
- return View();
- }
- }
复制代码
2.3 中间件(Middleware)
中间件是处理HTTP请求和响应的组件,它们构成一个管道:
- // 自定义中间件示例
- public class RequestTimeMiddleware
- {
- private readonly RequestDelegate _next;
-
- public RequestTimeMiddleware(RequestDelegate next)
- {
- _next = next;
- }
-
- public async Task Invoke(HttpContext context)
- {
- var startTime = DateTime.UtcNow;
-
- // 调用管道中的下一个中间件
- await _next(context);
-
- var endTime = DateTime.UtcNow;
- var duration = endTime - startTime;
-
- // 将请求处理时间添加到响应头
- context.Response.Headers["X-Response-Time-Ms"] = duration.TotalMilliseconds.ToString();
- }
- }
- // 在Startup.cs中配置中间件管道
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // 使用自定义中间件
- app.UseMiddleware<RequestTimeMiddleware>();
-
- // 其他内置中间件
- app.UseStaticFiles();
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller=Home}/{action=Index}/{id?}");
- });
- }
复制代码
2.4 实体框架(EF) Core
Entity Framework Core是ASP.NET Core中的ORM框架,用于数据访问:
- // 定义实体模型
- public class Product
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public decimal Price { get; set; }
- public string Description { get; set; }
- public int CategoryId { get; set; }
- public Category Category { get; set; }
- }
- public class Category
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public List<Product> Products { get; set; }
- }
- // 定义DbContext
- public class AppDbContext : DbContext
- {
- public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
- {
- }
-
- public DbSet<Product> Products { get; set; }
- public DbSet<Category> Categories { get; set; }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- // 配置实体关系
- modelBuilder.Entity<Product>()
- .HasOne(p => p.Category)
- .WithMany(c => c.Products)
- .HasForeignKey(p => p.CategoryId);
- }
- }
- // 在Startup.cs中注册DbContext
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext<AppDbContext>(options =>
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
- }
复制代码
2.5 身份认证与授权
ASP.NET Core提供了强大的身份认证和授权系统:
- // 在Startup.cs中配置身份认证
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext<AppDbContext>(options =>
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
-
- services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
- .AddEntityFrameworkStores<AppDbContext>();
-
- services.AddRazorPages();
-
- // 添加基于策略的授权
- services.AddAuthorization(options =>
- {
- options.AddPolicy("RequireAdminRole", policy =>
- policy.RequireRole("Admin"));
-
- options.AddPolicy("AtLeast18", policy =>
- policy.Requirements.Add(new MinimumAgeRequirement(18)));
- });
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // ... 其他中间件
-
- app.UseAuthentication();
- app.UseAuthorization();
-
- // ... 路由配置
- }
- // 自定义授权要求
- public class MinimumAgeRequirement : IAuthorizationRequirement
- {
- public int MinimumAge { get; }
-
- public MinimumAgeRequirement(int minimumAge)
- {
- MinimumAge = minimumAge;
- }
- }
- public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
- {
- protected override Task HandleRequirementAsync(
- AuthorizationHandlerContext context,
- MinimumAgeRequirement requirement)
- {
- var dateOfBirthClaim = context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth);
-
- if (dateOfBirthClaim == null)
- {
- return Task.CompletedTask;
- }
-
- var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
- int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
-
- if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
- {
- calculatedAge--;
- }
-
- if (calculatedAge >= requirement.MinimumAge)
- {
- context.Succeed(requirement);
- }
-
- return Task.CompletedTask;
- }
- }
- // 在控制器或操作上应用授权
- [Authorize(Policy = "RequireAdminRole")]
- public class AdminController : Controller
- {
- public IActionResult Index()
- {
- return View();
- }
-
- [Authorize(Policy = "AtLeast18")]
- public IActionResult AdultContent()
- {
- return View();
- }
- }
复制代码
3. 实战案例:构建一个电子商务网站
3.1 项目规划与架构设计
在开始开发之前,我们需要规划项目的整体架构。我们将采用分层架构,包括:
• 表示层(Presentation Layer):MVC控制器和视图
• 应用服务层(Application Service Layer):业务逻辑
• 领域层(Domain Layer):核心业务模型和逻辑
• 基础设施层(Infrastructure Layer):数据访问和外部服务集成
3.2 创建项目结构
首先,我们创建一个ASP.NET Core MVC项目:
- dotnet new mvc -n ECommerceApp
- cd ECommerceApp
复制代码
然后,我们添加必要的类库项目:
- # 创建领域层
- dotnet new classlib -n ECommerceApp.Domain
- # 创建应用服务层
- dotnet new classlib -n ECommerceApp.Application
- # 创建基础设施层
- dotnet new classlib -n ECommerceApp.Infrastructure
复制代码
3.3 实现领域模型
在ECommerceApp.Domain项目中,我们定义核心业务实体:
- // ECommerceApp.Domain/Entities/Product.cs
- namespace ECommerceApp.Domain.Entities
- {
- 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 ImageUrl { get; set; }
- public int StockQuantity { get; set; }
- public int CategoryId { get; set; }
- public Category Category { get; set; }
- public bool IsFeatured { get; set; }
- public DateTime CreatedAt { get; set; }
- public DateTime? UpdatedAt { get; set; }
- }
- }
- // ECommerceApp.Domain/Entities/Category.cs
- namespace ECommerceApp.Domain.Entities
- {
- public class Category
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Description { get; set; }
- public string ImageUrl { get; set; }
- public List<Product> Products { get; set; }
- }
- }
- // ECommerceApp.Domain/Entities/Customer.cs
- using System.Collections.Generic;
- namespace ECommerceApp.Domain.Entities
- {
- public class Customer
- {
- public int Id { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Email { get; set; }
- public string PhoneNumber { get; set; }
- public List<Address> Addresses { get; set; }
- public List<Order> Orders { get; set; }
- }
- }
- // ECommerceApp.Domain/Entities/Order.cs
- using System.Collections.Generic;
- namespace ECommerceApp.Domain.Entities
- {
- public class Order
- {
- public int Id { get; set; }
- public DateTime OrderDate { get; set; }
- public decimal TotalAmount { get; set; }
- public string Status { get; set; }
- public int CustomerId { get; set; }
- public Customer Customer { get; set; }
- public int ShippingAddressId { get; set; }
- public Address ShippingAddress { get; set; }
- public List<OrderItem> OrderItems { get; set; }
- }
- }
- // ECommerceApp.Domain/Entities/OrderItem.cs
- namespace ECommerceApp.Domain.Entities
- {
- public class OrderItem
- {
- public int Id { get; set; }
- public int ProductId { get; set; }
- public Product Product { get; set; }
- public int Quantity { get; set; }
- public decimal UnitPrice { get; set; }
- public int OrderId { get; set; }
- public Order Order { get; set; }
- }
- }
- // ECommerceApp.Domain/Entities/Address.cs
- namespace ECommerceApp.Domain.Entities
- {
- public class Address
- {
- public int Id { get; set; }
- public string Street { get; set; }
- public string City { get; set; }
- public string State { get; set; }
- public string PostalCode { get; set; }
- public string Country { get; set; }
- public int CustomerId { get; set; }
- public Customer Customer { get; set; }
- }
- }
复制代码
3.4 实现数据访问层
在ECommerceApp.Infrastructure项目中,我们实现数据访问:
3.5 实现应用服务层
在ECommerceApp.Application项目中,我们实现业务逻辑:
- // ECommerceApp.Application/Services/ProductService.cs
- using ECommerceApp.Domain.Entities;
- using ECommerceApp.Infrastructure.Repositories;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- namespace ECommerceApp.Application.Services
- {
- public class ProductService : IProductService
- {
- private readonly IProductRepository _productRepository;
-
- public ProductService(IProductRepository productRepository)
- {
- _productRepository = productRepository;
- }
-
- public async Task<Product> GetProductByIdAsync(int id)
- {
- return await _productRepository.GetProductWithDetailsAsync(id);
- }
-
- public async Task<IEnumerable<Product>> GetAllProductsAsync()
- {
- return await _productRepository.GetAllAsync();
- }
-
- public async Task<IEnumerable<Product>> GetFeaturedProductsAsync()
- {
- return await _productRepository.GetFeaturedProductsAsync();
- }
-
- public async Task<IEnumerable<Product>> GetProductsByCategoryAsync(int categoryId)
- {
- return await _productRepository.GetProductsByCategoryAsync(categoryId);
- }
-
- public async Task AddProductAsync(Product product)
- {
- product.CreatedAt = System.DateTime.Now;
- await _productRepository.AddAsync(product);
- await _productRepository.SaveChangesAsync();
- }
-
- public async Task UpdateProductAsync(Product product)
- {
- product.UpdatedAt = System.DateTime.Now;
- _productRepository.Update(product);
- await _productRepository.SaveChangesAsync();
- }
-
- public async Task DeleteProductAsync(int id)
- {
- var product = await _productRepository.GetByIdAsync(id);
- if (product != null)
- {
- _productRepository.Delete(product);
- await _productRepository.SaveChangesAsync();
- }
- }
- }
- }
- // ECommerceApp.Application/Services/IProductService.cs
- using ECommerceApp.Domain.Entities;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- namespace ECommerceApp.Application.Services
- {
- public interface IProductService
- {
- Task<Product> GetProductByIdAsync(int id);
- Task<IEnumerable<Product>> GetAllProductsAsync();
- Task<IEnumerable<Product>> GetFeaturedProductsAsync();
- Task<IEnumerable<Product>> GetProductsByCategoryAsync(int categoryId);
- Task AddProductAsync(Product product);
- Task UpdateProductAsync(Product product);
- Task DeleteProductAsync(int id);
- }
- }
- // ECommerceApp.Application/Services/OrderService.cs
- using ECommerceApp.Domain.Entities;
- using ECommerceApp.Infrastructure.Repositories;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- namespace ECommerceApp.Application.Services
- {
- public class OrderService : IOrderService
- {
- private readonly IRepository<Order> _orderRepository;
- private readonly IRepository<OrderItem> _orderItemRepository;
- private readonly IProductService _productService;
-
- public OrderService(
- IRepository<Order> orderRepository,
- IRepository<OrderItem> orderItemRepository,
- IProductService productService)
- {
- _orderRepository = orderRepository;
- _orderItemRepository = orderItemRepository;
- _productService = productService;
- }
-
- public async Task<Order> GetOrderByIdAsync(int id)
- {
- return await _orderRepository.GetByIdAsync(id);
- }
-
- public async Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId)
- {
- return await _orderRepository.FindAsync(o => o.CustomerId == customerId);
- }
-
- public async Task<Order> CreateOrderAsync(int customerId, int shippingAddressId, Dictionary<int, int> productQuantities)
- {
- var orderItems = new List<OrderItem>();
- decimal totalAmount = 0;
-
- foreach (var item in productQuantities)
- {
- var product = await _productService.GetProductByIdAsync(item.Key);
- if (product == null || product.StockQuantity < item.Value)
- {
- throw new Exception($"Product {item.Key} is not available in sufficient quantity.");
- }
-
- var orderItem = new OrderItem
- {
- ProductId = item.Key,
- Quantity = item.Value,
- UnitPrice = product.Price
- };
-
- orderItems.Add(orderItem);
- totalAmount += orderItem.Quantity * orderItem.UnitPrice;
-
- // Update product stock
- product.StockQuantity -= item.Value;
- await _productService.UpdateProductAsync(product);
- }
-
- var order = new Order
- {
- CustomerId = customerId,
- ShippingAddressId = shippingAddressId,
- OrderDate = DateTime.Now,
- TotalAmount = totalAmount,
- Status = "Pending",
- OrderItems = orderItems
- };
-
- await _orderRepository.AddAsync(order);
- await _orderRepository.SaveChangesAsync();
-
- return order;
- }
-
- public async Task UpdateOrderStatusAsync(int orderId, string status)
- {
- var order = await _orderRepository.GetByIdAsync(orderId);
- if (order != null)
- {
- order.Status = status;
- _orderRepository.Update(order);
- await _orderRepository.SaveChangesAsync();
- }
- }
- }
- }
- // ECommerceApp.Application/Services/IOrderService.cs
- using ECommerceApp.Domain.Entities;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- namespace ECommerceApp.Application.Services
- {
- public interface IOrderService
- {
- Task<Order> GetOrderByIdAsync(int id);
- Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId);
- Task<Order> CreateOrderAsync(int customerId, int shippingAddressId, Dictionary<int, int> productQuantities);
- Task UpdateOrderStatusAsync(int orderId, string status);
- }
- }
复制代码
3.6 实现控制器和视图
在主项目中,我们实现MVC控制器和视图:
- // ECommerceApp/Controllers/HomeController.cs
- using ECommerceApp.Application.Services;
- using Microsoft.AspNetCore.Mvc;
- using System.Threading.Tasks;
- namespace ECommerceApp.Controllers
- {
- public class HomeController : Controller
- {
- private readonly IProductService _productService;
-
- public HomeController(IProductService productService)
- {
- _productService = productService;
- }
-
- public async Task<IActionResult> Index()
- {
- var featuredProducts = await _productService.GetFeaturedProductsAsync();
- return View(featuredProducts);
- }
-
- public IActionResult About()
- {
- return View();
- }
-
- public IActionResult Contact()
- {
- return View();
- }
-
- public IActionResult Privacy()
- {
- return View();
- }
-
- [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
- public IActionResult Error()
- {
- return View(new ErrorViewModel { RequestId = System.Diagnostics.Activity.Current?.Id ?? HttpContext.TraceIdentifier });
- }
- }
- }
- // ECommerceApp/Controllers/ProductsController.cs
- using ECommerceApp.Application.Services;
- using Microsoft.AspNetCore.Mvc;
- using System.Threading.Tasks;
- namespace ECommerceApp.Controllers
- {
- public class ProductsController : Controller
- {
- private readonly IProductService _productService;
-
- public ProductsController(IProductService productService)
- {
- _productService = productService;
- }
-
- // GET: Products
- public async Task<IActionResult> Index()
- {
- var products = await _productService.GetAllProductsAsync();
- return View(products);
- }
-
- // GET: Products/Details/5
- public async Task<IActionResult> Details(int id)
- {
- var product = await _productService.GetProductByIdAsync(id);
- if (product == null)
- {
- return NotFound();
- }
-
- return View(product);
- }
-
- // GET: Products/Create
- public IActionResult Create()
- {
- return View();
- }
-
- // POST: Products/Create
- [HttpPost]
- [ValidateAntiForgeryToken]
- public async Task<IActionResult> Create([Bind("Name,Description,Price,ImageUrl,StockQuantity,CategoryId,IsFeatured")] Product product)
- {
- if (ModelState.IsValid)
- {
- await _productService.AddProductAsync(product);
- return RedirectToAction(nameof(Index));
- }
- return View(product);
- }
-
- // GET: Products/Edit/5
- public async Task<IActionResult> Edit(int id)
- {
- var product = await _productService.GetProductByIdAsync(id);
- if (product == null)
- {
- return NotFound();
- }
- return View(product);
- }
-
- // POST: Products/Edit/5
- [HttpPost]
- [ValidateAntiForgeryToken]
- public async Task<IActionResult> Edit(int id, [Bind("Id,Name,Description,Price,ImageUrl,StockQuantity,CategoryId,IsFeatured,CreatedAt")] Product product)
- {
- if (id != product.Id)
- {
- return NotFound();
- }
-
- if (ModelState.IsValid)
- {
- await _productService.UpdateProductAsync(product);
- return RedirectToAction(nameof(Index));
- }
- return View(product);
- }
-
- // GET: Products/Delete/5
- public async Task<IActionResult> Delete(int id)
- {
- var product = await _productService.GetProductByIdAsync(id);
- if (product == null)
- {
- return NotFound();
- }
-
- return View(product);
- }
-
- // POST: Products/Delete/5
- [HttpPost, ActionName("Delete")]
- [ValidateAntiForgeryToken]
- public async Task<IActionResult> DeleteConfirmed(int id)
- {
- await _productService.DeleteProductAsync(id);
- return RedirectToAction(nameof(Index));
- }
- }
- }
- // ECommerceApp/Controllers/CartController.cs
- using ECommerceApp.Application.Services;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- namespace ECommerceApp.Controllers
- {
- public class CartController : Controller
- {
- private readonly IProductService _productService;
-
- public CartController(IProductService productService)
- {
- _productService = productService;
- }
-
- // 获取购物车
- private Dictionary<int, int> GetCart()
- {
- var cart = HttpContext.Session.GetObject<Dictionary<int, int>>("Cart");
- if (cart == null)
- {
- cart = new Dictionary<int, int>();
- HttpContext.Session.SetObject("Cart", cart);
- }
- return cart;
- }
-
- // 保存购物车
- private void SaveCart(Dictionary<int, int> cart)
- {
- HttpContext.Session.SetObject("Cart", cart);
- }
-
- // GET: Cart
- public async Task<IActionResult> Index()
- {
- var cart = GetCart();
- var cartItems = new List<CartItemViewModel>();
-
- foreach (var item in cart)
- {
- var product = await _productService.GetProductByIdAsync(item.Key);
- if (product != null)
- {
- cartItems.Add(new CartItemViewModel
- {
- Product = product,
- Quantity = item.Value
- });
- }
- }
-
- return View(cartItems);
- }
-
- // POST: Cart/Add/5
- [HttpPost]
- public IActionResult Add(int id, int quantity = 1)
- {
- var cart = GetCart();
-
- if (cart.ContainsKey(id))
- {
- cart[id] += quantity;
- }
- else
- {
- cart.Add(id, quantity);
- }
-
- SaveCart(cart);
-
- return RedirectToAction("Index");
- }
-
- // POST: Cart/Update/5
- [HttpPost]
- public IActionResult Update(int id, int quantity)
- {
- var cart = GetCart();
-
- if (cart.ContainsKey(id))
- {
- if (quantity > 0)
- {
- cart[id] = quantity;
- }
- else
- {
- cart.Remove(id);
- }
- }
-
- SaveCart(cart);
-
- return RedirectToAction("Index");
- }
-
- // POST: Cart/Remove/5
- [HttpPost]
- public IActionResult Remove(int id)
- {
- var cart = GetCart();
-
- if (cart.ContainsKey(id))
- {
- cart.Remove(id);
- }
-
- SaveCart(cart);
-
- return RedirectToAction("Index");
- }
-
- // POST: Cart/Clear
- [HttpPost]
- public IActionResult Clear()
- {
- HttpContext.Session.Remove("Cart");
- return RedirectToAction("Index");
- }
- }
-
- // 购物车项视图模型
- public class CartItemViewModel
- {
- public Product Product { get; set; }
- public int Quantity { get; set; }
- }
- }
复制代码
3.7 实现视图
以下是一些关键视图的示例:
- <!-- ECommerceApp/Views/Home/Index.cshtml -->
- @model IEnumerable<ECommerceApp.Domain.Entities.Product>
- @{
- ViewData["Title"] = "Home";
- }
- <div class="text-center">
- <h1 class="display-4">Welcome to ECommerceApp</h1>
- <p>Check out our featured products below.</p>
- </div>
- <div class="row">
- @foreach (var product in Model)
- {
- <div class="col-md-4 mb-4">
- <div class="card h-100">
- <img src="@product.ImageUrl" class="card-img-top" alt="@product.Name">
- <div class="card-body">
- <h5 class="card-title">@product.Name</h5>
- <p class="card-text">@product.Description</p>
- <p class="card-text"><strong>Price: @product.Price.ToString("C")</strong></p>
- <a asp-controller="Products" asp-action="Details" asp-route-id="@product.Id" class="btn btn-primary">View Details</a>
- </div>
- </div>
- </div>
- }
- </div>
复制代码- <!-- ECommerceApp/Views/Products/Details.cshtml -->
- @model ECommerceApp.Domain.Entities.Product
- @{
- ViewData["Title"] = "Product Details";
- }
- <div class="container">
- <div class="row">
- <div class="col-md-6">
- <img src="@Model.ImageUrl" class="img-fluid" alt="@Model.Name">
- </div>
- <div class="col-md-6">
- <h2>@Model.Name</h2>
- <p class="text-muted">Category: @Model.Category.Name</p>
- <h3 class="text-primary">@Model.Price.ToString("C")</h3>
- <p>@Model.Description</p>
- <p>Stock: @Model.StockQuantity units available</p>
-
- <form asp-controller="Cart" asp-action="Add" method="post">
- <input type="hidden" name="id" value="@Model.Id" />
- <div class="form-group">
- <label for="quantity">Quantity:</label>
- <input type="number" id="quantity" name="quantity" class="form-control" value="1" min="1" max="@Model.StockQuantity" />
- </div>
- <button type="submit" class="btn btn-primary">Add to Cart</button>
- </form>
- </div>
- </div>
- </div>
复制代码- <!-- ECommerceApp/Views/Cart/Index.cshtml -->
- @model IEnumerable<ECommerceApp.Controllers.CartItemViewModel>
- @{
- ViewData["Title"] = "Shopping Cart";
- }
- <h2>Shopping Cart</h2>
- @if (!Model.Any())
- {
- <div class="alert alert-info">
- Your cart is empty.
- </div>
- }
- else
- {
- <table class="table">
- <thead>
- <tr>
- <th>Product</th>
- <th>Price</th>
- <th>Quantity</th>
- <th>Total</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody>
- @foreach (var item in Model)
- {
- <tr>
- <td>
- <div class="d-flex align-items-center">
- <img src="@item.Product.ImageUrl" style="width: 50px; height: 50px; object-fit: cover;" alt="@item.Product.Name" />
- <div class="ml-2">
- <a asp-controller="Products" asp-action="Details" asp-route-id="@item.Product.Id">@item.Product.Name</a>
- </div>
- </div>
- </td>
- <td>@item.Product.Price.ToString("C")</td>
- <td>
- <form asp-action="Update" method="post" class="form-inline">
- <input type="hidden" name="id" value="@item.Product.Id" />
- <input type="number" name="quantity" value="@item.Quantity" min="1" max="@item.Product.StockQuantity" class="form-control form-control-sm" style="width: 70px;" />
- <button type="submit" class="btn btn-sm btn-outline-primary ml-1">Update</button>
- </form>
- </td>
- <td>@((item.Product.Price * item.Quantity).ToString("C"))</td>
- <td>
- <form asp-action="Remove" method="post">
- <input type="hidden" name="id" value="@item.Product.Id" />
- <button type="submit" class="btn btn-sm btn-danger">Remove</button>
- </form>
- </td>
- </tr>
- }
- </tbody>
- <tfoot>
- <tr>
- <td colspan="3" class="text-right"><strong>Total:</strong></td>
- <td><strong>@Model.Sum(i => i.Product.Price * i.Quantity).ToString("C")</strong></td>
- <td></td>
- </tr>
- </tfoot>
- </table>
-
- <div class="d-flex justify-content-between">
- <form asp-action="Clear" method="post">
- <button type="submit" class="btn btn-outline-danger">Clear Cart</button>
- </form>
-
- <a asp-controller="Checkout" asp-action="Index" class="btn btn-primary">Proceed to Checkout</a>
- </div>
- }
复制代码
3.8 配置依赖注入
在Startup.cs中配置依赖注入:
- // ECommerceApp/Startup.cs
- using ECommerceApp.Application.Services;
- using ECommerceApp.Infrastructure.Data;
- using ECommerceApp.Infrastructure.Repositories;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
- namespace ECommerceApp
- {
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
- public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext<AppDbContext>(options =>
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
- // 注册仓储
- services.AddScoped<IProductRepository, ProductRepository>();
- services.AddScoped<IRepository<Order>, Repository<Order>>();
- services.AddScoped<IRepository<OrderItem>, Repository<OrderItem>>();
- services.AddScoped<IRepository<Customer>, Repository<Customer>>();
- services.AddScoped<IRepository<Address>, Repository<Address>>();
- services.AddScoped<IRepository<Category>, Repository<Category>>();
- // 注册应用服务
- services.AddScoped<IProductService, ProductService>();
- services.AddScoped<IOrderService, OrderService>();
- services.AddControllersWithViews();
- services.AddRazorPages();
- // 添加Session支持
- services.AddDistributedMemoryCache();
- services.AddSession(options =>
- {
- options.IdleTimeout = System.TimeSpan.FromMinutes(30);
- options.Cookie.HttpOnly = true;
- options.Cookie.IsEssential = true;
- });
- }
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- else
- {
- app.UseExceptionHandler("/Home/Error");
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
- app.UseHsts();
- }
- app.UseHttpsRedirection();
- app.UseStaticFiles();
- app.UseRouting();
- app.UseSession();
- app.UseAuthentication();
- app.UseAuthorization();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller=Home}/{action=Index}/{id?}");
- endpoints.MapRazorPages();
- });
- }
- }
- }
复制代码
3.9 添加Session扩展方法
为了在Session中存储复杂对象,我们需要添加扩展方法:
- // ECommerceApp/Extensions/SessionExtensions.cs
- using Microsoft.AspNetCore.Http;
- using System.Text.Json;
- namespace ECommerceApp.Extensions
- {
- public static class SessionExtensions
- {
- public static void SetObject<T>(this ISession session, string key, T value)
- {
- session.SetString(key, JsonSerializer.Serialize(value));
- }
- public static T GetObject<T>(this ISession session, string key)
- {
- var value = session.GetString(key);
- return value == null ? default(T) : JsonSerializer.Deserialize<T>(value);
- }
- }
- }
复制代码
4. 开发过程中的经验分享
4.1 架构设计经验
1. 分层架构的重要性:清晰的分层架构有助于代码的维护和扩展。在我们的电子商务网站中,我们将应用分为表示层、应用服务层、领域层和基础设施层,每一层都有明确的职责。
2. 依赖注入的使用:依赖注入是实现松耦合设计的关键。通过构造函数注入,我们可以轻松地替换实现,便于单元测试和维护。
3. 仓储模式的应用:仓储模式抽象了数据访问逻辑,使业务逻辑层不直接依赖于数据访问技术。这样,我们可以轻松地切换数据存储方式,而不影响业务逻辑。
分层架构的重要性:清晰的分层架构有助于代码的维护和扩展。在我们的电子商务网站中,我们将应用分为表示层、应用服务层、领域层和基础设施层,每一层都有明确的职责。
依赖注入的使用:依赖注入是实现松耦合设计的关键。通过构造函数注入,我们可以轻松地替换实现,便于单元测试和维护。
仓储模式的应用:仓储模式抽象了数据访问逻辑,使业务逻辑层不直接依赖于数据访问技术。这样,我们可以轻松地切换数据存储方式,而不影响业务逻辑。
4.2 性能优化经验
1. 异步编程:在ASP.NET Core中,使用异步方法可以显著提高应用程序的吞吐量。特别是在I/O操作中,如数据库访问和外部API调用,异步编程可以释放线程来处理其他请求。
2. 缓存策略:对于不经常变化的数据,使用缓存可以减少数据库访问次数,提高响应速度。ASP.NET Core提供了多种缓存选项,包括内存缓存、分布式缓存和响应缓存。
3. EF Core性能优化:使用AsNoTracking()进行只读查询批量操作减少数据库往返选择性加载需要的字段使用编译查询提高重复查询的性能
4. 使用AsNoTracking()进行只读查询
5. 批量操作减少数据库往返
6. 选择性加载需要的字段
7. 使用编译查询提高重复查询的性能
异步编程:在ASP.NET Core中,使用异步方法可以显著提高应用程序的吞吐量。特别是在I/O操作中,如数据库访问和外部API调用,异步编程可以释放线程来处理其他请求。
缓存策略:对于不经常变化的数据,使用缓存可以减少数据库访问次数,提高响应速度。ASP.NET Core提供了多种缓存选项,包括内存缓存、分布式缓存和响应缓存。
EF Core性能优化:
• 使用AsNoTracking()进行只读查询
• 批量操作减少数据库往返
• 选择性加载需要的字段
• 使用编译查询提高重复查询的性能
- // 使用AsNoTracking()进行只读查询
- public async Task<IEnumerable<Product>> GetAllProductsAsync()
- {
- return await _context.Products
- .AsNoTracking()
- .ToListAsync();
- }
- // 批量插入示例
- public async Task BulkInsertProductsAsync(IEnumerable<Product> products)
- {
- await _context.Products.AddRangeAsync(products);
- await _context.SaveChangesAsync();
- }
- // 选择性加载字段
- public async Task<IEnumerable<Product>> GetProductNamesAsync()
- {
- return await _context.Products
- .Select(p => new Product { Id = p.Id, Name = p.Name })
- .ToListAsync();
- }
- // 编译查询示例
- private static readonly Func<AppDbContext, int, Task<Product>> GetProductByIdCompiled =
- EF.CompileAsyncQuery((AppDbContext context, int id) =>
- context.Products.FirstOrDefault(p => p.Id == id));
- public async Task<Product> GetProductByIdAsync(int id)
- {
- return await GetProductByIdCompiled(_context, id);
- }
复制代码
4.3 安全性经验
1. 输入验证:始终验证用户输入,防止SQL注入、XSS和其他安全漏洞。ASP.NET Core提供了内置的验证特性,可以轻松地实现验证。
- public class Product
- {
- public int Id { get; set; }
-
- [Required(ErrorMessage = "Product name is required.")]
- [StringLength(100, ErrorMessage = "Product name cannot exceed 100 characters.")]
- public string Name { get; set; }
-
- [StringLength(500, ErrorMessage = "Description cannot exceed 500 characters.")]
- public string Description { get; set; }
-
- [Range(0.01, 10000, ErrorMessage = "Price must be between 0.01 and 10000.")]
- public decimal Price { get; set; }
-
- [Url(ErrorMessage = "Please enter a valid URL.")]
- public string ImageUrl { get; set; }
-
- [Range(0, int.MaxValue, ErrorMessage = "Stock quantity cannot be negative.")]
- public int StockQuantity { get; set; }
- }
复制代码
1. 身份认证和授权:使用ASP.NET Core Identity进行用户管理,并实现基于角色的访问控制和基于策略的授权。
2. HTTPS使用:在生产环境中始终使用HTTPS,确保数据传输的安全性。
3. 敏感数据保护:使用ASP.NET Core的数据保护API来保护敏感数据,如密码、连接字符串等。
身份认证和授权:使用ASP.NET Core Identity进行用户管理,并实现基于角色的访问控制和基于策略的授权。
HTTPS使用:在生产环境中始终使用HTTPS,确保数据传输的安全性。
敏感数据保护:使用ASP.NET Core的数据保护API来保护敏感数据,如密码、连接字符串等。
- // 在Startup.cs中配置数据保护
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDataProtection()
- .PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"))
- .ProtectKeysWithDpapi();
- }
复制代码
4.4 测试经验
1. 单元测试:为业务逻辑编写单元测试,确保代码的正确性。使用xUnit、NUnit或MSTest等测试框架,以及Moq等模拟框架。
- // ProductService单元测试示例
- using ECommerceApp.Application.Services;
- using ECommerceApp.Domain.Entities;
- using ECommerceApp.Infrastructure.Repositories;
- using Moq;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- using Xunit;
- namespace ECommerceApp.Tests
- {
- public class ProductServiceTests
- {
- [Fact]
- public async Task GetProductByIdAsync_ShouldReturnProduct()
- {
- // Arrange
- var mockRepo = new Mock<IProductRepository>();
- var productId = 1;
- var expectedProduct = new Product { Id = productId, Name = "Test Product" };
-
- mockRepo.Setup(repo => repo.GetProductWithDetailsAsync(productId))
- .ReturnsAsync(expectedProduct);
-
- var service = new ProductService(mockRepo.Object);
-
- // Act
- var result = await service.GetProductByIdAsync(productId);
-
- // Assert
- Assert.NotNull(result);
- Assert.Equal(productId, result.Id);
- Assert.Equal("Test Product", result.Name);
- }
-
- [Fact]
- public async Task AddProductAsync_ShouldSetCreatedAt()
- {
- // Arrange
- var mockRepo = new Mock<IProductRepository>();
- var service = new ProductService(mockRepo.Object);
- var product = new Product { Name = "New Product" };
-
- // Act
- await service.AddProductAsync(product);
-
- // Assert
- Assert.NotEqual(default(DateTime), product.CreatedAt);
- mockRepo.Verify(repo => repo.AddAsync(product), Times.Once);
- mockRepo.Verify(repo => repo.SaveChangesAsync(), Times.Once);
- }
- }
- }
复制代码
1. 集成测试:使用ASP.NET Core的测试服务器进行集成测试,验证组件之间的交互。
- // 集成测试示例
- using Microsoft.AspNetCore.Mvc.Testing;
- using System.Net.Http;
- using System.Threading.Tasks;
- using Xunit;
- namespace ECommerceApp.Tests
- {
- public class HomeControllerIntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
- {
- private readonly WebApplicationFactory<Startup> _factory;
-
- public HomeControllerIntegrationTests(WebApplicationFactory<Startup> factory)
- {
- _factory = factory;
- }
-
- [Fact]
- public async Task IndexPage_ReturnsSuccessAndCorrectContentType()
- {
- // Arrange
- var client = _factory.CreateClient();
-
- // Act
- var response = await client.GetAsync("/");
-
- // Assert
- response.EnsureSuccessStatusCode(); // Status Code 200-299
- Assert.Equal("text/html; charset=utf-8",
- response.Content.Headers.ContentType.ToString());
- }
- }
- }
复制代码
1. 测试驱动开发(TDD):考虑采用TDD方法,先编写测试,然后实现功能,这样可以确保代码的可测试性和高质量。
5. 常见问题及解决方案
5.1 数据库迁移问题
问题:在开发过程中,数据库模型变更后如何更新数据库结构?
解决方案:使用Entity Framework Core的迁移功能:
- # 添加迁移
- dotnet ef migrations add AddNewPropertyToProduct
- # 更新数据库
- dotnet ef database update
复制代码
如果需要回滚到之前的迁移:
- # 回滚到特定迁移
- dotnet ef database update PreviousMigrationName
- # 删除最后一个迁移(如果尚未应用到数据库)
- dotnet ef migrations remove
复制代码
5.2 依赖注入生命周期问题
问题:如何选择正确的服务生命周期(Scoped、Singleton、Transient)?
解决方案:理解不同生命周期的含义和适用场景:
• Transient:每次请求都会创建一个新的实例。适用于轻量级、无状态服务。
• Scoped:每个作用域(如每个HTTP请求)创建一个实例。适用于EF DbContext和每个请求需要保持状态的服务。
• Singleton:整个应用程序生命周期中只创建一个实例。适用于全局配置、日志服务等。
示例:
- public void ConfigureServices(IServiceCollection services)
- {
- // DbContext应该是Scoped的
- services.AddDbContext<AppDbContext>(options =>
- options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
-
- // 仓储通常也是Scoped的,与DbContext保持一致
- services.AddScoped<IProductRepository, ProductRepository>();
-
- // 应用服务通常也是Scoped的
- services.AddScoped<IProductService, ProductService>();
-
- // 日志服务通常是Singleton的
- services.AddSingleton<ILoggerService, LoggerService>();
-
- // 某些工具类可能是Transient的
- services.AddTransient<IEmailService, EmailService>();
- }
复制代码
5.3 跨域资源共享(CORS)问题
问题:当前端应用与后端API不在同一个域时,如何处理跨域请求?
解决方案:配置CORS策略:
- // 在Startup.cs中配置CORS
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddCors(options =>
- {
- options.AddPolicy("AllowSpecificOrigin",
- builder =>
- {
- builder.WithOrigins("https://example.com", "https://www.example.com")
- .AllowAnyHeader()
- .AllowAnyMethod();
- });
- });
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // ... 其他中间件
-
- app.UseCors("AllowSpecificOrigin");
-
- // ... 其他中间件
- }
复制代码
5.4 性能问题:N+1查询
问题:在使用EF Core时,如何避免N+1查询问题?
解决方案:使用Include和ThenInclude进行预加载:
- // 错误示例:会导致N+1查询问题
- var orders = _context.Orders.ToList();
- foreach (var order in orders)
- {
- // 每个order都会触发一次数据库查询
- var customer = _context.Customers.Find(order.CustomerId);
- // ...
- }
- // 正确示例:使用Include预加载相关实体
- var orders = _context.Orders
- .Include(o => o.Customer)
- .Include(o => o.OrderItems)
- .ThenInclude(oi => oi.Product)
- .ToList();
复制代码
5.5 内存泄漏问题
问题:在ASP.NET Core应用中,如何避免内存泄漏?
解决方案:
1. 正确处理IDisposable资源:确保实现了IDisposable接口的服务被正确释放。
- public class MyService : IDisposable
- {
- private readonly IDisposable _resource;
-
- public MyService()
- {
- _resource = new SomeDisposableResource();
- }
-
- public void Dispose()
- {
- _resource?.Dispose();
- }
- }
复制代码
1. 避免静态引用:静态变量会一直存在于应用程序的整个生命周期,可能导致内存泄漏。
2. 取消长时间运行的操作:使用CancellationToken来取消长时间运行的操作。
避免静态引用:静态变量会一直存在于应用程序的整个生命周期,可能导致内存泄漏。
取消长时间运行的操作:使用CancellationToken来取消长时间运行的操作。
- public async Task<long> ProcessDataAsync(CancellationToken cancellationToken)
- {
- var result = 0L;
-
- for (int i = 0; i < int.MaxValue; i++)
- {
- // 检查是否已请求取消
- cancellationToken.ThrowIfCancellationRequested();
-
- // 执行一些计算
- result += i;
-
- // 定期检查取消请求
- if (i % 1000 == 0)
- {
- await Task.Delay(10, cancellationToken);
- }
- }
-
- return result;
- }
复制代码
1. 使用内存分析工具:使用dotMemory、ANTS Memory Profiler等工具来检测内存泄漏。
6. 最佳实践和进阶建议
6.1 代码组织和结构
1. 遵循SOLID原则:单一职责原则(SRP):每个类应该只有一个改变的理由。开放封闭原则(OCP):软件实体应该对扩展开放,对修改封闭。里氏替换原则(LSP):子类型必须能够替换其基类型。接口隔离原则(ISP):客户端不应该依赖于它不使用的接口。依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
2. 单一职责原则(SRP):每个类应该只有一个改变的理由。
3. 开放封闭原则(OCP):软件实体应该对扩展开放,对修改封闭。
4. 里氏替换原则(LSP):子类型必须能够替换其基类型。
5. 接口隔离原则(ISP):客户端不应该依赖于它不使用的接口。
6. 依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
7. 使用CQRS模式:对于复杂的应用程序,考虑使用命令查询职责分离(CQRS)模式,将读取和写入操作分开。
遵循SOLID原则:
• 单一职责原则(SRP):每个类应该只有一个改变的理由。
• 开放封闭原则(OCP):软件实体应该对扩展开放,对修改封闭。
• 里氏替换原则(LSP):子类型必须能够替换其基类型。
• 接口隔离原则(ISP):客户端不应该依赖于它不使用的接口。
• 依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
使用CQRS模式:对于复杂的应用程序,考虑使用命令查询职责分离(CQRS)模式,将读取和写入操作分开。
- // 查询服务
- public interface IProductQueryService
- {
- Task<Product> GetProductByIdAsync(int id);
- Task<IEnumerable<Product>> GetAllProductsAsync();
- Task<IEnumerable<Product>> GetProductsByCategoryAsync(int categoryId);
- }
- // 命令服务
- public interface IProductCommandService
- {
- Task<int> CreateProductAsync(CreateProductCommand command);
- Task UpdateProductAsync(UpdateProductCommand command);
- Task DeleteProductAsync(int id);
- }
- // 命令示例
- public class CreateProductCommand
- {
- public string Name { get; set; }
- public string Description { get; set; }
- public decimal Price { get; set; }
- public string ImageUrl { get; set; }
- public int StockQuantity { get; set; }
- public int CategoryId { get; set; }
- public bool IsFeatured { get; set; }
- }
复制代码
1. 使用领域驱动设计(DDD):对于复杂的业务领域,考虑使用DDD方法,将业务逻辑集中在领域模型中。
- // 领域实体示例
- public class Order : Entity
- {
- private readonly List<OrderItem> _orderItems = new List<OrderItem>();
-
- public DateTime OrderDate { get; private set; }
- public decimal TotalAmount { get; private set; }
- public string Status { get; private set; }
- public int CustomerId { get; private set; }
- public Customer Customer { get; private set; }
- public int ShippingAddressId { get; private set; }
- public Address ShippingAddress { get; private set; }
- public IReadOnlyCollection<OrderItem> OrderItems => _orderItems.AsReadOnly();
-
- // 私有构造函数,通过工厂方法创建
- private Order() { }
-
- public static Order Create(int customerId, int shippingAddressId)
- {
- return new Order
- {
- CustomerId = customerId,
- ShippingAddressId = shippingAddressId,
- OrderDate = DateTime.Now,
- Status = "Pending",
- TotalAmount = 0
- };
- }
-
- public void AddOrderItem(Product product, int quantity)
- {
- if (product == null)
- throw new ArgumentNullException(nameof(product));
-
- if (quantity <= 0)
- throw new ArgumentException("Quantity must be greater than zero", nameof(quantity));
-
- var orderItem = new OrderItem(product, quantity);
- _orderItems.Add(orderItem);
-
- UpdateTotalAmount();
- }
-
- public void UpdateTotalAmount()
- {
- TotalAmount = _orderItems.Sum(oi => oi.TotalPrice);
- }
-
- public void Confirm()
- {
- if (Status != "Pending")
- throw new InvalidOperationException("Only pending orders can be confirmed");
-
- Status = "Confirmed";
- }
-
- public void Ship()
- {
- if (Status != "Confirmed")
- throw new InvalidOperationException("Only confirmed orders can be shipped");
-
- Status = "Shipped";
- }
-
- public void Cancel()
- {
- if (Status == "Shipped" || Status == "Delivered")
- throw new InvalidOperationException("Cannot cancel shipped or delivered orders");
-
- Status = "Cancelled";
- }
- }
复制代码
6.2 性能优化最佳实践
1. 响应缓存:对于不经常变化的数据,使用响应缓存减少服务器负载。
- [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
- public IActionResult FeaturedProducts()
- {
- var products = _productService.GetFeaturedProducts();
- return View(products);
- }
复制代码
1. 压缩响应:启用响应压缩减少网络传输时间。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddResponseCompression(options =>
- {
- options.EnableForHttps = true;
- options.Providers.Add<BrotliCompressionProvider>();
- options.Providers.Add<GzipCompressionProvider>();
- });
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- app.UseResponseCompression();
- // ...
- }
复制代码
1. 使用缓存标记:使用缓存标记实现缓存失效。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddOutputCache(options =>
- {
- options.AddBasePolicy(builder => builder
- .With(c => c.HttpContext.Request.Path.StartsWithSegments("/products"))
- .Tag("products"));
- });
- }
- // 在控制器中使用
- [OutputCache(Tags = new[] { "products" })]
- public IActionResult Details(int id)
- {
- var product = _productService.GetProductById(id);
- return View(product);
- }
- // 在更新产品时使缓存失效
- public IActionResult Update(Product product)
- {
- _productService.UpdateProduct(product);
- _outputCache.EvictByTagAsync("products");
- return RedirectToAction(nameof(Index));
- }
复制代码
6.3 安全性最佳实践
1. 使用HTTPS和HSTS:确保所有通信都通过HTTPS进行,并启用HTTP严格传输安全。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddHsts(options =>
- {
- options.Preload = true;
- options.IncludeSubDomains = true;
- options.MaxAge = TimeSpan.FromDays(365);
- });
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- else
- {
- app.UseExceptionHandler("/Error");
- app.UseHsts();
- }
-
- app.UseHttpsRedirection();
- // ...
- }
复制代码
1. 实施CSRF保护:使用ASP.NET Core内置的防伪令牌功能。
- // 在Startup.cs中配置
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddAntiforgery(options =>
- {
- options.HeaderName = "X-XSRF-TOKEN";
- options.Cookie.SameSite = SameSiteMode.Strict;
- options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
- });
- }
- // 在表单中包含防伪令牌
- <form method="post">
- @Html.AntiForgeryToken()
- <!-- 表单字段 -->
- </form>
- // 在AJAX请求中包含防伪令牌
- function getCookie(name) {
- const value = `; ${document.cookie}`;
- const parts = value.split(`; ${name}=`);
- if (parts.length === 2) return parts.pop().split(';').shift();
- }
- fetch('/api/products', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'RequestVerificationToken': getCookie('XSRF-TOKEN')
- },
- body: JSON.stringify(data)
- });
复制代码
1. 安全配置:使用安全头增强应用程序安全性。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddHsts(options =>
- {
- options.Preload = true;
- options.IncludeSubDomains = true;
- options.MaxAge = TimeSpan.FromDays(365);
- });
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // ... 其他中间件
-
- app.Use(async (context, next) =>
- {
- context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
- context.Response.Headers.Add("X-Frame-Options", "DENY");
- context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
- context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
- context.Response.Headers.Add("Content-Security-Policy",
- "default-src 'self'; " +
- "script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; " +
- "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; " +
- "img-src 'self' data: https://picsum.photos; " +
- "font-src 'self' https://cdnjs.cloudflare.com; " +
- "connect-src 'self'; " +
- "frame-ancestors 'none';");
-
- await next();
- });
-
- // ... 其他中间件
- }
复制代码
6.4 DevOps和部署最佳实践
1. CI/CD管道:设置持续集成和持续部署管道,自动化构建、测试和部署过程。
- # Azure Pipelines示例
- trigger:
- - main
- pool:
- vmImage: 'windows-latest'
- variables:
- solution: '**/*.sln'
- buildPlatform: 'Any CPU'
- buildConfiguration: 'Release'
- steps:
- - task: NuGetToolInstaller@1
- - task: NuGetCommand@2
- inputs:
- restoreSolution: '$(solution)'
- - task: VSBuild@1
- inputs:
- solution: '$(solution)'
- msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
- platform: '$(buildPlatform)'
- configuration: '$(buildConfiguration)'
- - task: VSTest@2
- inputs:
- platform: '$(buildPlatform)'
- configuration: '$(buildConfiguration)'
- - task: PublishBuildArtifacts@1
- inputs:
- pathtoPublish: '$(Build.ArtifactStagingDirectory)'
- artifactName: 'drop'
复制代码
1. 容器化部署:使用Docker容器化应用程序,简化部署和环境一致性。
- # 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 ["ECommerceApp/ECommerceApp.csproj", "ECommerceApp/"]
- COPY ["ECommerceApp.Domain/ECommerceApp.Domain.csproj", "ECommerceApp.Domain/"]
- COPY ["ECommerceApp.Application/ECommerceApp.Application.csproj", "ECommerceApp.Application/"]
- COPY ["ECommerceApp.Infrastructure/ECommerceApp.Infrastructure.csproj", "ECommerceApp.Infrastructure/"]
- RUN dotnet restore "ECommerceApp/ECommerceApp.csproj"
- COPY . .
- WORKDIR "/src/ECommerceApp"
- RUN dotnet build "ECommerceApp.csproj" -c Release -o /app/build
- FROM build AS publish
- RUN dotnet publish "ECommerceApp.csproj" -c Release -o /app/publish
- FROM base AS final
- WORKDIR /app
- COPY --from=publish /app/publish .
- ENTRYPOINT ["dotnet", "ECommerceApp.dll"]
复制代码
1. 监控和日志记录:实现全面的监控和日志记录策略,以便及时发现和解决问题。
- // 在Startup.cs中配置日志记录
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddLogging(loggingBuilder =>
- {
- loggingBuilder.AddConsole();
- loggingBuilder.AddDebug();
- loggingBuilder.AddEventSourceLogger();
-
- if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
- {
- loggingBuilder.AddApplicationInsights();
- }
- });
- }
- // 在控制器中使用日志记录
- public class ProductsController : Controller
- {
- private readonly ILogger<ProductsController> _logger;
-
- public ProductsController(ILogger<ProductsController> logger)
- {
- _logger = logger;
- }
-
- public IActionResult Details(int id)
- {
- try
- {
- _logger.LogInformation("Getting product details for ID: {ProductId}", id);
-
- var product = _productService.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.");
- }
- }
- }
复制代码
7. 总结
通过本文的实战案例和经验分享,我们深入探讨了ASP.NET项目开发的核心技能,从基础概念到高级技术,从代码实现到最佳实践。我们构建了一个完整的电子商务网站,涵盖了产品管理、购物车功能、订单处理等核心功能,并分享了在开发过程中的经验教训和解决方案。
ASP.NET Core作为一个强大、灵活的框架,为Web开发提供了丰富的功能和工具。通过掌握其核心技能,如依赖注入、中间件、Entity Framework Core、身份认证和授权等,开发者可以构建高性能、安全、可维护的Web应用程序。
在实际开发中,我们需要注重代码质量、性能优化、安全性和可维护性,遵循最佳实践,不断学习和探索新的技术和方法。希望本文的内容能够帮助读者更好地理解和掌握ASP.NET项目开发的核心技能,在实际项目中取得成功。
版权声明
1、转载或引用本网站内容(掌握ASP.NET项目开发核心技能实战案例全程解析与经验分享)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://upload.pixtech.org/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://upload.pixtech.org/thread-31295-1-1.html
|
|