日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

巅峰对决!Spring Boot VS .NET 6

發布時間:2023/12/4 asp.net 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 巅峰对决!Spring Boot VS .NET 6 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring Boot 和 ASP.NET Core 都是企業中流行的 Web 框架, 對于喜歡 C# 的人會使用 ASP.NET Core, 而對于 Java 或 Kotlin 等基于 JVM 的語言,Spring Boot 是最受歡迎的。

這本文中,會對比這兩個框架在以下方面有何不同:

?控制器?模型綁定和驗證?異常處理?數據訪問?依賴注入?認證與授權?性能


基礎項目

這是一個有關訂單的基礎項目, 非常簡單的后端 api, 客戶可以創建一個訂單來購買一個或多個產品, 我使用了 MySQL 作為數據庫,下面是實體關系圖。

這里使用的框架版本分別是, Spring Boot (v2.5.5) 和 .NET 6, 讓我們開始對比吧!


1.控制器

控制器是負責處理傳入請求的層, 為了在 Spring Boot 中定義一個控制器,我創建了一個類 ProductOrderController, 然后使用了?@RestController?和?@RequestMapping?注解, 然后在控制器的每個方法上, 可以使用下面的注解來定義支持的 HTTP 方法和路徑(可選)。

?@GetMapping?@PostMapping?@PutMapping?@DeleteMapping?@PatchMapping

如果要綁定到路徑變量, 我們可以將參數添加到用@PathVariable?注釋的控制器方法中,并指定與參數同名的路由路徑模板,下面的 getOrderById() 方法,我們將id綁定為路徑變量。

@RestController @RequestMapping("/v1/orders") class ProductOrderController(private val productOrderService: IProductOrderService ) {@GetMappingfun getOrders(query: ProductOrderQuery): List<ProductOrderDto> = when {query.productId?.isNotEmpty() == true -> productOrderService.getByProductId(query.productId!!)query.customerId?.isNotEmpty() == true -> productOrderService.getByCustomerId(query.customerId!!)else -> productOrderService.getAllOrders()}@GetMapping("{id}")fun getOrderById(@PathVariable id: String): ProductOrderDto = productOrderService.getById(id) }

在 .NET Core 中, 控制器和上面是相似的, 首先創建一個?ProductOrderController類, 并繼承?ControllerBase?,標記?[ApiController]?特性, 然后通過?[Route]?特性指定基本路徑, 然后在控制器的每個方法上, 可以使用下面的特性來定義支持的 HTTP 方法和路徑(可選)。

[ApiController] [Route("v1/orders")] public class ProductOrderController : ControllerBase {private readonly IProductOrderService _productOrderService;public ProductOrderController(IProductOrderService productOrderService){_productOrderService = productOrderService;}[HttpGet]public async Task<List<ProductOrderDto>> GetOrders([FromQuery] ProductOrderQuery query){List<ProductOrderDto> orders;if (!string.IsNullOrEmpty(query.ProductId)){orders = await _productOrderService.GetAllByProductId(query.ProductId);}else if (!string.IsNullOrEmpty(query.CustomerId)){orders = await _productOrderService.GetAllByCustomerId(query.CustomerId);}else{orders = await _productOrderService.GetAll();}return orders;}[HttpGet("{id}")]public async Task<ProductOrderDto> GetOrderById(string id) => await _productOrderService.GetById(id); }


2.模型綁定和驗證

在 Spring Boot 中, 我們只需要給控制器的方法的參數加上下面的注解

?@RequestParam → 從查詢字符串綁定?@RequestBody → 從請求體綁定?@RequestHeader → 從請求頭綁定

對比表單的請求,不需要給參數加注解就可以綁定。

@RestController @RequestMapping("/v1/customer") class CustomerController(private val customerService: CustomerService ) {@PostMapping("/register")fun register(@Valid @RequestBody form: RegisterForm) = customerService.register(form)@PostMapping("/login")fun login(@Valid @RequestBody form: LoginForm) = customerService.login(form) }@RestController @RequestMapping("/v1/orders") class ProductOrderController(private val productOrderService: IProductOrderService ) {@GetMappingfun getOrders(query: ProductOrderQuery): List<ProductOrderDto> {...} }

