.NET WEB API 2 에서 사용 하기 위해 Swagger (Swashbuckle)를 파보았다.

발단

API Docs Share에 대한 고민을 하고 있었는데, Swagger가 그나마 제일 괜찮아서 알아봤더니 꽤나 괜찮다는 사실 하지만 이걸 어떻게 공유하지??? 에 대한 생각은 꼬리를 물고 이어졌지만 역시 빠른 포기가 정답 이라는 사실을 발견! 유레카!!

빠른 포기가 정답

전개

일단 ASP.NET WEB API 2 (CORE 아님) 에서 사용하기 위해서 튜토리얼을 클리어 하도록 하자. 친절하게도 XML 파일 만드는 것 까지 나와서 참으로 좋다. 내가 글을 더 써야 함이 줄어들어서 더 좋다.

Tutorial : http://wmpratt.com/swagger-and-asp-net-web-api-part-1/

역시 가장 중요한 것은 Nuget package 를 설치 하는 일!!

PM> Install-Package Swashbuckle  

튜토리얼 페이지의 Enable Swagger to use XML comments 를 보고 XML파일을 만들자.

기본적인 사용법을 익혔으면 열심히 주석을 달면서 API 문서를 만들어야 한다.

사용하다보니 추가 옵션 사항들이 발견되었다.

1. XML 파일의 배포 문제 (Deploy XML)

일반적으로 XML 파일을 Bin\ 에 둔다. 그래서 Deploy (mode: release) 시에는 딸려오지 않는 문제가 발생하길래 XML 파일을 어떻게 하면 같이 가져올 수 있을까? 싶어 고민하다가 해결한 내용은 Build Event 를 활용하는 방안

프로젝트 속성 - Build Event - 빌드 후 이벤트 명령줄 (After build event Command) 를 보면 빌드가 끝난 후 발생하는 이벤트를 정의 할 수 있다.

빌드 후 이벤트 명령줄을 아래와 같이 정의한다. ( *솔루션 폴더의 경로와 파일 이름은 변경 해야만 한다)

if exist "$(SolutionDir)bin\demo.api.xml" (  
    copy "$(SolutionDir)bin\demo.api.xml" "$(ProjectDir)APP_DATA\demo.api.xml"
)

위와 같이 빌드 후 이벤트를 실행하면 APP_DATA\ 폴더에 XML 파일이 존재하게 된다.

  • SwaggerConfig.cs 에서 IncludeXmlComments 를 APP_DATA\ 폴더로 맞춰주자.
public static void Register()  
{
    var thisAssembly = typeof(SwaggerConfig).Assembly;
    GlobalConfiguration.Configuration 
        .EnableSwagger(c =>
        {
             c.IncludeXmlComments(GetXmlCommentsPath());
        })
}

private static string GetXmlCommentsPath()  
{
    return string.Format(@"{0}\APP_DATA\demo.api.xml", System.AppDomain.CurrentDomain.BaseDirectory);
}

이렇게 설정 해두면 XML파일은 지속적으로 APP_DATA\ 폴더에 있기 때문에 해당 폴더&파일을 commit 해서 등록해두면 항상 최신을 유지할 수 있다.

2. Swagger 보안 설정

사실 기본적으로 제공하는 ApiKey 보안 설정 및 OAuth 보안 설정이 있기 때문에 특별히 손댈만한 일은 없지만, 사내에서 사용하는거라 귀찮은 보안 설정은 빼버리고, 사내 IP만 접근 가능 하게 만들고 싶었다.

찾아보니 Swagger 에는 해당 기능이 존재하지 않아서, WEB API 2 Framework Life Cycle의 기반하에 MessageHandler를 조정하는 형식으로 진행하게 되었다.

해당 WEB API 2 프로젝트에 몇 개의 파일을 추가 해야만 한다.

  • SwaggerAccessMessageHandler.cs
public class SwaggerAccessMessageHandler : DelegatingHandler  
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (IsSwagger(request) && !request.AllowIP())
        {
            var response = request.CreateResponse(HttpStatusCode.Unauthorized);
            return Task.FromResult(response);
        }
        else
        {
            return base.SendAsync(request, cancellationToken);
        }
    }

    private bool IsSwagger(HttpRequestMessage request)
    {
        return request.RequestUri.PathAndQuery.StartsWith("/swagger");
    }
}

