OpenApi, Swagger, NSwag

Swagger를 써서 api의 상세스팩을(open api) 만들고 이걸 swagger website를 이용하여 웹화면으로 보게 해준다.

openapi 3.0규격에 맞게 문서를 생성해주므로 아주 편하다.

기본 프로젝트 생성

mkdir ~/Desktop/NSwagSample
cd ~/Desktop/NSwagSample
dotnet new web

package install

dotnet add package NSwag.AspNetCore
dotnet add package NSwag.MSBuild

startup.cs

public void ConfigureServices(IServiceCollection services)
{
  //jwt token을 사용하여 인증을 통과 후 테스트가 가능하게
  services.AddOpenApiDocument(configure =>
  {
    configure.Title = "My API";
    configure.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
    {
      Type = OpenApiSecuritySchemeType.ApiKey,
      Name = "Authorization",
      In = OpenApiSecurityApiKeyLocation.Header,
      Description = "Type into the textbox: Bearer {your JWT token}."
    });

    configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
  });
  ...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  ...

  app.UseOpenApi();

  app.UseSwaggerUi3(options =>
  {
    options.Path = "/swagger";
    options.DocumentPath = "/swagger/v1/swagger.json";
  });

  app.UseReDoc(options =>
  {
    options.Path = "/redoc";
    options.DocumentPath = "/swagger/v1/swagger.json";
  });
  ...
}

이제 프로젝트를 실행후 웹사이트를 확인해보자.

  • https://localhost:5001/swagger
  • https://localhost:5001/recoc

openapi오 redoc스타일 둘다 볼수 있다.

project.csproj 수정

<Target Name="NSwag" AfterTargets="Build" Condition="$(Configuration)== 'Debug'">
  <Exec Command="$(NSwagExe_Net50) run nswag.json /variables:Configuration=$(Configuration)" />
</Target>

NSwagExe_Net50 : 이부분은 각자 알아서 바꾼다. github에 가보면 적당한 내용이 다 있다.

debug일때만 코드 자동생성 하려고 Condition="$(Configuration)== 'Debug' 이부분 추가

위 내용은 보면 빌드후 커맨드를 하나 실행하는데 nswag.json 을 참고함을 알수 있다.

csproj파일에 같은 위치에 파일을 하나 만들자.

vi nswag.json

{
  "runtime": "Net50",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": "WebAPI.csproj",
      "msBuildProjectExtensionsPath": null,
      "configuration": null,
      "runtime": null,
      "targetFramework": null,
      "noBuild": true,
      "verbose": false,
      "workingDirectory": null,
      "requireParametersWithoutDefault": true,
      "apiGroupNames": null,
      "defaultPropertyNameHandling": "CamelCase",
      "defaultReferenceTypeNullHandling": "Null",
      "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
      "defaultResponseReferenceTypeNullHandling": "NotNull",
      "defaultEnumHandling": "Integer",
      "flattenInheritanceHierarchy": false,
      "generateKnownTypes": true,
      "generateEnumMappingDescription": false,
      "generateXmlObjects": false,
      "generateAbstractProperties": false,
      "generateAbstractSchemas": true,
      "ignoreObsoleteProperties": false,
      "allowReferencesWithProperties": false,
      "excludedTypeNames": [],
      "serviceHost": null,
      "serviceBasePath": null,
      "serviceSchemes": [],
      "infoTitle": "WEB API",
      "infoDescription": null,
      "infoVersion": "1.0.0",
      "documentTemplate": null,
      "documentProcessorTypes": [],
      "operationProcessorTypes": [],
      "typeNameGeneratorType": null,
      "schemaNameGeneratorType": null,
      "contractResolverType": null,
      "serializerSettingsType": null,
      "useDocumentProvider": true,
      "documentName": "v1",
      "aspNetCoreEnvironment": null,
      "createWebHostBuilderMethod": null,
      "startupType": null,
      "allowNullableBodyParameters": true,
      "output": "wwwroot/api/specification.json",
      "outputType": "OpenApi3",
      "assemblyPaths": [],
      "assemblyConfig": null,
      "referencePaths": [],
      "useNuGetCache": false
    }
  },
  "codeGenerators": {
    "openApiToTypeScriptClient": {
      "className": "{controller}Client",
      "moduleName": "",
      "namespace": "",
      "typeScriptVersion": 2.7,
      "template": "Angular",
      "promiseType": "Promise",
      "httpClass": "HttpClient",
      "withCredentials": false,
      "useSingletonProvider": true,
      "injectionTokenType": "InjectionToken",
      "rxJsVersion": 6.0,
      "dateTimeType": "Date",
      "nullValue": "Undefined",
      "generateClientClasses": true,
      "generateClientInterfaces": true,
      "generateOptionalParameters": false,
      "exportTypes": true,
      "wrapDtoExceptions": false,
      "exceptionClass": "SwaggerException",
      "clientBaseClass": null,
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "protectedMethods": [],
      "configurationClass": null,
      "useTransformOptionsMethod": false,
      "useTransformResultMethod": false,
      "generateDtoTypes": true,
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "markOptionalProperties": true,
      "generateCloneMethod": false,
      "typeStyle": "Class",
      "classTypes": [],
      "extendedClasses": [],
      "extensionCode": null,
      "generateDefaultValues": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateConstructorInterface": true,
      "convertConstructorInterfaceData": false,
      "importRequiredTypes": true,
      "useGetBaseUrlMethod": false,
      "baseUrlTokenName": "API_BASE_URL",
      "queryNullValue": "",
      "inlineNamedDictionaries": false,
      "inlineNamedAny": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "../../../rendercore-cloud-www/src/app/shared/web-api-client.ts"
    }
  }
}

“output”: “wwwroot/api/specification.json” : 이부분이 spec을 만들어줘서 이 경로에 json을 저장한다.

“output”: “../../../frontend/src/app/shared/web-api-client.ts” : 이부분은 위 json을 이용하여 클라이언트 코드를 자동 생성해준 클라이언트 파일이다. 나의 경우는 프론트앤드에 shared폴더에 복사를 자동으로 하게 해두었다.

위 설정 파일은 nswagstudio라는 윈도우 프로그램을 설치하면 만들수 있고 https://github.com/RicoSuter/NSwag/wiki/NSwagStudio 또는 이걸 만들어주는 웹사이트도 있다고한다.

클라이언트코드는 c#용 type script용 등으로 만들수 있다.

swagger웹사이트에서 자기에게 필요한 변환기를 찾아볼수도 있다.

이제 프론트에서는 생성된 클라를 사용하기만 하면된다.

이제 빌드하면 문서도 자동으로 생기고 클라이언트 파일도 생성이 되서 작업하기는 편하게 됬다.

장단점

  • 생성된 코드가 편하게 이해할수 없다.
  • 매번 api가 바뀔때마다 재 생성되므로 관련 내용을 프론트에서 수정하지 않아도 되니 편하다.
  • 커스터마이즈가 잘 안된다.
  • 개인프로젝트에 한번 써보려고 햇는데 생성된 코드는 잘 안되는걸 확인했다.
  • dotnet 5 부터 connect service라는 기능으로 이 클라이언트를 이용하여 코딩하는 부분이 쉬워졌다.
  • 클라이언트를 그냥 쓸수 있을 정도의 프로젝트라면 상당한 속도의 코딩을 해나갈수 있을듯 하다.
  • 문서화가 잘되기때문에 프론트 작업자와 커뮤니케이션을 줄일수 있다.
teamsmiley's profile image

teamsmiley

2021-03-29 00:00

Read more posts by this author