如果要對參數進行驗證, 需要添加?spring-boot-starter-validation?依賴項, 然后給 DTO 的屬性加上?@NotEmpty、@Length?等注解, 最后給DTO加上?@Valid?即可。

.NET Core 和上面類似, 同樣你可以使用下面的特性標記控制器的方法

?[FromQuery] → 從查詢字符串綁定?[FromRoute] → 從路由數據綁定?[FromForm] → 從表單數據綁定?[FromBody] → 從請求體綁定?[FromHeader] → 從請求頭綁定

[Route("v1/customer")][ApiController]public class CustomerController : ControllerBase{[HttpPost("register")]public async Task<AuthResultDto> Register([FromBody] RegisterForm form) => await _customerService.Register(form);[HttpPost("login")]public async Task<AuthResultDto> Login([FromBody] LoginForm form) => await _customerService.Login(form);}[Route("v1/orders")][ApiController]public class ProductOrderController : ControllerBase{[HttpGet]public async Task<List<ProductOrderDto>> GetOrders([FromQuery] ProductOrderQuery query){.....}}

模型驗證也是類似的, 給 DTO 的屬性上加上 [Required]、[MinLength]、[MaxLength] 等特性就可以了。

public class RegisterForm {[Required(ErrorMessage = "Please enter user id")]public string UserId { get; set; }[Required(ErrorMessage = "Please enter name")]public string Name { get; set; }[Required(ErrorMessage = "Please enter password")][MinLength(6, ErrorMessage = "Password must have minimum of 6 characters")]public string Password { get; set; } }


3.異常處理

Spring Boot 的異常處理,主要用?@RestControllerAdvice?和?ExceptionHandler

注解,如下

abstract class AppException(message: String) : RuntimeException(message) {abstract fun getResponse(): ResponseEntity<BaseResponseDto> }@RestControllerAdvice class ControllerExceptionHandler : ResponseEntityExceptionHandler() {@ExceptionHandler(AppException::class)fun handleAppException(ex: AppException, handlerMethod: HandlerMethod): ResponseEntity<BaseResponseDto> {return ex.getResponse()} }

在 ASP.NET Core 中,異常處理程序被注冊為過濾器/中間件,我們可以創建一個異常處理類,并繼承?IExceptionFilter?接口。

public class ControllerExceptionFilter : IExceptionFilter {public void OnException(ExceptionContext context){if (context.Exception is AppException exception){context.Result = exception.GetResponse();}} }

然后注冊這個異常過濾器

var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers(options => {options.Filters.Add<ControllerExceptionFilter>(); });


4.數據訪問

在 Spring Boot 中, 你可以使用?Hibernate?ORM, 創建一個Repository 接口, 并繼承?JpaRepository?, 這樣就有了開箱即用的基本查詢方法,比如 findAll() 和 findById()。

您還可以在定義自定義查詢方法。只要遵循嚴格的方法命名約定,Spring 就會構建這個存儲庫的實現,包括運行時的所有查詢,魔法?是的!

interface IProductOrderRepository : JpaRepository<ProductOrder, String> {@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, value = "product-order-graph")override fun findById(id: String): Optional<ProductOrder>@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, value = "product-order-graph")fun findAllByCustomer(customer: Customer): List<ProductOrder>@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, value = "product-order-graph")@Query("SELECT ord FROM ProductOrder ord JOIN OrderItem item ON item.productOrder = ord WHERE item.productId = :productId")fun findAllByProductId(productId: String): List<ProductOrder> }

而在 .NET Core 中,我們可以使用官方的?Entity Framework?ORM, 首先,我們需要創建一個?DB Context?類, 這是 ORM 框架用來連接數據庫和運行查詢的橋梁。

