APIs מינימליות הן תכונה מרגשת שהוצגה ב-.NET 6, שנועדה לחולל מהפכה בדרך שבה אתה יוצר APIs.
דמיין לבנות APIs חזקים עם מינימום קוד ואפס קוד חוזר—בלי עוד קרב עם בקרי דוגמאות, ניתובים או תוכנה מתווכת. זה מה שאפשרי עם APIs מינימליות. הרעיון מאחורי APIs אלו הוא לייעל את תהליך הפיתוח, מה שמקל מאוד ומייעל את העבודה.
במאמר זה, נצלול לתוך עולם ה-APIs המינימליות ב-.NET 8 וננחה אותך כיצד ליצור API של חנות ספרים פונקציונלית לחלוטין. תלמד כיצד לקבל את כל הספרים, לשחזר ספר לפי מזההו, להוסיף ספרים חדשים ואפילו למחוק ספרים. בוא נתחיל.
תוכן העניינים
דרישות מוקדמות
לפני שנתחיל, וודא שיש לך את הדרישות הבאות מותקנות על המחשב שלך:
-
Visual Studio Code או כל עורך קוד אחר שתבחרו
-
C# Dev Kit עבור Visual Studio Code
בנוסף, ניתן להשתמש ב-Visual Studio 2022 שמגיע עם תמיכה מובנית ב-.NET 8. אך במאמר זה, נעשה שימוש ב-Visual Studio Code. זה קל, נוח לשימוש ויכול לרוץ על מגוון פלטפורמות.
נשתמש ב-Swagger UI כדי לבדוק את ה- API שלנו. Swagger UI הוא כלי חזק שמאפשר לך לפעול עם ה- API שלך ישירות מהדפדפן שלך. הוא מספק ממשק ידידותי למשתמש לבדיקת נקודות הגישה של ה- API, ובכך מקל על בדיקה וניפוי שגיאות ב- API שלך.
כאשר אתה יוצר פרוייקט חדש, הוא יתקין באופן אוטומטי את החבילות הדרושות ויגדיר את הפרוייקט להשתמש ב- Swagger UI. .NET 8 כולל Swagger UI כברירת מחדל, אז בין אם אתה יוצר את היישום שלך ב-Visual Studio או באמצעות .NET, Swagger UI יוגדר עבורך.
הפעל את היישום שלך, וה-Swagger UI יפתח באופן אוטומטי בדפדפן שלך – אך מאחר שאנו משתמשים ב-VS Code, עלינו ללחוץ על מספר הפורט בטרמינל שלנו.
אתה יכול למצוא את קוד המקור עבור פרויקט זה ב-GitHub.
הקדמה ל-APIs מינימליים
דמיין לעבוד בקוד עם מספר רב של נקודות קצה, מה שהופך אותו לגדול ומורכב למדי. באופן מסורתי, בניית API ב-ASP.NET Core כרוכה בשימוש בבקרים, ניתוב, middleware וכמות משמעותית של קוד דיפולטיבי. אבל ישנן שתי גישות לבניית API ב-ASP.NET Core: הדרך המסורתית והדרך המינימלית.
הדרך המסורתית מוכרת לרוב המפתחים, כוללת בקרים וקוד תשתית נרחב. הדרך המינימלית, שהוצגה ב-.NET 6
, מאפשרת לך ליצור APIs עם קוד מינימלי ואפס דיפולטים. גישה זו מפשטת את תהליך הפיתוח, ומאפשרת לך להתמקד בכתיבת לוגיקת עסק במקום להתמודד עם קוד תשתית.
APIs מינימליים הם קלים, מהירים, ומתאימים לבניית APIs בגודל קטן עד בינוני. הם אידיאליים לפרוטוטייפים, בניית מיקרו-שירותים, או יצירת APIs פשוטים שאינם דורשים מורכבות רבה. במדריך זה, נחקור את עולם ה-APIs המינימליים ב-.NET 6 ונלמד כיצד ליצור API של חנות ספרים פונקציונלית לחלוטין מאפס.
איך ליצור API מינימלי
יצירת API מינימלי היא פשוטה כאשר משתמשים ב-dotnet CLI
, שכן התבנית הדיפולטיבית היא כבר API מינימלי. אבל אם אתה משתמש ב-Visual Studio, תצטרך להסיר את קוד הדיפולט ששייך לתבנית הפרויקט.
בואו נתחיל על ידי שימוש ב־dotnet CLI
כדי ליצור פרוייקט API מינימלי.
dotnet new webapi -n BookStoreApi
הפקודה dotnet new webapi
יוצרת פרוייקט API מינימלי חדש בשם BookStoreApi
. בפרוייקט זה נמצאים הקבצים והתיקיות הדרושים כדי להתחיל.
בואו נסתכל על מבנה הפרוייקט:
-
Program.cs
: נקודת הכניסה של האפליקציה, היכן שהמארח מוגדר. -
bookapi-minimal.sln
: הקובץ המכיל את הפתרון הכולל את הפרוייקט. -
bookapi-minimal.http
: קובץ המכיל בקשות HTTP דוגמה לבדיקת הAPI. -
bookapi-minimal.csproj
: קובץ הפרוייקט שמכיל את התצורה של הפרוייקט. -
appsettings.json
: קובץ ההגדרות ששומר על הגדרות האפליקציה. -
appsettings.Development.json
: קובץ ההגדרות עבור סביבת הפיתוח.
כאשר אתה פותח את קובץ program.cs, תשים לב שהקוד הוא מינימלי. בקובץ Program.cs
מופיע הקוד הבא:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
אם אינך מבין באופן מלא את הקוד כעת, אל תדאג – נכסה אותו בפרקים הבאים. הנקודה העיקרית היא שממשקי תוכנה מינימליים דורשים כמעט כלום קוד, שזו אחת מיתרונותיהם העיקריים.
הקוד ברירת המחדל מגדיר API פשוט לתחזיות מזג אוויר שאתה יכול להשתמש בו כדי לבדוק את ההגדרה שלך. זה יוצר רשימת תחזיות מזג אוויר ומחזיר אותן כאשר אתה עשוי בקשת GET
לנקודת הקצה /weatherforecast
. בנוסף, הקוד כולל Swagger UI כדי לעזור לך לבדוק את ה-API.
תשים תשומת לב מיוחדת לשיטת app.MapGet
, שממפה נתיב לפונקציית מטפל. במקרה זה, היא ממפה את נתיב ה- /weatherforecast
לפונקציה שמחזירה רשימת תחזיות מזג אוויר. נשתמש בשיטות דומות כדי ליצור את הנקודות הסופיות שלנו בפרקים הבאים.
לפני שנתחיל ליצור את מבנה התיקיות של הפרויקט שלנו, נבין את שיטות ה-HTTP ב-API בהתבסס על קונטרולרים וממשקי תוכנה מינימליים.
שיטות ה-HTTP ב-API בהתבסס על קונטרולרים וממשקי תוכנה מינימליים
בגישה המבוססת על קונטרולרים, שהיא הדרך המסורתית ליצירת API באינטרנט, עליך ליצור מחלקת קונטרולר ולהגדיר שיטות עבור כל שיטת HTTP. לדוגמה:
-
כדי ליצור מתודת
GET
, אתה משתמש באטריבוט[HttpGet]
. -
כדי ליצור מתודת
POST
, אתה משתמש באטריבוט[HttpPost]
. -
כדי ליצור מתודת
PUT
, אתה משתמש באטריבוט[HttpPut]
. -
כדי ליצור מתודת
DELETE
, אתה משתמש באטריבוט[HttpDelete]
.
ככה נוצרות נקודות קצה בגישה מבוססת בקרות.
בניגוד לכך, APIs מינימליים משתמשים במתודות כמו app.MapGet
, app.MapPost
, app.MapPut
ו-app.MapDelete
כדי ליצור נקודות קצה. זו ההבחנה העיקרית בין שתי הגישות: APIs מבוססי בקרות משתמשים באטריבוטים כדי להגדיר נקודות קצה, בעוד ש-APIs מינימליים משתמשים במתודות.
עכשיו כשאתה מבין איך לטפל בבקשות HTTP הן ב-APIs מבוססי בקרות והן ב-APIs מינימליים, בוא ניצור את מבנה תיקיות הפרויקט שלנו.
לפני שניצור את מבנה תיקיות הפרויקט שלנו, נריץ קודם את מה שיש לנו. כפי שלמדנו קודם, כאשר אתה יוצר פרויקט עם Visual Studio או .NET CLI, הוא מגיע עם פרויקט WeatherForecast ברירת מחדל שאותו נוכל להריץ ולראות בממשק המשתמש. נריץ אותו כדי לוודא שהכל עובד לפני שנמשיך ליצור את תיקיית הפרויקט שלנו.
הרץ את הפקודה הזאת:
dotnet run
אתה אמור לראות את הפלט הבא:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5228
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\Devolopemnt\Dotnet\bookapi-minimal
זה אומר שהיישום פועל ומקשיב ב-http://localhost:5228
. כפי שהזכרתי למעלה, מכיוון שאנחנו משתמשים ב-dotnet CLI
וב-Visual Studio Code, היישום לא יפתח אוטומטית את הדפדפן עבורנו. אנחנו צריכים לעשות זאת ידנית.
פתח את הדפדפן שלך ועבור ל-http://localhost:5228/swagger/index.html
כדי לראות את התגובה ברירת המחדל מה-API.
אתה אמור לראות משהו כזה:
עכשיו הדבר הבא שעלינו לעשות הוא למצוא דרך לארגן את הפרויקט שלנו וליצור את הקבצים והתיקיות הנדרשים כדי להתחיל.
קבצי פרויקט API מינימליים
כדי לארגן את הפרויקט שלנו, ניצור היררכיית תיקיות מסודרת. זה יעזור לשמור על הקוד שלנו נקי וניתן לתחזוקה. הנה מבנה התיקיות שאנו נשתמש בו:
-
AppContext: מכיל את הקשר לבסיס הנתונים והגדרות קשורות.
-
הגדרות: מכיל הגדרות של Entity Framework Core ונתוני זריעה עבור מסד הנתונים.
-
חוזים: מכיל אובייקטים להעברת נתונים (DTOs) המשמשים באפליקציה שלנו.
-
נקודות סיום: המקום בו אנו מגדירים ומגדירים את נקודות הקצה של ה-API המינימלי שלנו.
-
חריגות: מכיל מחלקות חריגות מותאמות אישית בפרויקט.
-
הרחבות: מכיל שיטות הרחבה שנשתמש בהן במהלך הפרויקט.
-
מודלים: מכיל מודלים לוגיים.
-
שירותים: מכיל מחלקות שירות שמיישמות לוגיקת עסקים.
-
ממשקים: מכיל 定义的接口,用于映射我们的服务。
בתוך Visual Studio Code, ניתן ליצור את מבנה התיקיות הבא כך:
- AppContext
- Configurations
- Contracts
- Endpoints
- Exceptions
- Extensions
- Models
- Services
- Interfaces
לאחר הגדרתו, מבנה התיקיות של פרויקטך צריך להיראות כך:
עכשיו שמבנה הפרויקט שלנו מוכן נוכל להמשיך ולהתחיל לכתוב את הקוד שלנו. בואו נתחיל על ידי יצירת המודלים שלנו.
כיצד ליצור את המודלים
בסעיף זה, ניצור מודלים עבור היישום שלנו. המודלים הם הבלוקים בנייה של היישום שלנו, מייצגים את הנתונים שבהם היישום שלנו יעבוד. לדוגמה, ניצור מודל עבור ספר.
כדי להתחיל, צרו תיקייה בשם Models
בתיקיית הפרויקט שלכם. בתוך תיקייה זו, צרו קובץ בשם BookModel.cs
והוסיפו את הקוד הבא:
// Models/BookModel.cs
namespace bookapi_minimal.Models
{
public class BookModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
המחלקה BookModel
מגדירה את המאפיינים המייצגים את פרטי הספר, כמו כותרת
, מחבר
, תיאור
, קטגוריה
, שפה
, ו- סה"כ עמודים
. כל מאפיין מיועד לשמירת מידע ספציפי על הספר, עשוי להקל על ניהול ועיבוד נתוני הספר בתוך היישום שלנו.
עכשיו שיצרנו את המודל שלנו, בואו ניצור את ההקשר של בסיס הנתונים שלנו.
כיצד ליצור את ההקשר של בסיס הנתונים
ההקשר של בסיס הנתונים הוא מחלקה שמייצגת סשן עם בסיס הנתונים. היא אחראית על התקשרות עם בסיס הנתונים וביצוע פעולות בסיס נתונים. ביישום שלנו, נשתמש ב-Entity Framework Core כדי להתקשר עם הבסיס הנתונים שלנו.
התקינו את החבילות הנדרשות
לפני שניצור את context בסיס הנתונים שלנו, עלינו להתקין את החבילות הבאות:
-
Microsoft.EntityFrameworkCore
-
Microsoft.EntityFrameworkCore.SqlServer
-
FluentValidation.DependencyInjectionExtensions
ניתן להתקין את החבילות באמצעות הפקודות הבאות:
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package FluentValidation.DependencyInjectionExtensions
אימות התקנת החבילות
כדי לוודא כי החבילות מותקנות, פתח את קובץ bookapi-minimal.csproj
בתיקיית השורש של הפרויקט שלך. עליך לראות את החבילות שהותקנו מפורטות כך:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>bookapi_minimal</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
זה מאשר שהחבילות הותקנו בהצלחה.
עכשיו ניצור את context בסיס הנתונים שלנו.
בתיקיית AppContext, צור קובץ חדש בשם ApplicationContext.cs
והוסף את הקוד הבא:
// AppContext/ApplicationContext.cs
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.AppContext
{
public class ApplicationContext(DbContextOptions<ApplicationContext> options) : DbContext(options)
{
// Default schema for the database context
private const string DefaultSchema = "bookapi";
// DbSet to represent the collection of books in our database
public DbSet<BookModel> Books { get; set; }
// Constructor to configure the database context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(DefaultSchema);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);
}
}
}
נפרק את הקוד למטה:
-
אנו מגדירים מחלקה בשם
ApplicationContext
שיורשת מ־DbContext
. מחלקתDbContext
היא חלק מ־Entity Framework Core ומייצגת סשן עם מסד הנתונים. -
הבונה מקבל מופע של
DbContextOptions<ApplicationContext>
. בונה זה משמש להגדרת אפשרויות ההקשר של מסד הנתונים. -
אנו מגדירים מאפיין בשם
Books
מסוגDbSet<BookModel>
. מאפיין זה מייצג את אוסף הספרים במסד הנתונים שלנו. -
אנו מחליף את השיטה
OnModelCreating
כדי להגדיר את סכימת בסיס הנתונים וליישם כל תצורות שהוגדרו ביישום שלנו.
עכשיו שיצרנו את ההקשר של בסיס הנתונים שלנו, בואו ניצור את שיטת ההרחבה שלנו ונרשום את ההקשר של הבסיס הנתונים במנוע ההזנה לתלות.
יצירת שיטת הרחבה
לפני שניצור את שיטת הרחבה, בואו נבין מהו שיטת רחבה בהקשר של ASP.NET Core.
שיטת רחבה היא שיטה סטטית שמוסיפה פונקציונליות חדשה לסוג קיים מבלי לשנות את הסוג המקורי. ב־ASP.NET Core, שיטות הרחבה נהריים בשימוש להרחבת פונקציונליות של ממשק ה־IServiceCollection
, שמשמש לרישום שירותים במנוע ההזנה לתלות.
שירותים הם רכיבים שמספקים פונקציונליות ליישום, כגון גישה למסד נתונים, לוגינג והגדרות. על ידי יצירת שיטת רחבה עבור ממשק ה־IServiceCollection
, תוכל לפשט את התהליך של רישום השירותים שלך במנוע ההזנה לתלות.
במקום לשים הכל בקובץ Program.cs
, ניצור שיטת הרחבה כדי לרשום את השירותים שלנו בתוך מנגנון הכניסה לתלות. זה יעזור לנו לשמור על הקוד שלנו נקי ומאורגן.
בתיקיית Extensions
, ניתן ליצור קובץ חדש בשם ServiceExtensions.cs
ולהוסיף את הקוד הבא:
using System.Reflection;
using bookapi_minimal.AppContext;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.Extensions
{
public static class ServiceExtensions
{
public static void AddApplicationServices(this IHostApplicationBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (builder.Configuration == null) throw new ArgumentNullException(nameof(builder.Configuration));
// הוספת הקשר לבסיס הנתונים
builder.Services.AddDbContext<ApplicationContext>(configure =>
{
configure.UseSqlServer(builder.Configuration.GetConnectionString("sqlConnection"));
});
// הוספת מאמתים מהאסימבליה הנוכחית
builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
}
}
}
בואו נפרק את הקוד לעיל:
-
אנו מגדירים מחלקה סטטית בשם
ServiceExtensions
שמכילה שיטת הרחבה בשםAddApplicationServices
. שיטה זו מרחיבה את ממשקIHostApplicationBuilder
, המשמש להגדרת קו העיבוד של בקשות האפליקציה. -
שיטת
AddApplicationServices
מקבלת מופע שלIHostApplicationBuilder
כפרמטר. פרמטר זה משמש לגישה להגדרות האפליקציה ולשירותים שלה. -
אנו מוסיפים את
ApplicationContext
לתוך מנוע ההזנה ומגדירים אותו להשתמש ב- SQL Server כספק בסיס הנתונים. אנו משיגים את מחרוזת החיבור מתוך קובץappsettings.json
באמצעות השיטהGetConnectionString
. -
אנו מוסיפים
validators
מתוך ה-assembly
הנוכחי באמצעות השיטהAddValidatorsFromAssembly
. שיטה זו סורקת את ה-assembly הנוכחי למחלקות שמיישמות את ממשק ה- IValidator ומרשימות אותן בתוך מנוע ההזנה.
בשלב הבא, אנו צריכים להוסיף את מחרוזת החיבור לקובץ appsettings.json
. הוסף את הקוד הבא לתוך קובץ ה- appsettings.json
שלך:
{
"ConnectionStrings": {
"sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
}
}
ודא שאתה מחליף your_password
עם הסיסמה האמיתית שלך של SQL Server.
קובץ ה- appsettings.json
שלך צריך להיראות כמו זה:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"sqlConnection": "Server=localhost\\SQLEXPRESS02;Database=BookAPIMinimalAPI;Integrated Security=true;TrustServerCertificate=true;"
},
"AllowedHosts": "*"
}
ברכות! יצרת בהצלחה את מצב המסד נתונים, את שיטת ההרחבה, ואת מחרוזת החיבור לאפליקציה שלך. בקטע הבא ניצור חוזה.
איך ליצור חוזה
חוזים הם אובייקטיי עבר נתונים (DTOs) שמגדירים את מבנה הנתונים המועברים בין הלקוח והשרת. באפליקציה שלנו, ניצור חוזים כדי לייצג את הנתונים שנשלחים ומתקבלים על ידי נקודות הקצה של ה- API שלנו.
כאן החוזים שאנו מתכננים ליצור:
-
CreateBookRequest: מייצג את הנתונים שנשלחים בעת יצירת ספר חדש.
-
UpdateBookRequest: מייצג את הנתונים שנשלחים בעת עדכון ספר קיים.
-
BookResponse: מייצג את הנתונים שמוחזרים בעת אחזור על ספר.
-
ErrorResponse: מייצג את תגובת השגיאה שמוחזרת בעת התרחשות חריגה.
-
ApiResponse: מייצג את התגובה שמוחזרת על ידי ה- API.
בתיקיית Contracts
, יש ליצור קובץ חדש בשם CreateBookRequest
ולהוסיף את הקוד הבא:
// Contracts/CreateBookRequest.cs
namespace bookapi_minimal.Contracts
{
public record CreateBookRequest
{
public string Title { get; init; }
public string Author { get; init; }
public string Description { get; init; }
public string Category { get; init; }
public string Language { get; init; }
public int TotalPages { get; init; }
}
}
בתיקיית Contracts
, יש ליצור קובץ חדש בשם UpdateBookRequest
ולהוסיף את הקוד הבא:
// חוזים/UpdateBookRequest.cs
namespace bookapi_minimal.Contracts
{
public record UpdateBookRequest
{
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
בתיקיית Contracts
, צור קובץ חדש בשם BookResponse
והוסף את הקוד הבא:
// חוזים/BookResponse.cs
namespace bookapi_minimal.Contracts
{
public record BookResponse
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public string Language { get; set; }
public int TotalPages { get; set; }
}
}
בתיקיית Contracts
, צור קובץ חדש בשם ErrorResponse
והוסף את הקוד הבא:
// חוזים/ErrorResponse.cs
namespace bookapi_minimal.Contracts
{
public record ErrorResponse
{
public string Title { get; set; }
public int StatusCode { get; set; }
public string Message { get; set; }
}
}
בתיקיית Contracts
, צור קובץ חדש בשם ApiResponse
והוסף את הקוד הבא:
// חוזים/ApiResponse.cs
namespace bookapi_minimal.Contracts
{
public class ApiResponse<T>
{
public T Data { get; set; }
public string Message { get; set; }
public ApiResponse(T data, string message)
{
Data = data;
Message = message;
}
}
}
אלו חוזים שיעזרו לנו להגדיר את מבנה הנתונים שנשלחים בין הלקוח והשרת, וזה יקל עלינו לעבוד עם הנתונים באפליקציה שלנו.
בקטע הבא, ניצור שירותים ליישום הלוגיקה העסקית של האפליקציה שלנו.
כיצד להוסיף שירותים
שירותים הם רכיבים שמספקים פונקציונליות לאפליקציה. באפליקציה שלנו, ניצור שירותים כדי ליישם את הלוגיקה העסקית של האפליקציה שלנו. ניצור שירותים שיטפלו בפעולות CRUD עבור ספרים, יעבירו אימות של נתוני ספר, ויטפלו בחריגות.
ב-ASP.NET Core, שירותים נרשמים במנתח ההזרמה לתוך וניתן להזרים אותם לרכיבים אחרים, כמו בקרים ונקודות קצה, אך זהו API מינימלי ולכן נזרום את השירותים ישירות לנקודות הקצה.
בואו ניצור ממשק עבור השירותים שלנו. בתיקיית Interfaces
, צרו קובץ חדש בשם IBookService.cs
והוסיפו את הקוד הבא:
// Interfaces/IBookService.cs
using bookapi_minimal.Contracts;
namespace bookapi_minimal.Interfaces
{
public interface IBookService
{
Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest);
Task<BookResponse> GetBookByIdAsync(Guid id);
Task<IEnumerable<BookResponse>> GetBooksAsync();
Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest);
Task<bool> DeleteBookAsync(Guid id);
}
}
. בואו נפרק את הקוד שנכתב: הגדרנו ממשק בשם IBookService
המכיל שיטות לטיפול בפעולות CRUD עבור ספרים. הממשק מגדיר את השיטות הבאות:
-
AddBookAsync
: מוסיף ספר חדש למסד הנתונים. -
GetBookByIdAsync
: מחזיר ספר לפי ה- ID שלו. -
GetBooksAsync
: מחזיר את כל הספרים ממסד הנתונים. -
UpdateBookAsync
: מעדכן ספר קיים.
אנו משתמשים בחוזה שיצרנו קודם בתיקיית Contracts
. הממשק IBookService
מגדיר את מבנה השיטות שיבוצעו על ידי מחלקות השירות. זה עוזר לנו להפריד בין הממשק לבין המימוש, וכך הופך זאת לקל יותר לתחזק ולבדוק את הקוד שלנו.
עכשיו שיצרנו את הממשק, בואו ניצור את המחלקת שירות שמיישמת את הממשק.
כיצד ליישם את שירות הספרים
השירות הזה יממש את הממשק IBookService
ויספק את הלוגיקה העסקית לאפליקציה שלנו. בתיקיית Services
, צרו קובץ חדש בשם BookService.cs
. הקובץ הראשוני שלכם צריך להיראות כך:
// Services/BookService.cs
namespace bookapi_minimal.Services
{
public class BookService
{
}
}
הדבר הראשון שעלינו לעשות הוא להוסיף את הממשק למחלקת BookService
. עדכנו את מחלקת BookService
כך שתממש את הממשק IBookService
כך:
// Services/BookService.cs
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Services
{
public class BookService:IBookService
{
}
}
כאשר תעשו זאת, עשוי להופיע שגיאה ב- VS Code כיוון שלא מימשנו את השיטות בממשק. בואו נמשיך ונממש את השיטות במחלקת BookService
.
ב- VS Code תוכלו להשתמש בקיצור הדרך Ctrl + .
כדי לממש את השיטות בממשק. לאחר מכן תראו את הקוד הבא שנוצר עבורכם:
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Services
{
// Service class for managing books
public class BookService : IBookService
{
// Method to add a new book to the database
public Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
throw new NotImplementedException();
}
// Method to Delete a book from the database
public Task<bool> DeleteBookAsync(Guid id)
{
throw new NotImplementedException();
}
// Method to Get a book from the database by its ID
public Task<BookResponse> GetBookByIdAsync(Guid id)
{
throw new NotImplementedException();
}
// Method to Get all books from the database
public Task<IEnumerable<BookResponse>> GetBooksAsync()
{
throw new NotImplementedException();
}
// Method to Update a book in the database
public Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest updateBookRequest)
{
throw new NotImplementedException();
}
}
}
עכשיו אתה יכול לראות שהשיטות בממשק הוטמעו במחלקת BookService
. נמשיך ונממש את לוגיקת העסקים עבור כל שיטה בקטע הבא.
לפני שנעשה זאת, נוסיף את התלות הנדרשות למחלקת BookService
. אנו צריכים להכניס את התלות ApplicationContext
ו-ILogger
למחלקת BookService
. ApplicationContext
משמש לאינטראקציה עם מסד הנתונים, בעוד ILogger
משמש ללוגינג.
כדי להכניס את התלות, עדכן את מחלקת BookService
כדלקמן:
// Services/BookService.cs
// ...
private readonly ApplicationContext _context; // קשר עם מסד הנתונים
private readonly ILogger<BookService> _logger; // לוגר להודעות ושגיאות
//..
מאחר והוספנו את התלות, עלינו לעדכן את בנאי ה-BookService
כך שיקבל את התלות. עדכן את בנאי ה-BookService
כדלקמן:
// Services/BookService.cs
// ...
// בנאי לאתחול קשר המסד והלוגר
public BookService(ApplicationContext context, ILogger<BookService> logger)
{
_context = context;
_logger = logger;
}
// ...
עכשיו שהוספנו את התלות ועדכנו את בנאי המחלקה, נוכל לממש את לוגיקת העסקים עבור כל שיטה במחלקת BookService
.
בואו ניצור לוגיקה עבור פעולות ה-CREATE, READ, UPDATE, ו-DELETE במחלקת BookService
.
כיצד ליישם את השיטה AddBookAsync
כפי שציינתי מראש, נשתמש בשיטת AddBookAsync
כדי להוסיף ספר חדש למסד הנתונים. בשיטה זו, ניצור ישות ספר חדשה, נמפה את הנתונים מאובייקט CreateBookRequest
לישות הספר, ונשמור את יישות הספר במסד הנתונים. נחזיר גם את יישות הספר כאובייקט BookResponse
.
עדכן את השיטה AddBookAsync
במחלקת BookService
כדלקמן:
// Services/BookService.cs
// ...
/// <summary>
/// להוסיף ספר חדש
/// </summary>
/// <param name="createBookRequest">בקשת ספר להוספה</param>
/// <returns>פרטי הספר שנוצר</returns>
public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
try
{
var book = new BookModel
{
Title = createBookRequest.Title,
Author = createBookRequest.Author,
Description = createBookRequest.Description,
Category = createBookRequest.Category,
Language = createBookRequest.Language,
TotalPages = createBookRequest.TotalPages
};
// הוסף את הספר למסד הנתונים
_context.Books.Add(book);
await _context.SaveChangesAsync();
_logger.LogInformation("Book added successfully.");
// החזר את פרטי הספר שנוצר
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error adding book: {ex.Message}");
throw;
}
}
// ...
בקוד הזה, אנו יוצרים ישות ספר חדשה מאובייקט CreateBookRequest
, ממפים את הנתונים מאובייקט CreateBookRequest
לישות הספר, שומרים את ישות הספר במסד הנתונים, ומחזירים את ישות הספר כאובייקט BookResponse
.
אנו גם מפעילים רישום מידע ושגיאות באמצעות תלות ILogger
. אם יש חריגה במהלך התהליך, אנו מפעילים רישום של הודעת השגיאה ומשליכים מחדש את החריגה.
עכשיו שהמימוש של השיטה AddBookAsync
הושלם, בואו נממש את השיטה GetBookByIdAsync
.
איך לממש את השיטה GetBookByIdAsync
השיטה GetBookByIdAsync
משמשת לאחזור ספר לפי המזהה שלו מהמסד. בשיטה זו, נבצע שאילתא למסד הנתונים עבור הספר עם המזהה המצוין, ממפים את יישות הספר לאובייקט BookResponse
, ומחזירים את אובייקט BookResponse
.
עדכנו את השיטה GetBookByIdAsync
במחלקת BookService
כדלקמן:
// Services/BookService.cs
//...
/// <summary>
/// לקבל ספר לפי ה- ID שלו
/// </summary>
/// <param name="id">ה- ID של הספר</param>
/// <returns>פרטי הספר</returns>
public async Task<BookResponse> GetBookByIdAsync(Guid id)
{
try
{
// למצוא את הספר לפי ה- ID שלו
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// להחזיר את פרטי הספר
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving book: {ex.Message}");
throw;
}
}
//...
בקוד זה, אנו מבצעים שאילתה למסד הנתונים עבור הספר עם ה- ID המצוין, ממפים את יישות הספר לאובייקט BookResponse
, ומחזירים את אובייקט ה- BookResponse
. אנו גם מפעילים יומנים עבור מידע ושגיאות באמצעות התלות ILogger
.
אם הספר עם ה- ID המצוין לא נמצא, אנו מפעילים הודעת אזהרה ומחזירים null. אם קורה חריגה במהלך התהליך, אנו מפעילים הודעת שגיאה ומזרים מחדש את החריגה.
עכשיו שהמימוש של שיטת GetBookByIdAsync
כבר מוכן, בואו נממש את השיטה GetBooksAsync
.
איך לממש את השיטה GetBooksAsync
השיטה GetBooksAsync
משמשת לקבלת כל הספרים ממסד הנתונים. בשיטה זו, נבצע שאילתה לבסיס הנתונים עבור כל הספרים, ממפים כל ישות ספר לאובייקט BookResponse
, ומחזירים רשימה של אובייקטים BookResponse
.
עדכון שיטת GetBooksAsync
במחלקת BookService
כדלקמן:
// Services/BookService.cs
//...
/// <summary>
/// לקבלת כל הספרים
/// </summary>
/// <returns>רשימת כל הספרים</returns>
public async Task<IEnumerable<BookResponse>> GetBooksAsync()
{
try
{
// לקבל את כל הספרים ממסד הנתונים
var books = await _context.Books.ToListAsync();
// החזרת פרטי כל הספרים
return books.Select(book => new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
});
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving books: {ex.Message}");
throw;
}
}
//...
כאן, אנו מבצעים שאילתה לבסיס הנתונים עבור כל הספרים, ממפים כל ישות ספר לאובייקט BookResponse
, ומחזירים רשימה של אובייקטים BookResponse
. אנו גם משתמשים בלוגינג למידע ושגיאות באמצעות התלות ILogger
. אם חריגה מתרחשת במהלך התהליך, אנו מקליטים את הודעת השגיאה ומחזירים מחדש את החריגה.
כעת שהשיטה GetBooksAsync
מיושמת, בואו נממש את השיטה UpdateBookAsync
.
איך לממש את השיטה UpdateBookAsync
השיטה UpdateBookAsync
משמשת לעדכון ספר קיים במסד הנתונים. בשיטה זו, נבצע שאילתה למסד הנתונים עבור הספר עם ה- ID המצוין, נעדכן את יישות הספר עם הנתונים מאובייקט ה- UpdateBookRequest
, נשמור את יישות הספר המעודכנת במסד הנתונים, ונחזיר את יישות הספר המעודכנת כאובייקט BookResponse
.
עדכן את השיטה UpdateBookAsync
במחלקת BookService
כדלקמן:
// Services/BookService.cs
//...
/// <summary>
/// לעדכן ספר קיים
/// </summary>
/// <param name="id">ה- ID של הספר שיש לעדכן</param>
/// <param name="book">דגם הספר שעודכן</param>
/// <returns>פרטי הספר שעודכן</returns>
public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
{
try
{
// מצא את הספר הקיים לפי ה- ID שלו
var existingBook = await _context.Books.FindAsync(id);
if (existingBook == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// עדכן את פרטי הספר
existingBook.Title = book.Title;
existingBook.Author = book.Author;
existingBook.Description = book.Description;
existingBook.Category = book.Category;
existingBook.Language = book.Language;
existingBook.TotalPages = book.TotalPages;
// שמור את השינויים במסד הנתונים
await _context.SaveChangesAsync();
_logger.LogInformation("Book updated successfully.");
// החזר פרטי הספר שעודכן
return new BookResponse
{
Id = existingBook.Id,
Title = existingBook.Title,
Author = existingBook.Author,
Description = existingBook.Description,
Category = existingBook.Category,
Language = existingBook.Language,
TotalPages = existingBook.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error updating book: {ex.Message}");
throw;
}
}
//...
כאן, אנו עושים שאילתה למסד הנתונים עבור הספר עם ה- ID המסוים, מעדכנים את יישות הספר עם הנתונים מאובייקט ה- UpdateBookRequest
, שומרים את יישות הספר המעודכנת במסד הנתונים, ומחזירים את יישות הספר המעודכנת כאובייקט של BookResponse
. אנו גם מפעילים רישום של מידע ושגיאות באמצעות התלות ILogger
.
אם הספר עם ה- ID המסוים לא נמצא, אנו מפעילים הודעת אזהרה ומחזירים ערך ריק. אם יש חריגה במהלך התהליך, אנו מפעילים את הודעת השגיאה ומשליכים מחדש את החריגה.
עכשיו שהמימוש של השיטה UpdateBookAsync
בוצע, בואו נממש את השיטה DeleteBookAsync
.
איך לממש את השיטה DeleteBookAsync
השיטה DeleteBookAsync
משמשת למחיקת ספר קיים ממסד הנתונים. בשיטה זו, נעשה שאילתה למסד הנתונים עבור הספר עם ה- ID המסוים, מסירים את יישות הספר ממסד הנתונים, ומחזירים ערך בוליאני המציין האם הספר נמחק בהצלחה.
מעדכנים את השיטה DeleteBookAsync
במחלקת BookService
כך:
// Services/BookService.cs
//...
/// <summary>
/// מחיקת ספר על פי זיהויו
/// </summary>
/// <param name="id">זיהוי הספר שיש למחוק</param>
/// <returns>True אם הספר נמחק, false אחרת</returns>
public async Task<bool> DeleteBookAsync(Guid id)
{
try
{
// מציאת הספר על פי זיהויו
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return false;
}
// הסרת הספר מבסיס הנתונים
_context.Books.Remove(book);
await _context.SaveChangesAsync();
_logger.LogInformation($"Book with ID {id} deleted successfully.");
return true;
}
catch (Exception ex)
{
_logger.LogError($"Error deleting book: {ex.Message}");
throw;
}
}
//...
בקוד זה, אנו מבצעים שאילתת בסיס הנתונים לספר עם הזיהוי המסוים, מסירים את יישות הספר מבסיס הנתונים, ומחזירים ערך בוליאני המציין האם הספר נמחק בהצלחה. כמו כן, אנו מציינים מידע ושגיאות באמצעות תלות ILogger
.
אם הספר עם הזיהוי המסוים לא נמצא, אנו מקבלים הודעת אזהרה ומחזירים false. אם קורה חריגה במהלך התהליך, אנו מקבלים הודעת שגיאה ומזרים מחדש את החריגה.
עכשיו ביצעת בהצלחה את הלוגיקה העסקית עבור השיטות AddBookAsync
, GetBookByIdAsync
, GetBooksAsync
, UpdateBookAsync
, ו־DeleteBookAsync
במחלקת BookService
. על ידי אלו השיטות ניתן לנהל את פעולות ה־CRUD עבור ספרים, לאמת נתוני ספרים ולטפל בחריגות. כעת, המחלקה BookService
שלך אמורה להיראות כך:
using bookapi_minimal.AppContext;
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
namespace bookapi_minimal.Services
{
public class BookService : IBookService
{
private readonly ApplicationContext _context; // הקשר של בסיס הנתונים
private readonly ILogger<BookService> _logger; // מפעיל לרישום מידע ושגיאה
// בונה לאתחול את הקשר של בסיס הנתונים והמפעיל
public BookService(ApplicationContext context, ILogger<BookService> logger)
{
_context = context;
_logger = logger;
}
/// הוספת ספר חדש
/// </summary>
/// <param name="createBookRequest">בקשת ספר להוספה</param>
/// <returns>פרטי הספר שנוצר</returns>
public async Task<BookResponse> AddBookAsync(CreateBookRequest createBookRequest)
{
try
{
var book = new BookModel
{
Title = createBookRequest.Title,
Author = createBookRequest.Author,
Description = createBookRequest.Description,
Category = createBookRequest.Category,
Language = createBookRequest.Language,
TotalPages = createBookRequest.TotalPages
};
// הוספת הספר לבסיס הנתונים
_context.Books.Add(book);
await _context.SaveChangesAsync();
_logger.LogInformation("Book added successfully.");
// מחזיר את פרטי הספר שנוצר
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error adding book: {ex.Message}");
throw;
}
}
/// <summary>
/// מציג ספר לפי המזהה שלו
/// </summary>
/// <param name="id">המזהה של הספר</param>
/// <returns>פרטי הספר</returns>
public async Task<BookResponse> GetBookByIdAsync(Guid id)
{
try
{
// מוצא את הספר לפי המזהה שלו
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// מחזיר את פרטי הספר
return new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving book: {ex.Message}");
throw;
}
}
/// <summary>
/// מציג את כל הספרים
/// </summary>
/// <returns>רשימת כל הספרים</returns>
public async Task<IEnumerable<BookResponse>> GetBooksAsync()
{
try
{
// מקבל את כל הספרים מבסיס הנתונים
var books = await _context.Books.ToListAsync();
// מחזיר את פרטי כל הספרים
return books.Select(book => new BookResponse
{
Id = book.Id,
Title = book.Title,
Author = book.Author,
Description = book.Description,
Category = book.Category,
Language = book.Language,
TotalPages = book.TotalPages
});
}
catch (Exception ex)
{
_logger.LogError($"Error retrieving books: {ex.Message}");
throw;
}
}
/// <summary>
/// מעדכן ספר קיים
/// </summary>
/// <param name="id">המזהה של הספר לעדכון</param>
/// <param name="book">דגם הספר המעודכן</param>
/// <returns>פרטי הספר שעודכנו</returns>
public async Task<BookResponse> UpdateBookAsync(Guid id, UpdateBookRequest book)
{
try
{
// מוצא את הספר הקיים לפי המזהה שלו
var existingBook = await _context.Books.FindAsync(id);
if (existingBook == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return null;
}
// מעדכן את פרטי הספר
existingBook.Title = book.Title;
existingBook.Author = book.Author;
existingBook.Description = book.Description;
existingBook.Category = book.Category;
existingBook.Language = book.Language;
existingBook.TotalPages = book.TotalPages;
// שומר את השינויים בבסיס הנתונים
await _context.SaveChangesAsync();
_logger.LogInformation("Book updated successfully.");
// מחזיר את פרטי הספר שעודכנו
return new BookResponse
{
Id = existingBook.Id,
Title = existingBook.Title,
Author = existingBook.Author,
Description = existingBook.Description,
Category = existingBook.Category,
Language = existingBook.Language,
TotalPages = existingBook.TotalPages
};
}
catch (Exception ex)
{
_logger.LogError($"Error updating book: {ex.Message}");
throw;
}
}
/// <summary>
/// מוחק ספר לפי המזהה שלו
/// </summary>
/// <param name="id">המזהה של הספר שיש למחוק</param>
/// <returns>True אם הספר נמחק, אחרת יחזיר False</returns>
public async Task<bool> DeleteBookAsync(Guid id)
{
try
{
// מוצא את הספר לפי המזהה שלו
var book = await _context.Books.FindAsync(id);
if (book == null)
{
_logger.LogWarning($"Book with ID {id} not found.");
return false;
}
// מסיר את הספר מבסיס הנתונים
_context.Books.Remove(book);
await _context.SaveChangesAsync();
_logger.LogInformation($"Book with ID {id} deleted successfully.");
return true;
}
catch (Exception ex)
{
_logger.LogError($"Error deleting book: {ex.Message}");
throw;
}
}
}
}
מזל טוב! ביצעת בהצלחה את הלוגיקה העסקית עבור השיטות AddBookAsync
, GetBookByIdAsync
, GetBooksAsync
, UpdateBookAsync
, ו- DeleteBookAsync
במחלקת BookService
.
יש דבר אחד שעלינו לעשות: עלינו לרשום את השירות בשיטת ההרחבה שלנו. בואו נמשיך ונעשה זאת.
בקובץ ServiceExtensions.cs
שלך, הוסף את הקוד הבא:
// Extensions/ServiceExtensions.cs
//..
builder.Services.AddScoped<IBookService, BookService>();
//...
זה ירשום את מחלקת BookService
כשירות בהיקף. זה אומר שהשירות ייווצר פעם אחת לכל בקשה ויסולק לאחר שהבקשה הושלמה.
עכשיו שהשירות עובד, בואו נמשיך וניצור את מחלקות החריגים.
איך ליצור חריגים
טיפול תקין בחריגים חיוני להבטיח את יציבות ואמינות היישום. בהקשר של ASP.NET Core, ישנם שני סוגים ראשיים של חריגים:
-
חריגי מערכת: אלה הם חריגים שנזרקים על ידי ריצת .NET או המערכת הסותמת.
-
חריגי יישום: אלה הם חריגים שנזרקים על ידי קוד היישום כדי לטפל בשגיאות או תנאים מסוימים.
ב-ASP.NET Core עם .NET 8, הוכנסת תכונה חדשה בשם טיפול גלובלי בחריגות. תכונה זו מאפשרת לך לטפל בחריגות באופן גלובלי באפליקציה שלך, מה שהופך את ניהול השגיאות וספק חווית משתמש עקבית.
באפליקציה שלנו ניצור מחלקות חריגה מותאמות אישית כדי לטפל בשגיאות ומצבים מסוימים. נשתמש גם בתכונת טיפול גלובלי בחריגות כדי לנהל חריגות באופן גלובלי, מבטיחים גישה אחידה לטיפול בשגיאות בכל האפליקציה.
אנו מתכננים ליצור את המחלקות החריגה הבאות:
-
NoBookFoundException
: מוזרק כאשר ספר עם ה- ID המסוים לא נמצא. -
BookDoesNotExistException
: מוזרק כאשר ספר עם ה- ID המסוים לא קיים. -
GlobalExceptionHandler
: מנהל חריגות באופן גלובלי באפליקציה.
בתיקיית Exceptions
, צרו קובץ חדש בשם NoBookFoundException.cs
והוסיפו את הקוד הבא:
// Exceptions/NoBookFoundException.cs
namespace bookapi_minimal.Exceptions
{
public class NoBookFoundException : Exception
{
public NoBookFoundException() : base("No books found")
{}
}
}
בקוד זה, אנו יוצרים מחלקת יוצא דופן מותאמת אישית בשם NoBookFoundException
שממשיכה מהמחלקה Exception
. מחלקת NoBookFoundException
משמשת לטיפול בתרחיש בו לא נמצאו ספרים במסד הנתונים. אנו גם מספקים הודעת שגיאה מותאמת אישית עבור החריגה.
בתיקיית Exceptions
, יש ליצור קובץ חדש בשם BookDoesNotExistException.cs
ולהוסיף את הקוד הבא:
namespace bookapi_minimal.Exceptions
{
public class BookDoesNotExistException : Exception
{
private int id { get; set; }
public BookDoesNotExistException(int id) : base($"Book with id {id} does not exist")
{
this.id = id;
}
}
}
בקוד זה, אנו יוצרים מחלקת יוצא דופן מותאמת אישית בשם BookDoesNotExistException
שממשיכה מהמחלקה Exception
. מחלקת BookDoesNotExistException
משמשת לטיפול בתרחיש בו ספר עם המזהה המסוים אינו קיים במסד הנתונים. גם כאן אנו מספקים הודעת שגיאה מותאמת אישית עבור החריגה.
בתיקיית Exceptions
, יש ליצור קובץ חדש בשם GlobalExceptionHandler.cs
ולהוסיף את הקוד הבא:
// חריגות/GlobalExceptionHandler.cs
using System.Net;
using bookapi_minimal.Contracts;
using Microsoft.AspNetCore.Diagnostics;
namespace bookapi_minimal.Exceptions
{
// מחלקת מנהל חריגות גלובלי המיישמת את הממשק IExceptionHandler
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
// קונסטרוקטור לאתחול הלוגר
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
_logger = logger;
}
// שיטה לטיפול בחריגות בצורה אסינכרונית
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
// רישום פרטי החריגה
_logger.LogError(exception, "An error occurred while processing your request");
var errorResponse = new ErrorResponse
{
Message = exception.Message,
Title = exception.GetType().Name
};
// קביעת קוד הסטטוס על פי סוג החריגה
switch (exception)
{
case BadHttpRequestException:
errorResponse.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case NoBookFoundException:
case BookDoesNotExistException:
errorResponse.StatusCode = (int)HttpStatusCode.NotFound;
break;
default:
errorResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
// קביעת קוד סטטוס התגובה
httpContext.Response.StatusCode = errorResponse.StatusCode;
// כתיבת התגובה על השגיאה כ-JSON
await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);
// החזרת true כדי לציין שהחריגה טופלה
return true;
}
}
}
בואו ננתח את הקוד למעלה:
-
אנו מגדירים מחלקה בשם
GlobalExceptionHandler
המיישמת את הממשקIExceptionHandler
. הממשקIExceptionHandler
משמש לטיפול בחריגות באופן גלובלי באפליקציה. -
המחלקה
GlobalExceptionHandler
מכילה קונסטרוקטור שמאתחל את התלותILogger<GlobalExceptionHandler>
. הILogger
משמש לרישום מידע וטעויות. -
השיטה
TryHandleAsync
משמשת לטיפול בחריגות באופן אסינכרוני. השיטה מקבלת את הפרמטריםHttpContext
,Exception
, ו־CancellationToken
. -
אנו מדביקים את פרטי החריגה באמצעות התלות
ILogger
. -
אנו יוצרים אובייקט
ErrorResponse
כדי לייצג את תגובת השגיאה שמוחזרת על ידי ה- API. אובייקט ה־ErrorResponse
מכיל את הודעת השגיאה, הכותרת, וקוד הסטטוס. -
אנו מחליטים על קוד הסטטוס בהתאם לסוג החריגה. אם החריגה היא
BadHttpRequestException
, אנו מגדירים את קוד הסטטוס ל־BadRequest
. אם החריגה היאNoBookFoundException
אוBookDoesNotExistException
, אנו מגדירים את קוד הסטטוס ל־NotFound
. אחרת, אנו מגדירים את קוד הסטטוס ל־InternalServerError
. -
אנו מגדירים את קוד הסטטוס בתגובה באמצעות המאפיין
httpContext.Response.StatusCode
. -
אנו כותבים את תגובת השגיאה כ JSON באמצעות השיטה
httpContext.Response.WriteAsJsonAsync
. -
אנו מחזירים
true
כדי לציין שהחריגה טופלה בהצלחה.
עכשיו שיצרנו את מחלקות החריגות, בואו נרשום את ה־GlobalExceptionHandler
בתוך תיק הכניסה לתלות. מאחר שיצרנו שיטת ההרחבה לרישום שירותים בתוך תיק הכניסה לתלות, נוסיף את ה־GlobalExceptionHandler
לתוך מחלקת ה־ServiceExtensions
.
עדכנו את מחלקת ה־ServiceExtensions
בתיקיית Extensions
כך:
// Extensions/ServiceExtensions.cs
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
//...
השיטה AddExceptionHandler
מרשימה את המחליף GlobalExceptionHandler
בתוך תופס התלות. השיטה AddProblemDetails
מרשימה את מחליף המידע ProblemDetails
בתוך תופס התלות.
עכשיו שרישמנו את המחליף GlobalExceptionHandler
בתוך תופס התלות, נוכל להשתמש בו כדי לטפל בחריגות באופן גלובלי באפליקציה שלנו. בקטע הבא, ניצור את קצוות ה- API לקשר עם נתוני הספרים.
כיצד ליצור את קצוות ה- API
בהקשר של ממשקי API מינימליים ב- ASP.NET Core, ישנן הרבה דרכים להגדיר את הקצוות שלך.
ניתן להגדיר אותם ישירות בקובץ ה- Program.cs
שלך. אך כאשר הפרויקט שלך מתרחב ואתה זקוק להוסיף קצוות או פונקציונליות נוספת, יעיל לארגן את הקוד שלך בצורה טובה יותר. דרך אחת להשיג זאת היא על ידי יצירת מחליף נפרד לטיפול בכל הקצוות.
כפי שדנו למעלה, ממשקי API מינימליים לא משתמשים בבקרים או צפיות כמו באפליקציות ASP.NET Core מסורתיות. במקום זאת, הם משתמשים בשיטות כגון MapGet
, MapPost
, MapPut
, ו- MapDelete
כדי להגדיר שיטות HTTP ונתיבים לקצוות ה- API.
כדי להתחיל, נווט לתיקיית Endpoints
וצור קובץ חדש בשם BookEndpoints.cs
. הוסף את הקוד הבא לקובץ:
// נקודות קצה/BookEndpoints.cs
namespace bookapi_minimal.Endpoints
{
public static class BookEndPoint
{
public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
{
return app;
}
}
}
המחלקה BookEndpoints
מכילה את השיטה MapBookEndPoint
שמחזירה אובייקט IEndpointRouteBuilder
. האובייקט IEndpointRouteBuilder
משמש להגדרת שיטות ה-HTTP והנתיבים עבור נקודות הקצה של ה-API. בסעיפים הבאים, נגדיר את נקודות הקצה של ה-API עבור יצירה, קריאה, עדכון ומחיקת ספרים.
כיצד ליצור את נקודת הקצה AddBookAsync
לספרים
בסעיף זה, ניצור את נקודת הקצה AddBookAsync
. נקודת קצה זו תקבל אובייקט Book
כגוף JSON ותוסיף אותו למסד הנתונים. נשתמש בשיטת MapPost
כדי להגדיר את שיטת ה-HTTP והנתיב עבור נקודת הקצה הזו.
הוסף את הקוד הבא למחלקת BookEndpoints
:
// נקודות קצה/BookEndpoints.cs
//...
// נקודת קצה להוספת ספר חדש
app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
{
var result = await bookService.AddBookAsync(createBookRequest);
return Results.Created($"/books/{result.Id}", result);
});
//...
-
הגדרת הנתיב: שיטת MapPost מגדירה את הנתיב עבור נקודת הקצה כ-
/books
. -
דגם הבקשה: נקודת הקצה מקבלת אובייקט
CreateBookRequest
כגוף JSON. האובייקטCreateBookRequest
מכיל את הנתונים הנדרשים ליצירת ספר חדש. -
דגם תגובה: הנקודת הקצה מחזירה אובייקט
Book
כעובדת JSON. אובייקטBook
מכיל את הנתונים עבור הספר שנוצר בהצלחה. -
ערך החזרה: הנקודת הקצה מחזירה תוצאת
Created
. תוצאתCreated
מכילה את מיקום הספר שנוצר ואת אובייקטBook
.
כיצד ליצור את נקודת הקצה GetBookAsync
לקבלת הספר
בסעיף זה, ניצור את נקודת הקצה GetBookAsync
. נקודת הקצה תקבל מזהה ספר כפרמטר שאילתה ותחזיר את הספר עם המזהה המסוים. נשתמש בשיטת MapGet
כדי להגדיר את שיטת ה-HTTP והנתיב עבור נקודת הקצה הזו.
הוסיפו את הקוד הבא למחלקת BookEndpoints
:
// Endpoints/BookEndpoints.cs
// ...
// נקודת קצה לקבלת כל הספרים
app.MapGet("/books", async (IBookService bookService) =>
{
var result = await bookService.GetBooksAsync();
return Results.Ok(result);
});
//...
-
הגדרת נתיב: שיטת MapGet מגדירה את הנתיב עבור נקודת הקצה כ-
/books
. -
דגם של בקשה: נקודת הקצה מקבלת אובייקט
Book
כגוף JSON. האובייקטBook
מכיל את הנתונים הנדרשים ליצירת ספר חדש. -
דגם של תגובה: נקודת הקצה מחזירה אובייקט
Book
כגוף JSON. האובייקטBook
מכיל את הנתונים עבור הספר שנוצר כעת. -
ערך החזרה: נקודת הקצה מחזירה תוצאת
Ok
. תוצאתOk
מכילה את אובייקט ה-Book
.
איך ליצור את נקודת הקצה של GetBookByIdAsync GetBookByIdAsync
בסעיף זה, ניצור את נקודת הקצה GetBookByIdAsync
. נקודת הקצה זו תקבל מזהה של ספר כפרמטר בנתיב ותחזיר את הספר עם המזהה המסוים. נשתמש בשיטת MapGet
כדי להגדיר את השיטת HTTP והנתיב עבור נקודת הקצה זו.
הוסף את הקוד הבא למחלקת BookEndpoints
:
// Endpoints/BookEndpoints.cs
//...
// נקודת קצה לקבלת ספר לפי המזהה
app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.GetBookByIdAsync(id);
return result != null ? Results.Ok(result) : Results.NotFound();
});
//...
-
הגדרת הנתיב: השיטה MapGet מגדירה את הנתיב עבור נקודת הקצה כ-
/books/{id:guid}
. הפרמטר{id:guid}
מציין כי הפרמטרid
צריך להיות GUID. -
מודל בקשה: נקודת הקצה מקבלת אובייקט
Book
כמטען JSON. האובייקטBook
מכיל את המידע הדרוש ליצירת ספר חדש. -
מודל תגובה: נקודת הקצה מחזירה אובייקט
Book
כמטען JSON. האובייקטBook
מכיל את המידע לספר שנוצר בהצלחה. -
ערך החזרה: נקודת הקצה מחזירה תוצאת
Ok
אם הספר נמצא. תוצאתNotFound
מוחזרת אם הספר לא נמצא.
כיצד ליצור את נקודת הקצה לעדכון הספר UpdateBookAsync
בסעיף זה, ניצור את נקודת הקצה UpdateBookAsync
. נקודת הקצה זו תקבל מזהה של ספר כפרמטר במסלול ואובייקט Book
כתוצאת JSON ותעדכן את הספר עם המזהה המצויין. נשתמש בשיטת MapPut
כדי להגדיר את שיטת ה־HTTP והמסלול עבור נקודת קצה זו.
הוסף את הקוד הבא למחלקת BookEndpoints
:
// נקודות הקצה/BookEndpoints.cs
//...
// נקודת קצה לעדכון ספר לפי מזהה
app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
{
var result = await bookService.UpdateBookAsync(id, updateBookRequest);
return result != null ? Results.Ok(result) : Results.NotFound();
});
//...
-
הגדרת המסלול: שיטת ה־MapPut מגדירה את המסלול עבור נקודת הקצה כ־
/books/{id:guid}
. הפרמטר{id:guid}
מציין שהפרמטרid
צריך להיות GUID. -
מודל בקשה: הנקודת סיום מקבלת אובייקט
Book
כערך JSON. האובייקטBook
מכיל את הנתונים הנדרשים ליצירת ספר חדש. -
מודל תגובה: הנקודת סיום מחזירה אובייקט
Book
כערך JSON. האובייקטBook
מכיל את הנתונים עבור הספר שנוצר חדש. -
ערך החזרה: הנקודת סיום מחזירה תוצאת
Ok
אם הספר נמצא. תוצאתNotFound
מוחזרת אם הספר לא נמצא.
איך ליצור את נקודת הסיום של DeleteBookAsync
במקטע זה, ניצור את נקודת הקצה DeleteBookAsync
. נקודת הקצה זו תקבל את מזהה הספר כפרמטר נתיב ותמחוק את הספר עם המזהה המצוין. נשתמש בשיטת MapDelete
כדי להגדיר את שיטת ה-HTTP והנתיב עבור נקודת הקצה הזו.
הוסף את הקוד הבא למחלקת BookEndpoints
:
// Endpoints/BookEndpoints.cs
//...
// נקודת קצה למחיקת ספר לפי מזהה
app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
return result ? Results.NoContent() : Results.NotFound();
});
//...
-
הגדרת נתיב: שיטת MapDelete מגדירה את הנתיב עבור נקודת הקצה כ-
/books/{id:guid}
. הפרמטר{id:guid}
מציין כי הפרמטרid
צריך להיות GUID. -
דגם בקשה: נקודת הקצה מקבלת אובייקט
Book
כנתוני JSON. האובייקטBook
מכיל את הנתונים הנדרשים ליצירת ספר חדש. -
דגם תגובה: נקודת הקצה מחזירה אובייקט
Book
כנתוני JSON. האובייקטBook
מכיל את הנתונים עבור הספר שנוצר לאחרונה. -
ערך החזרה: נקודת הקצה מחזירה תוצאת
NoContent
אם הספר נמחק בהצלחה. תוצאתNotFound
מוחזרת אם הספר לא נמצא.
עכשיו הגדרנו את כל השיטות עבור נקודות הקצה של הספר. לכן מחלקת הנקודת הקצה שלך צריכה להיראות כך:
// Endpoints/BookEndpoints.cs
using bookapi_minimal.Contracts;
using bookapi_minimal.Interfaces;
namespace bookapi_minimal.Endpoints
{
public static class BookEndPoint
{
public static IEndpointRouteBuilder MapBookEndPoint(this IEndpointRouteBuilder app)
{
// Define the endpoints
// Endpoint to add a new book
app.MapPost("/books", async (CreateBookRequest createBookRequest, IBookService bookService) =>
{
var result = await bookService.AddBookAsync(createBookRequest);
return Results.Created($"/books/{result.Id}", result);
});
// Endpoint to get all books
app.MapGet("/books", async (IBookService bookService) =>
{
var result = await bookService.GetBooksAsync();
return Results.Ok(result);
});
// Endpoint to get a book by ID
app.MapGet("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.GetBookByIdAsync(id);
return result != null ? Results.Ok(result) : Results.NotFound();
});
// Endpoint to update a book by ID
app.MapPut("/books/{id:guid}", async (Guid id, UpdateBookRequest updateBookRequest, IBookService bookService) =>
{
var result = await bookService.UpdateBookAsync(id, updateBookRequest);
return result != null ? Results.Ok(result) : Results.NotFound();
});
// Endpoint to delete a book by ID
app.MapDelete("/books/{id:guid}", async (Guid id, IBookService bookService) =>
{
var result = await bookService.DeleteBookAsync(id);
return result ? Results.NoContent() : Results.NotFound();
});
return app;
}
}
}
ברכותינו! יצרת את כל נקודות הקצה עבור API הספר. נקודות הקצה מטפלות בפעולות ה-CRUD עבור ספרים ומחזירות את התגובות המתאימות בהתאם לבקשה והנתונים.
כיצד לרשום את נקודות הקצה
לאחר הגדרת נקודות ה- API עבור ה- API של הספר, השלב הבא הוא לרשום את נקודות הקצה הללו בקובץ Program.cs
. נשתמש בשיטת MapBookEndpoints
כדי לרשום את נקודות הספר.
כדאי גם לנקות את ה- class שלנו ב- Program.cs
כדי להבטיח שהוא יישאר מאורגן וניתן לתחזוקה.
// Program.cs
using System.Reflection;
using bookapi_minimal.Endpoints;
using bookapi_minimal.Services;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.AddApplicationServices();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c=>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Mimal API", Version = "v1", Description = "Showing how you can build minimal " +
"api with .net" });
// קביעת נתיבי ההערות עבור ה- Swagger JSON וה- UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
var app = builder.Build();
// הגדרת צינור הבקשות של HTTP.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseExceptionHandler();
app.MapGroup("/api/v1/")
.WithTags(" Book endpoints")
.MapBookEndPoint();
app.Run();
בואו נברר את רכיבי המפתח של קובץ Program.cs
:
-
AddApplicationServices: שיטה זו רושמת את השירותים הדרושים עבור ה- API. זו שיטת הרחבה שיצרנו קודם כדי להוסיף שירותים למנגנון ההזנה לתלות.
-
AddSwaggerGen: שיטה זו רושמת את מחולל ה- Swagger, שמשמש ליצירת מסמך ה- Swagger עבור ה- API. אנו מציינים את הכותרת, הגרסה והתיאור של ה- API במסמך ה- Swagger.
-
MapGroup: שיטה זו מקבצת את נקודות הקצה. היא מקבלת נתיב כפרמטר ומחזירה אובייקט
IEndpointRouteBuilder
. אנו משתמשים בשיטתWithTags
כדי להוסיף תגיות לנקודות הקצה ובשיטתMapBookEndpoints
כדי לרשום את נקודות הסופר. -
Run: שיטה זו מפעילה את היישום.
כדי לאפשר תיעוד Swagger, עליך להוסיף את פרופריות ה-GenerateDocumentationFile
לקובץ ה-.csproj
שלך. בדוגמה זו, הקובץ נקרא bookapi-minimal.csproj
, אך השם עשוי להשתנות בהתאם לפרויקט שלך.
הוסף את השורה הבאה לקובץ ה-.csproj
שלך:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
כעת, ה-bookapi-minimal.csproj
צריך להיראות כך:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RootNamespace>bookapi_minimal</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
</Project>
עכשיו שרשמנו את נקודות הספר בקובץ Program.cs
, אנו יכולים להפעיל את היישום ולבדוק את נקודות ה-API באמצעות Swagger.
כאשר אתה מפעיל את היישום, אתה צריך לראות את התיעוד של Swagger בכתובת ה-URL הבאה: https://localhost:5001/swagger/index.html
. התיעוד של Swagger מספק מידע על נקודות ה- API, דגמי בקשה ותגובה, ומאפשר לך לבדוק את נקודות ה- API ישירות מהדפדפן. אתה צריך לראות משהו דומה לזה:
מזל טוב! המימוש שלך ללוגיקת העסקים עבור שירות הספרים, יצירת חריגות מותאמות, הגדרת נקודות ה- API, ורישום הנקודות בקובץ Program.cs
. גם הפעלת את התיעוד של Swagger כדי לבדוק את נקודות ה- API.
איך להוסיף נתוני זריעה למסד הנתונים
צעד חשוב נוסף הוא לזרוע את מסד הנתונים בנתונים ראשוניים כאשר היישום מתחיל. נתוני הזריעה הללו ימלאו את מסד הנתונים, מאפשרים לך לבדוק את נקודות ה- API שלך ללא הוספת נתונים באופן ידני.
בואו נוסיף כמה נתוני זריעה לפני ביצוע מיגרציות ובדיקת נקודות ה- API שלנו.
כדי להשיג זאת, ניצור מחלקה חדשה בתיקיית ההגדרות שלנו בשם BookTypeConfigurations
ונוסיף את הקוד הבא:
using bookapi_minimal.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace bookapi_minimal.Configurations
{
public class BookTypeConfigurations : IEntityTypeConfiguration<BookModel>
{
public void Configure(EntityTypeBuilder<BookModel> builder)
{
// להגדיר את שם הטבלה
builder.ToTable("Books");
// להגדיר את המפתח הראשי
builder.HasKey(x => x.Id);
// להגדיר מאפיינים
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property(x => x.Title).IsRequired().HasMaxLength(100);
builder.Property(x => x.Author).IsRequired().HasMaxLength(100);
builder.Property(x => x.Description).IsRequired().HasMaxLength(500);
builder.Property(x => x.Category).IsRequired().HasMaxLength(100);
builder.Property(x => x.Language).IsRequired().HasMaxLength(50);
builder.Property(x => x.TotalPages).IsRequired();
// נתוני זריעה
builder.HasData(
new BookModel
{
Id = Guid.NewGuid(),
Title = "The Alchemist",
Author = "Paulo Coelho",
Description = "The Alchemist follows the journey of an Andalusian shepherd",
Category = "Fiction",
Language = "English",
TotalPages = 208
},
new BookModel
{
Id = Guid.NewGuid(),
Title = "To Kill a Mockingbird",
Author = "Harper Lee",
Description = "A novel about the serious issues of rape and racial inequality.",
Category = "Fiction",
Language = "English",
TotalPages = 281
},
new BookModel
{
Id = Guid.NewGuid(),
Title = "1984",
Author = "George Orwell",
Description = "A dystopian social science fiction novel and cautionary tale about the dangers of totalitarianism. ",
Category = "Fiction",
Language = "English",
TotalPages = 328
}
);
}
}
}
בואו נפרק את הקוד שלמעלה:
ב- Entity Framework Core, אתה יכול להשתמש בממשק IEntityTypeConfiguration
כדי לקבוע את סוג הישות ולזרוע נתונים עבור מסד הנתונים. מחלקת BookTypeConfigurations
מממשת את הממשק IEntityTypeConfiguration<BookModel>
ומספקת את ההגדרות עבור הישות BookModel
.
-
שיטת Configure: שיטה זו משמשת לקביעת סוג הישות
BookModel
. היא מגדירה את שם הטבלה, את המפתח הראשי ואת המאפיינים עבור הישותBookModel
.-
שם הטבלה: שיטת
ToTable
מציינת את שם הטבלה שתיווצר במסד הנתונים. במקרה זה, שם הטבלה מוגדר ל-"Books". -
מפתח ראשי: שיטת
HasKey
מציינת את המפתח הראשי עבור הישותBookModel
. המפתח הראשי מוגדר למאפייןId
. -
מאפיינים: שיטת
Property
קובעת את המאפיינים של הישותBookModel
. היא מציינת את סוג הנתונים, אורך, ומגבלות עבור כל מאפיין.
-
-
נתוני זריעה: שיטת
HasData
מזריעה את מסד הנתונים עם נתונים ראשוניים. היא יוצרת שלושה אובייקטים שלBookModel
עם נתוני דוגמה לבדיקת נקודות הקצה של API.
כעת שיצרנו את מחלקת BookTypeConfigurations
, עלינו לרשום את התצורה הזו במחלקת ApplicationContext
. זה מבטיח שהתצורה תיושם כאשר המסד נתונים נוצר או מוגדר.
סוף סוף אנחנו כמעט מוכנים לבדוק את ה- API שלנו. אך לפני שנעשה זאת, עלינו לבצע העברות כדי ליצור את בסיס הנתונים ולהחיל את נתוני הגיזום.
זכור שהוספנו את מחרוזת החיבור של בסיס הנתונים שלנו בקובץ appsettings.json
? עכשיו בוא נבצע העברה ואחר כך נעדכן את בסיס הנתונים שלנו כדי שההעברה תיכנס לתוקף.
איך לבצע העברה
העברות מאפשרות לך לעדכן את סכימת בסיס הנתונים בהתבסס על שינויים שבוצעו במחלקות המודל שלך. ב- Entity Framework Core, ניתן להשתמש בפקודת dotnet ef migrations add
כדי ליצור העברה חדשה המשקפת את השינויים הללו.
כדי לבצע העברה, הריץ את הפקודה הבאה בטרמינל:
dotnet ef migrations add InitialCreate
אם הפקודה מוצלחת, תראה פלט דומה לזה:
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
עכשיו תראה תיקייה חדשה בשם Migrations
בפרוייקט שלך. התיקייה הזו מכילה את קבצי המעבר שנוצרו בהתבסס על השינויים שבוצעו במחלקות המודל שלך. קבצי ההעברה אלה כוללים את הפקודות ה- SQL הנדרשות כדי לעדכן את סכימת בסיס הנתונים.
איך לעדכן את בסיס הנתונים
לאחר יצירת ההעברה, עליך להחיל את ההעברה כדי לעדכן את סכימת בסיס הנתונים. ניתן להשתמש בפקודת dotnet ef database update
כדי להחיל את ההעברה ולעדכן את בסיס הנתונים. וודא ששרת ה- SQL פועל.
הריץ את הפקודה הבאה בטרמינל:
dotnet ef database update
זה יעדכן את סכימת בסיס הנתונים בהתבסס על השינויים שבוצעו במחלקות המודל שלך. וודא שאין שגיאות במחרוזת החיבור של הבסיס נתונים שלך.
איך לבדוק את נקודות הקצה של ה- API
כעת אנו יכולים לבדוק את הנקודות הקצה שלנו באמצעות Swagger. כדי לעשות זאת, הפעל את האפליקציה על ידי ביצוע הפקודה הבאה בטרמינל:
dotnet run
זה יפעיל את האפליקציה שלנו. ניתן לפתוח את הדפדפן שלך ולנווט אל https://localhost:5001/swagger/index.html
כדי לגשת למסמך ה- Swagger. עליך לראות רשימה של נקודות ה- API, דגמי בקשה ומענה, ואת היכולת לבדוק את נקודות הקצה ישירות מהדפדפן.
אם מספר הפורט שלך שונה מ- 5001
, אל תדאג – זה עדיין יעבוד. הפורט עשוי להשתנות בהתאם לסוג המכונה שאתה משתמש בה, אך זה עדיין ישיג את התוצאה הזו.
כיצד לבדוק את נקודת הקצה "קבלת כל הספרים"
כדי לבדוק את נקודת הקצה "קבלת כל הספרים", עקוב אחר השלבים הבאים:
-
במסמך ה- Swagger, לחץ על נקודת הקצה
GET /api/v1/books
. -
לחץ על כפתור
נסה אותו
. -
לחץ על כפתור
בצע
.
זה ישלח בקשה ל- API כדי לאחזר את כל הספרים במסד הנתונים.
עליך לראות את המענה מה- API, שיכלול את רשימת הספרים שהוזרעו במסד הנתונים.
התמונה למטה מציגה את המענה מה- API:
כיצד לבדוק את נקודת הקצה קבלת ספר לפי זיהוי
כדי לבדוק את נקודת הקצה קבלת ספר לפי זיהוי
, עקוב אחר השלבים הבאים:
-
במסמך ה-Swagger, לחץ על נקודת הקצה
GET /api/v1/books/{id}
. -
הזן את זיהוי הספר בשדה
id
. ניתן להשתמש באחד מזיהויי הספרים שהוזנו במסד הנתונים. -
לחץ על הכפתור
נסה אותו
.
פעולה זו תשלח בקשה ל-API כדי לאחזר את הספר עם הזיהוי המסוים. עליך לראות את התגובה מה-API, שתכלול את הספר עם הזיהוי המסוים.
התמונה למטה מציגה את התגובה מה-API:
כיצד לבדוק את נקודת הקצה הוספת ספר
כדי לבדוק את נקודת הקצה הוספת ספר
, עקוב אחר השלבים הבאים:
-
במסמך ה-Swagger, לחץ על נקודת הקצה
POST /api/v1/books
. -
לחץ על הכפתור
נסה אותו
. -
הזן את פרטי הספר בגוף הבקשה.
-
לחץ על כפתור
בצע
.
פעולה זו תשלח בקשה ל- API כדי להוסיף ספר חדש למסד הנתונים.
עליך לראות את התגובה מה- API, שתכלול את הספר שנוצר לאחרונה.
התמונה למטה מציגה את התגובה מה- API:
כיצד לבדוק את נקודת הקצה של עדכן ספר
כדי לבדוק את נקודת הקצה של עדכן ספר
, עקוב אחר השלבים האלה:
-
במסמך ה-Swagger, לחץ על נקודת הקצה
PUT /api/v1/books/{id}
. -
הזן את מזהה הספר בשדה
id
. ניתן להשתמש במזהה של אחד מהספרים שהוספנו לאחרונה. -
לחץ על כפתור
נסה את זה
.
פעולה זו תשלח בקשה ל- API כדי לעדכן את הספר עם המזהה המבוקש.
עליך לראות את התגובה מה- API, שתכלול את הספר שעודכן.
התמונה למטה מציגה את התגובה מה- API:
כיצד לבדוק את נקודת הקצה של מחק ספר
כדי לבדוק את נקודת הקצה של מחק ספר
, עקוב אחר השלבים האלה:
-
במסמך ה-Swagger, לחץ על נקודת הקצה
DELETE /api/v1/books/{id}
. -
הזן את מזהה הספר בשדה
id
. ניתן להשתמש באחד ממזהה הספרים שהוספנו לאחרונה או בנתוני הזרע. -
לחץ על הכפתור
נסה זאת
.
פעולה זו תשלח בקשה לAPI כדי למחוק את הספר עם המזהה המצויין.
התמונה למטה מציגה את התגובה מהAPI:
ברכות! ביצעת את כל פעולות הCRUD עבור ספרים ובדקת את נקודות הקצה של הAPI באמצעות Swagger, ואימתת שהן עובדות כצפוי. עכשיו תוכל לבנות על היסוד הזה כדי להוסיף יותר תכונות ופונקציונליות לAPI שלך.
מסקנה
מדריך זה חקר כיצד ליצור API מינימלי בASP.NET Core עם .NET 8. בנינו API של ספרים מקיף התומך בפעולות CRUD, מיישם חריגות מותאמות, הגדיר ורישם נקודות קצה לAPI ואפשר תיעוד Swagger לבדיקה קלה.
על פי המדריך הזה, רכשת יסוד חזק לבניית API מינימליים עם ASP.NET Core. עכשיו תוכל ליישם את הידע הזה וליצור API חזקים עבור דמיינים ותעשיות שונות.
מקווה שמצאתם את המדריך עזרני ומעניין. תודה על הקריאה!
אל תהססו להתחבר איתי ברשתות החברתיות:
Source:
https://www.freecodecamp.org/news/create-a-minimal-api-in-net-core-handbook/