مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت دوم - شروع به مستند سازی یک API
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: شش دقیقه

پس از معرفی اجمالی OpenAPI و Swagger در قسمت قبل و همچنین ارائه‌ی یک برنامه‌ی نمونه که آن‌را به مرور تکمیل خواهیم کرد، در ادامه کتابخانه‌ی Swashbuckle را نصب کرده و شروع به مستند سازی API ارائه شده خواهیم کرد.


نصب Swashbuckle (سوواَش باکِل)

اگر عبارت Swashbuckle.AspNetCore را در سایت NuGet جستجو کنیم، چندین بسته‌ی مختلف مرتبط با آن‌را خواهیم یافت. ما در این بین، بیشتر به این بسته‌ها علاقمندیم:
- Swashbuckle.AspNetCore.Swagger: کار آن ارائه‌ی خروجی OpenAPI تولیدی بر اساس ASP.NET Core API برنامه‌ی ما، به صورت یک JSON Endpoint است.
- Swashbuckle.AspNetCore.SwaggerGen: کار آن ساخت Swagger document objects است؛ یا همان OpenAPI Specification.
عموما این دو بسته را با هم جهت ارائه‌ی OpenAPI Specification استفاده می‌کنند.
- Swashbuckle.AspNetCore.SwaggerUI: این بسته، نگارش جایگذاری شده‌ی (embedded) ابزار swagger-UI را به همراه دارد. کار آن، ارائه‌ی یک UI خودکار، بر اساس OpenAPI Specification است که از آن برای آزمایش API نیز می‌توان استفاده کرد.

یک نکته: اگر صرفا بسته‌ی Swashbuckle.AspNetCore را نصب کنیم، هر سه بسته‌ی فوق را با هم دریافت خواهیم کرد و اگر از Visual Studio برای نصب آن‌ها استفاده می‌کنید، انتخاب گزینه‌ی Include prerelease را فراموش نکنید؛ از این جهت که قصد داریم از نگارش 5 آن‌ها استفاده کنیم. چون این نگارش است که از OpenAPI 3x، پشتیبانی می‌کند. خلاصه‌ی این موارد، افزودن PackageReference زیر به فایل پروژه‌ی OpenAPISwaggerDoc.Web.csproj است و سپس اجرای دستور dotnet restore:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" />
  </ItemGroup>
</Project>


تنظیم میان‌افزار Swashbuckle

پس از افزودن ارجاعی به Swashbuckle.AspNetCore، اکنون نوبت انجام تنظیمات میان‌افزارهای آن است. برای این منظور ابتدا به کلاس Startup و متد ConfigureServices آن مراجعه می‌کنیم:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
    // ...
            services.AddSwaggerGen(setupAction =>
            {
                setupAction.SwaggerDoc(
                   name: "LibraryOpenAPISpecification",
                   info: new Microsoft.OpenApi.Models.OpenApiInfo()
                   {
                       Title = "Library API",
                       Version = "1",
                       Description = "Through this API you can access authors and their books.",
                       Contact = new Microsoft.OpenApi.Models.OpenApiContact()
                       {
                           Email = "name@site.com",
                           Name = "DNT",
                           Url = new Uri("https://www.dntips.ir")
                       },
                       License = new Microsoft.OpenApi.Models.OpenApiLicense()
                       {
                           Name = "MIT License",
                           Url = new Uri("https://opensource.org/licenses/MIT")
                       }
                   });
            });
        }
در اینجا نحوه‌ی تنظیمات ابتدایی سرویس‌های مرتبط با SwaggerGen را ملاحظه می‌کنید. ابتدا نیاز است یک SwaggerDoc به آن اضافه شود که یک name و info را دریافت می‌کند. این name، جزئی از آدرسی است که در نهایت، OpenAPI Specification تولیدی را می‌توان در آنجا یافت. پارامتر Info آن نیز به همراه یک سری مشخصات عمومی درج شده‌ی در مستندات OpenAPI است.

اکنون در متد Configure، میان‌افزار آن‌را خواهیم افزود:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
    // ...
            app.UseHttpsRedirection();

            app.UseSwagger();
    // ...
        }
بهتر است UseSwagger را پس از UseHttpsRedirection درج کرد تا هر نوع درخواست HTTP به آن، به صورت خودکار به HTTPS تبدیل و هدایت شود.

تا اینجا اگر برنامه را اجرا کنید، می‌توان OpenAPI Specification تولیدی را در آدرس زیر یافت:
 https://localhost:5001/swagger/LibraryOpenAPISpecification/swagger.json