public class AppDbContext : DbContext {public DbSet<Customer> Customer { get; set; }public DbSet<Product> Product { get; set; }public DbSet<ProductOrder> ProductOrder { get; set; }public DbSet<OrderItem> OrderItem { get; set; }public AppDbContext(DbContextOptions<AppDbContext> options) : base(options){Customer = Set<Customer>();Product = Set<Product>();ProductOrder = Set<ProductOrder>();OrderItem = Set<OrderItem>();} }

接下來,還需要注冊上面的 DB Context,并配置數據庫連接字符串

var builder = WebApplication.CreateBuilder(args);// Add services to the container. builder.Services.AddDbContext<AppDbContext>(options => {// Using Pomelo.EntityFrameworkCore.MySql libraryoptions.UseMySql(builder.Configuration.GetConnectionString("EaterMysql"), ServerVersion.Parse("8.0.21-mysql")); });

在我們的 Repository 中,我們訪問 DB 上下文中的 DbSet 字段來執行查詢, 在這里,我們使用 LINQ,這是一組直接融入 C# 語言的 API,用于從各種數據源進行查詢。這是我非常喜歡的一項功能,因為它提供了 Fluent API,例如 Where()、Include() 或 OrderBy(),這非常方便!

public class ProductOrderRepository : BaseRepository<ProductOrder>, IProductOrderRepository {public ProductOrderRepository(AppDbContext context) : base(context){}public Task<ProductOrder?> GetById(string id) => _context.ProductOrder.Include(o => o.Customer).Include(o => o.Items).Where(o => o.Id == id).FirstOrDefaultAsync();public Task<List<ProductOrder>> GetAllByCustomer(Customer customer) => _context.ProductOrder.Include(o => o.Items).Where(o => o.Customer == customer).ToListAsync();public Task<List<ProductOrder>> GetAllByProductId(string productId) => _context.ProductOrder.Include(o => o.Customer).Include(o => o.Items).Where(o => o.Items.Any(item => item.ProductId == productId)).ToListAsync(); }


5.依賴注入

Spring Boot 中的依賴注入真的非常簡單, 只需根據類的角色使用?@Component、**@Service?@Repository** 等注解即可,在啟動時,它會進行掃描,然后注冊。