해당 코드는 잘 살펴보면 DelegatingHanlder를 상속하여 새로운 MessageHandler를 등록하기 위한 코드이다. 이름도 SwaggerAccess 로서 아래 IsSwagger 함수를 보면 보통 편히 사용하는 https://api사이트주소/swagger 형식을 따른다.

  • static bool AllowIP()
public static bool AllowIP(this HttpRequestMessage request)  
{
    var whiteListedIPs = ConfigurationManager.AppSettings["WhiteListIPList"];
    if (!string.IsNullOrEmpty(whiteListedIPs))
    {
        var whiteListIPList = whiteListedIPs.Split(',').ToList();
        var ipAddressString = request.GetIP();
        var ipAddress = IPAddress.Parse(ipAddressString);
        var isInwhiteListIPList =
            whiteListIPList
                .Where(a => a.Trim()
                .Equals(ipAddressString, StringComparison.InvariantCultureIgnoreCase))
                .Any();
        return isInwhiteListIPList;
    }
    return true;
}

추가로 AllowIP() 라는 HttpRequestMessage의 확장 static 메소드를 하나 더 만들어줘야 한다. static 메소드를 추가 할 수 없다면 static class 를 하나 만들어줘야 한다.

  • Web.config 에 WhiteListIPList Key 추가
<appSettings>  
 ...
<add key="WhiteListIPList" value="127.0.0.1, 1.23.456.789, :::1" /> // 여러 개 등록 가능  
 ...
</appSettings>  

Web.config 에는 AllowIP() 에서 사용하는 AccessIP 키를 등록한다.

  • App_Start\WebApiConfig.cs 에 해당 MessageHandler 를 등록한다.
public static void Register(HttpConfiguration config)  
{
    ...
    ...
    config.MessageHandlers.Add(new SwaggerAccessMessageHandler());
}

등록 후 빌드 하고 해당 IP에서만 접근 가능한지 테스트 해보면 다른 곳에서 접근하면 404 Unauthorized 가 나와야 정상이다.

3. Swagger 추가 사용자 헤더 등록

Swashbuckle 에서는 별도의 Header를 사용하려면 IOperationFilter 를 통해서 등록해야만 한다.

아래는 인증(Authorization)헤더를 추가 하는 파일의 코드이다. 해당 파일을 프로젝트에 추가 한 이후에 SwaggerConfig.cs 에 등록하는 형태로 추가 Header 등록이 가능하다.

  • AddAuthorizationHeader.cs
public class AddAuthorizationHeader : IOperationFilter  
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (operation == null) return;

        if (operation.parameters == null)
        {
            operation.parameters = new List<Parameter>();
        }

        var parameter = new Parameter
        {
            description = "The authorization token",
            @in = "header",
            name = "Authorization",
            required = false, // 필수 Header 여부 선택 가능 (T or F)
            type = "string"
        };

        if (apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
        {
            parameter.required = false;
        }

        operation.parameters.Add(parameter);
    }
}
  • SwaggerConfig.cs
GlobalConfiguration.Configuration  
    .EnableSwagger(c =>
    {
         ....
         ....
         c.OperationFilter<AddAuthorizationHeader>(); // add authorization header
         ....
         ....
    })

해당 헤더 등록이 되면 아래와 같이 Authorization 헤더를 추가 할 수 있다. 추가 헤더 등록

결론

API 문서 공유를 외부에 해야 한다면 Swagger는 올바르지 않은 선택이 될 수도 있을 것 같은 느낌이다. (굉장히 주관적인 생각)

하지만!! ASP.NET WEB API 2 에서 문서 만드는데는 Swashbuckle 이 짱임. 두 번 쓰세요. Swagger 사용하면 협업 하는 사람들 입장에서 바로 테스트 가능하니 더 좋은 것 같다.
물론 API 문서를 지속적으로 업데이트 해야 함은 당연히 신경써야 할 부분이고, 사내의 내부 API 공유와 테스트는 Swagger 하나로 끝날 듯 싶다.

Ssemi

Read more posts by this author.