不管是官方自帶模板還是其他開源搞的,總是一來一大堆,如果你也嫌棄這些過于臃腫,不如看看我這個方式
已開源,沒啥技術含量,只是一個思路
開源地址:https://github.com/DotNetGoodMorning/SimpleWaiBao/tree/main
正文
1、前提
假設我要做一個簡單的API
2、方式
想到清爽,那肯定是簡單方便,腦袋第一個念頭就是.NET 6 推出的miniapi了
3、官方路子
使用 ASP.NET Core 創(chuàng)建最小 API 兩篇官方文檔足以,按照文檔step by step 就ok了,其他的需要就加
我的野路子
官方是官方,官方走的路子當然還是基于它最標準的搞法,我的路子則是基于國內實際情況 總體思路就是用控制臺改api
模擬前提場景
搞一個普通企業(yè)官網(wǎng)的api,那么要求就是以下幾點
根據(jù)這些要求,我需要引入最基本的就幾個:
Swashbuckle.AspNetCore (swagger相關) SqlSugarCore (sqlsugar Orm) (用啥都可以,例如還有freesql) Microsoft.AspNetCore.Authentication.JwtBearer (授權鑒權這里用簡單的jwt) 如果有其他需求,再自己加,一點也不冗余
注意:需要先右鍵控制臺項目,將 <Project Sdk="Microsoft.NET.Sdk">
改為 <Project Sdk="Microsoft.NET.Sdk.Web">
其原因可以去官網(wǎng)翻找資料感悟一下
代碼
dotNet 幾忘了,反正很早就是通用主機了,如果你同時還要搞blazor server啥的,把這一坨封裝一下即可,通用的
Program.cs
里面直接無腦寫下以下代碼
var builder = WebApplication.CreateBuilder(args);#region 基本設置 builder.Services.AddMemoryCache(); builder.Services.AddControllers(); builder.Services.Configure<FormOptions>(options => { // 設置上傳大小限制256MB options.MultipartBodyLengthLimit = 268435456 ; }); builder.Services.AddSingleton<SqlSugarMemoryCacheService>();#endregion #region 授權鑒權 // 添加身份驗證和授權中間件 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true , ValidateAudience = true , ValidateLifetime = true , ValidateIssuerSigningKey = true , ValidIssuer = "ningissuer" , ValidAudience = "wr" , IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("sdfsdfsrty45634kkhxxhtdgdfss345t678xx" )) }; }); builder.Services.AddAuthorization(options => { options.AddPolicy("AdminOnly" , policy => { policy.RequireClaim("role" , "admin" ); }); });#endregion #region swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1" , new OpenApiInfo { Title = "企業(yè)官網(wǎng)Api" , Version = "v1" }); // 添加身份驗證 c.AddSecurityDefinition("Bearer" , new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme" , Name = "Authorization" , In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }); // 添加授權要求 c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string [] {} } }); // 設置 XML 注釋文件的路徑 var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name} .xml" ; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); });#endregion var app = builder.Build(); app.UseSwagger(); app.UseStaticFiles();// 啟用身份驗證和授權中間件 app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 這里配置了使用控制器的路由 }); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json" , "企業(yè)官網(wǎng)Api" ); c.RoutePrefix = string .Empty; // 將 Swagger UI 設置為應用程序的根路徑 }); ServiceLocator.Instance = app.Services; ServiceLocator.ApplicationBuilder = app;var db = SqlSugarHelper.Db;//數(shù)據(jù)庫初始化 app.MapGet("/seed" , async () => { db.CodeFirst.InitTables<SysUserEntity>(); string name = "op" ; string pwd = "op" ; var loginResult = await db.Queryable<SysUserEntity>().Where(a => !a.IsBan && a.UsePwd == pwd && a.UserName == name).AnyAsync(); if (!loginResult) { await db.Insertable<SysUserEntity>(new SysUserEntity { IsBan = false , UsePwd = pwd, UserName = name }).ExecuteCommandAsync(); } db.CodeFirst.InitTables<FileSourceEntity>(); db.CodeFirst.InitTables<ArticleEntity>(); }); app.MapGet("/health" , () => "1024" ); app.Run();
接口就"勉為其難"的新建個api文件夾然后
/// <summary> /// 系統(tǒng)用戶/// </summary> [Route("api/[controller]/[action]" ) ] [ApiController ]public class SysUserController : BaseApi { public SysUserController () { } /// <summary> /// 檢測Token信息 /// </summary> /// <returns></returns> [HttpGet ] [Authorize ] public ApiResult CheckToken () { var httpContext = HttpContext; // 從請求頭中獲取 Authorization 標頭的值 var authorizationHeader = httpContext.Request.Headers["Authorization" ].FirstOrDefault(); if (!string .IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith("Bearer " )) { // 提取令牌字符串(去除 "Bearer " 前綴) var token = authorizationHeader.Substring(7 ); var tokenHandler = new JwtSecurityTokenHandler(); var jwtToken = tokenHandler.ReadJwtToken(token); // 獲取 ClaimTypes.Name 的值 var username = jwtToken.Claims.FirstOrDefault(claim => claim.Type == "name" )?.Value; // 在這里使用 username 進行其他操作 return Success($"當前Token用戶是:{username} " ); } return Error("Toekn信息解析失敗" ); } /// <summary> /// 登錄 /// </summary> /// <param name="model"></param> /// <returns></returns> [AllowAnonymous ] [HttpPost ] public async Task<ApiResult> Login (SysUserEntity model ) { string secretKey = "sdfsdfsrty45634kkhxxhtdgdfss345t678xx" ; var loginResult = await db.Queryable<SysUserEntity>().Where(a => !a.IsBan && a.UsePwd == model.UsePwd && a.UserName == model.UserName).AnyAsync(); // 驗證用戶名和密碼 if (!loginResult) { return Error("賬號密碼錯誤" ); } // 生成 JWT 令牌 var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(secretKey); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.Name, model.UserName), new Claim(JwtRegisteredClaimNames.Jti, model.Id.ToString()), new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), new Claim(ClaimTypes.Expiration, DateTime.Now.AddHours(10 ).ToString()) }), Expires = DateTime.UtcNow.AddDays(7 ), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), Issuer = "ningissuer" , Audience = "wr" , }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); // 返回 JWT 令牌 return Success(new { token = "Bearer " + tokenString }); } }
到這里,基本上已經(jīng)結束了,剩下的無非加加業(yè)務,或者加一些更豐富的組件,什么autofac啦,nacos啦,yarp啦,seq啦
總結
對項目而言
其實這種方式已經(jīng)足夠適用絕大多數(shù)中小公司的普通項目需求了,如果你還要加些限流或者什么中間件的話,也是可以很直觀的去加,而不是像某些框架封裝一坨又一坨,你在哪加個什么東西要翻找半天,毀壞了原本dotNet自身的生態(tài)(指官方文檔)
這樣出來對的項目也很直觀,物盡其才,只要后續(xù)開發(fā)定好一個規(guī)范管理,就不會像你公司那破框架一堆密密麻麻的東西都沒使用過的情況出現(xiàn)
對新手而言
同時呢,這樣構建一個項目框架,也方便新手學習,因為十分的直觀,不會對莫名其妙出現(xiàn)的東西感覺到匪夷所思,根本不知道拿來做什么的,像這樣需要什么加什么,就對所有加的東西包括nuget包,中間件,或者封裝啥的都有個很清晰的認知
對轉行到.NET 的人而言
dotnet官方本身已經(jīng)是一個大封裝了,不要把別的語言思維帶到這里,做什么破功能都要自己寫,寫又寫不好,寫好了又沒文檔,人走了之后又坑公司又坑其他.net開發(fā)者
結語,給所有中小公司和個人的開發(fā)建議
馬上2024了,.Net的生態(tài)已經(jīng)算是十分豐富了,請不要再試圖自行造輪子
舉個例子假如你要 對接微信(企業(yè)微信,小程序,公眾號)/字節(jié)用這個: https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat
別在那自己瞎琢磨封裝,對個人而言你瞎封裝有什么用對你也沒什么好處費時費力,還封裝不好,你能保證自己封裝完了還會提供詳細的文檔?
一句很重要的話,我在一線開發(fā)從curd干到框架,我覺得很多人都沒意識到的一點就是:企業(yè)的項目,技術方面所有都要為了實際業(yè)務而做出努力,而不是為了技術而技術。
就剛才這封裝的例子,如果你是自己封裝,隨便有點變動你是不是要拋下業(yè)務需求不管去維護?
一切的代碼和封裝前提思想就是不要為了寫代碼而去寫代碼,唉,忍不吐槽一下,這其實是碼農基本素養(yǎng),但還是看的太多太心累
代碼文件補充
SqlSugarHelper
public class SqlSugarHelper { public static readonly SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig() { ConnectionString = "server=xxx;Database=xxx;Uid=root;Pwd=xxx;Port=6607;Allow User Variables=True;" ,//連接符字串 DbType = DbType.MySql, IsAutoCloseConnection = true , }, db => { ExternalServicesSetting(db); db.Aop.OnLogExecuting = (sql, pars) => { Console.WriteLine(sql); }; }); /// <summary> /// 拓展配置 /// </summary> /// <param name="db"></param> /// <param name="config"></param> private static void ExternalServicesSetting (SqlSugarClient db ) { var cache = ServiceLocator.Instance.GetService<SqlSugarMemoryCacheService>(); db.CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices { DataInfoCacheService = cache, }; } }