@Service class ProductOrderService(private val customerRepository: ICustomerRepository,private val productOrderRepository: IProductOrderRepository,private val mapper: IMapper ) : IProductOrderService {// ...// ...// ... }

在 .NET Core 中, 服務根據生命周期分成3中類型,單例的,范圍的, 瞬時的,并且在啟動時手動注冊到 DI 容器中

var builder = WebApplication.CreateBuilder(args);// Add services to the container.// Services builder.Services.AddSingleton<IPasswordEncoder, PasswordEncoder>(); builder.Services.AddSingleton<ITokenService, TokenService>(); builder.Services.AddScoped<IProductOrderService, ProductOrderService>(); builder.Services.AddScoped<ICustomerService, CustomerService>();// Repositories builder.Services.AddScoped<IProductOrderRepository, ProductOrderRepository>(); builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();


6.身份驗證和授權

在 Spring Boot 中, 首先需要添加依賴?spring-boot-starter-security, 然后,在 build.gradle 文件(或 pom.xml,如果您使用 Maven)中為 JWT 庫添加以下依賴項:

implementation("io.jsonwebtoken:jjwt-api:${jjwtVersion}") implementation("io.jsonwebtoken:jjwt-impl:${jjwtVersion}") implementation("io.jsonwebtoken:jjwt-jackson:${jjwtVersion}")

接下來, 需要創建一個負責 JWT 令牌解析和驗證的過濾器/中間件, 然后重寫?doFilterInternal?方法, 編寫解析和驗證邏輯。

class JwtAuthenticationFilter(private val tokenService: ITokenService ) : OncePerRequestFilter() {override fun doFilterInternal(request: HttpServletRequest,response: HttpServletResponse,filterChain: FilterChain) {val authorization = request.getHeader("Authorization")if (authorization == null || !authorization.startsWith("Bearer")) {return filterChain.doFilter(request, response)}val token = authorization.replaceFirst("Bearer ", "")val claims = try {tokenService.parse(token).body} catch (ex: JwtException) {SecurityContextHolder.clearContext()return}// Set authentication to tell Spring that the user is valid and authenticated.SecurityContextHolder.getContext().authentication = UsernamePasswordAuthenticationToken(claims.id, null, arrayListOf())filterChain.doFilter(request, response)} }

要配置和強制執行身份驗證,需要先創建一個繼承WebSecurityConfigurerAdapter的配置類,并使用?@Configuration?注解, 在這里注冊我們上面創建的 JWT 過濾器,并在configure方法中配置哪些端點應該進行身份驗證。比如,我允許匿名訪問客戶登錄和注冊端點。其他所有內容都應進行身份驗證

class ApiAccessDeniedHandler : AccessDeniedHandler {override fun handle(request: HttpServletRequest,response: HttpServletResponse,accessDeniedException: AccessDeniedException) {response.status = HttpStatus.FORBIDDEN.value()} }class AuthEntryPoint : AuthenticationEntryPoint {override fun commence(request: HttpServletRequest,response: HttpServletResponse,authException: AuthenticationException) {response.status = HttpStatus.UNAUTHORIZED.value()} }@Configuration class SecurityConfig(tokenService: ITokenService ) : WebSecurityConfigurerAdapter() {private val jwtAuthenticationFilter = JwtAuthenticationFilter(tokenService)@Beanfun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()override fun configure(http: HttpSecurity) {http.csrf().disable().cors().disable().addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java).exceptionHandling().accessDeniedHandler(ApiAccessDeniedHandler()).authenticationEntryPoint(AuthEntryPoint()).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/v1/customer/register", "/v1/customer/login").permitAll().anyRequest().authenticated()} }

在 ASP.NET Core 中實現 JWT 身份驗證和授權非常簡單, 首先安裝Microsoft.AspNetCore.Authentication.JwtBearer` NuGet 包, 然后,在?Program.cs?文件中配置一些設置,例如密鑰、頒發者和到期時間。

var builder = WebApplication.CreateBuilder(args);// Configure JWT Authentication builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{options.SaveToken = true;options.RequireHttpsMetadata = true;options.TokenValidationParameters = new TokenValidationParameters(){ValidateAudience = false,ValidIssuer = builder.Configuration["JWT:ValidIssuer"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"])),ClockSkew = TimeSpan.FromSeconds(30)};});var app = builder.Build();// Enable Authentication & Authorization app.UseAuthentication(); app.UseAuthorization();app.MapControllers();app.Run();

如果需要認證,就在控制或者方法上,加上?[Authorize]?特性, 同樣,可以加上?[AllowAnonymous]?代表允許匿名訪問。

[Route("v1/customer")] [ApiController] [Authorize] public class CustomerController : ControllerBase {[HttpPost("login")][AllowAnonymous]public async Task<AuthResultDto> Login([FromBody] LoginForm form) => await _customerService.Login(form);[HttpGet]public async Task<CustomerDto> GetProfile() => await _customerService.GetProfile(); }


7.性能

最后是關鍵的部分,性能, 這兩個框架在 QPS 和 內存使用率方面的表現如何?

在這里,我做了一個負載測試,調用一個 API,通過 id 獲取一個產品訂單。

? 測試環境

CPU:Intel Core i7–8750H( 4.10 GHz),6 核 12 線程 RAM:32 GB 操作系統:Windows 11

? 測試設置

我使用的壓力測試工具是?K6, 進行了2次測試, 因為我想看看程序預熱后性能提高了多少。在每次測試中,前 30 秒將從 0 增加到 1000 個虛擬用戶,然后在那里停留 1 分鐘。然后再過 30 秒,測試將從 1000 用戶減少到 0 用戶。

我還將 Golang(使用 Gin 框架和 Gorm)添加到基準測試, 這里只是為了對比 我們都知道 Golang 非???。

? 測試結果

顯然,Golang?是最快的,我檢查了兩者都執行了查詢優化,確認沒有?N+1?問題,所以在?QPS?上?.NET?Core?勝出。

在內存使用方面,Golang 當然是最小的(只有 113 MB!),其次是 .NET Core, 最后就是超過1 GB 內存的 Spring Boot, 另外我觀察到的有趣的事情是,測試完成后,Golang 和 .NET Core 的內存消耗分別減少到 10 MB 和 100 MB 左右,而 Spring Boot 保持在 1 GB 以上,直到我終止進程。

最后,Spring Boot 和 ASP.NET Core 都是非常成熟的框架,您都可以考慮使用, 希望對您有用!

總結

以上是生活随笔為你收集整理的巅峰对决!Spring Boot VS .NET 6的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 91免费高清在线观看 | 亚欧美精品 | 国产成人自拍视频在线观看 | 国产精品久久久久久久久免费 | 视频在线观看99 | 小sao货水好多真紧h无码视频 | 人妻无码一区二区三区久久99 | 色综合激情网 | 麻豆影视在线观看 | 蜜臀av免费一区二区三区水牛 | 全部免费毛片在线播放 | 波多野结衣高清在线 | 精品国产免费观看 | 色老板最新地址 | 欧美日韩国产精品一区二区 | 在线看国产精品 | 91九色在线播放 | 五月天激情婷婷 | 欧美精品在线免费观看 | 国产精品久久999 | 男插女视频在线观看 | 国产网红av| 欧美日韩亚洲国产综合 | 97色综合| 黄色伊人网 | 丁香六月婷婷 | 毛片视频播放 | 风间由美在线观看 | 欧美成人三级视频 | 在线免费观看一区二区三区 | 秋霞影院午夜老牛影院 | 在线电影一区二区三区 | 国产精品98 | 国产另类视频 | 在线看黄免费 | 丰满少妇毛片 | 日韩一卡二卡三卡四卡 | 奇米影视四色在线 | 欧美日韩a级片 | 成人深夜视频在线观看 | 无码人妻熟妇av又粗又大 | 午夜欧美日韩 | 99久久婷婷国产综合精品 | 中文字幕日韩久久 | 日本va视频 | 67194少妇在线观看 | 久久网站av | 亚洲二区av| 天天天干 | 亚洲天堂av网站 | 奇米精品一区二区三区在线观看 | 自拍视频网站 | 美女在线播放 | 免费av不卡在线观看 | 国产伦子伦对白视频 | 精品xxxxx | а中文在线天堂 | 操天天| a天堂av| 91亚洲天堂| 国产夜色精品一区二区av | 热久久久久 | 国产一区二区福利 | 蜜桃av免费看| 精品国产乱码久久久人妻 | 色乱码一区二区三区在线男奴 | 亚洲成人福利在线 | 精品国产一区二区三区久久久久久 | 在线免费观看av不卡 | 久久综合久久综合久久综合 | 国产精品自拍片 | 免费无码av片在线观看 | 欧美成综合 | 人与拘一级a毛片 | 成人a毛片 | aaaaa黄色片 天堂网在线观看 | 美日韩精品视频 | 夜夜骑日日操 | 天天干夜夜怕 | av免费的| 黄色一级大片 | 美女光屁股视频 | 色戒在线免费 | 一区二区手机在线 | 91精品久久久久久久99蜜桃 | 中国一级特黄毛片大片 | 中文字幕日韩精品一区 | 午夜精品一区二区三区免费视频 | 精品国产乱码久久久久久闺蜜 | 青青草原伊人 | 欧美性受xxxx黑人xyx | 日本熟女一区二区 | 免费一区二区视频 | 黄色电影在线视频 | 日韩激情电影在线 | 欧美黑人又粗又大的性格特点 | 中国女人av | 亚洲综合在线成人 | 96在线视频 |