در این آدرس، LibraryOpenAPISpecification، همان نامی است که در قسمت setupAction.SwaggerDoc تنظیم کردیم.


نگاهی به OpenAPI Specification تولیدی

در ابتدای swagger.json تولیدی، همانطور که در تصویر فوق نیز مشخص است، همان مشخصات ذکر شده‌ی در قسمت info متد setupAction.SwaggerDoc، قابل مشاهده‌است. سپس لیست مسیرهای این API مشخص شده‌اند:


این‌ها مسیرهایی هستند که توسط دو کنترلر کتاب‌ها و نویسندگان برنامه‌ی Web API ما عمومی شده‌اند. در اینجا مقابل هر مسیر، تعداد آیتم‌های متناظری نیز ذکر شده‌اند. این موارد مرتبط هستند با HTTP methods پشتیبانی شده‌:


که هر کدام به همراه نام متدها و پارامترهای متناظر با آن‌ها نیز می‌شوند. به علاوه نوع responseهای پشتیبانی شده‌ی توسط این متدها نیز ذکر شده‌اند. هر کدام از خروجی‌ها نیز نوع مشخصی دارند که توسط قسمت components -> schemas تصاویر فوق، جزئیات دقیق آن‌ها بر اساس نوع مدل‌های متناظر، استخراج و ارائه شده‌اند.


مشکل: نوع Response تولیدی در OpenAPI Specification صحیح نیست


اگر به جزئیات مسیر /api/authors/{authorId} دقت کنیم، نوع response آن‌را صرفا 200 یا Ok ذکر کرده‌است؛ در حالیکه GetAuthor تعریف شده، حالت NotFound را نیز دارد:
[HttpGet("{authorId}")]
public async Task<ActionResult<Author>> GetAuthor(Guid authorId)
{
    var authorFromRepo = await _authorsService.GetAuthorAsync(authorId);
    if (authorFromRepo == null)
    {
        return NotFound();
    }
    return Ok(_mapper.Map<Author>(authorFromRepo));
}
نمونه‌ی دیگر آن اکشن متد public async Task<ActionResult<Book>> CreateBook است که می‌تواند NotFound یا 404 و یا CreatedAtRoute را که معادل 201 است، بازگشت دهد و در اینجا فقط 200 را ذکر کرده‌است که اشتباه است. بنابراین برای نزدیک کردن این خروجی به اطلاعات واقعی اکشن متدها، نیاز است کار بیشتری انجام شود.


افزودن و راه اندازی Swagger UI

در ادامه می‌خواهیم یک رابط کاربری خودکار را بر اساس OpenAPI Specification تولیدی، ایجاد کنیم:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
    // ...

            app.UseHttpsRedirection();

            app.UseSwagger();
            app.UseSwaggerUI(setupAction =>
            {
                setupAction.SwaggerEndpoint(
                    "/swagger/LibraryOpenAPISpecification/swagger.json",
                    "Library API");
            });

    // ...
        }
برای این منظور میان‌افزار SwaggerUI را پس از UseSwagger، در متد Configure کلاس Startup، تعریف می‌کنیم. در اینجا باید مشخص کنیم که OpenAPI Specification تولید شده، دقیقا در چه آدرسی قرار دارد که روش انجام آن‌را در متد setupAction.SwaggerEndpoint ملاحظه می‌کنید. پارامتر دوم آن یک نام اختیاری است.
پس از این تنظیم اگر آدرس https://localhost:5001/swagger/index.html را در مرورگر باز کنیم، چنین خروجی قابل مشاهده خواهد بود:


و اگر بر روی هر کدام کلیک کنیم، ریز جزئیات آن‌ها بر اساس OpenAPI Specification ای که بررسی کردیم، تولید شده‌است (از پارامترها تا نوع خروجی):


اکنون اگر بر روی دکمه‌ی try it out آن نیز کلیک کنید، در همینجا می‌توان این API را آزمایش کرد. برای مثال Controls Accept header را بر روی application/json قرار داده و سپس بر روی دکمه‌ی execute که پس از کلیک بر روی دکمه‌ی try it out ظاهر شده‌است، کلیک کنید تا بتوان خروجی Web API را مشاهده کرد.

در انتهای این صفحه، در قسمت schemas آن، مشخصات مدل‌های بازگشت داده شده‌ی توسط Web API نیز ذکر شده‌اند:



یک نکته: تغییر آدرس  https://localhost:5001/swagger/index.html به ریشه‌ی سایت

اگر علاقمند باشید تا زمانیکه برای اولین بار آدرس ریشه‌ی سایت را در مسیر https://localhost:5001 باز می‌کنید، Swagger UI نمایان شود، می‌توانید تنظیم RoutePrefix زیر را اضافه کنید:
app.UseSwaggerUI(setupAction =>
            {
                setupAction.SwaggerEndpoint(
                    "/swagger/LibraryOpenAPISpecification/swagger.json",
                    "Library API");
                setupAction.RoutePrefix = "";
            });


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: OpenAPISwaggerDoc-02.zip

در قسمت بعد، به بهبود و غنی سازی جزئیات OpenAPI Specification تولیدی خواهیم پرداخت.
  • #
    ‫۵ سال و ۳ ماه قبل، دوشنبه ۳۰ اردیبهشت ۱۳۹۸، ساعت ۱۴:۱۱
    با سلام. در هنگام ارائه تنظیمات در SwaggerGen به مشکلی برمیخورم که گویا توانایی تبدیل OpenApiInfo را به Swagger.Info ندارد! 


    • #
      ‫۵ سال و ۳ ماه قبل، دوشنبه ۳۰ اردیبهشت ۱۳۹۸، ساعت ۱۵:۰۱
      همانطور که در مطلب هم عنوان شد، از آخرین نگارش موجود آن یعنی 5.0.0rc2 استفاده شده:
      <Project Sdk="Microsoft.NET.Sdk.Web">
        <ItemGroup>
          <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" />
        </ItemGroup>
      </Project>
  • #
    ‫۴ سال و ۳ ماه قبل، یکشنبه ۲۸ اردیبهشت ۱۳۹۹، ساعت ۱۳:۳۱
    با سلام؛ من طبق مستندات پیش رفتم، ولی خط میده. مثل اینکه نمیتونه فایل xml ایجاد کنه. تنظیمات مربوط به قسمت سولوشن پروژه را هم انجام دادم، ولی بازم خطا میده:
    An unhandled exception has occurred while executing the request.
    System.IO.FileNotFoundException: Could not find file
  • #
    ‫۴ سال و ۳ ماه قبل، یکشنبه ۲۸ اردیبهشت ۱۳۹۹، ساعت ۱۴:۱۴
     

     
    • #
      ‫۴ سال و ۳ ماه قبل، یکشنبه ۲۸ اردیبهشت ۱۳۹۹، ساعت ۱۴:۲۵
      - GenerateDocumentationFile را در قسمت بعدی این سری جستجو کنید.
      - همچنین این کدها در انتهای این سری به صورت زیر در آمده‌اند تا تمام اسمبلی‌های موجود را پوشش دهند که البته پروژه‌های مجزای آن‌ها هم باید هر کدام GenerateDocumentationFile خودشان را داشته باشند:
      var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.TopDirectoryOnly).ToList();
      xmlFiles.ForEach(xmlFile => setupAction.IncludeXmlComments(xmlFile));
  • #
    ‫۳ سال و ۱۱ ماه قبل، دوشنبه ۷ مهر ۱۳۹۹، ساعت ۱۱:۴۸
    یک نکته‌ی تکمیلی
    اگر با استفاده از قالب پیش‌فرض تولید Web API در NET 5. پروژه‌ای را ایجاد کنید (dotnet new webapi)، به صورت پیش‌فرض به همراه Swashbuckle.AspNetCore ارائه می‌شود:
    <Project Sdk="Microsoft.NET.Sdk.Web">
      <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
      </ItemGroup>
    </Project>
  • #
    ‫۳ سال و ۹ ماه قبل، دوشنبه ۱۷ آذر ۱۳۹۹، ساعت ۲۱:۳۷
    با سلام؛ قصد اضافه کردن این کتابخانه به پروژه دارم. طبق همین مقاله  پیش رفتم مسیر زیر میشناسه ولی ui را نمایش نمیده و پیدا نمیکنه.

    • #
      ‫۳ سال و ۹ ماه قبل، دوشنبه ۱۷ آذر ۱۳۹۹، ساعت ۲۳:۳۶
      - تقدم و تاخر UseStaticFiles با میان‌افزار آن مهم هست.
      - برای دسترسی به مسیر swagger/index.html نباید setupAction.RoutePrefix مقدار دهی شود ( RoutePrefix را در همین صفحه جستجو کنید).