yamamoWorks

.NET技術を中心に気まぐれに更新していきます

ASP.NET Core Web APIでSnake Caseに対応する - Swagger編

前回でアクションメソッドの引数の変数名はそのままに、クエリパラメータを「snake_case」でリクエストして動作させる事はできました。 昨今ではAPIを作ったらSwaggerで仕様を公開する場面が多いでしょう。 ASP.NET Core Web APIではSwashbuckleを使って簡単にSwaggerの出力やWebページが作成できます。 [参考]:Swashbuckle と ASP.NET Core の概要

今回はSwaggerにも「snake_case」が適応されるようにするのがゴールです。

【追記】
次の記事 ASP.NET Core Web APIでSnake Caseに対応する - ApiExplorer編 - yamamoWorks で紹介している方式の方が良いでしょう。

前提

こんなWeb APIがあったとします。

[ApiController]
public class UsersController : ControllerBase
{
    [HttpGet]
    public ActionResult<User[]> List([FromQuery]string firstName)
    {
        var users = new[] {
            new User { UserId = "001", FirstName = "Taro", LastName = "Yamada" },
            new User { UserId = "002", FirstName = "Hanako", LastName = "Yamada" }
        };
        return Ok(users.Where(u => firstName == null ? true : u.FirstName == firstName));
    }
}

前回の対応を実施済みなので「/api/users/?first_name=Taro」でリクエストできます。

ここでSwashbuckleを有効にしてWebページを見てみるとパラメータは「firstName」のままです。

f:id:yamamoWorks:20190429042747p:plain

Swaggerのクエリパラメータに「snake_case」を適応する

SwashbuckleではIParameterFilterインターフェイスでSwaggerドキュメントのパラメータ部分をカスタマイズできます。 StartupクラスのConfigureServicesメソッド内のAddSwaggerGenメソッドを呼ぶ際にSnakeCaseParameterFilterを登録します。

services.AddSwaggerGen(options =>
{
    options.ParameterFilter<SnakeCaseParameterFilter>();
    // etc
});

これでパラメータが「first_name」に変わりました。 f:id:yamamoWorks:20190429044641p:plain

Swaggerのパスに「snake_case」を適応する

実はParameterFilterの適応だけでは問題が残ります。 こんなWeb APIがあったとして

[HttpGet("{userId}")]
public ActionResult<User> Get(string userId, bool hideAge)
{
    return Ok(new User { UserId = userId, Age = hideAge ? -1 : 40, FirstName = "Taro", LastName = "Yamada" });
}

Swaggerを見てみると、パスの構成要素である「userId」にも「snake_case」が適応され「user_id」になっています。 パスの構成要素なのでリクエストの際には名前は関係なく「/api/users/001?hide_age=true」でリクエストすれば正常に動作します。

f:id:yamamoWorks:20190429045838p:plain

但し、一番上のGETの横にあるパスが「/api/users/{userId}」のままであり、SwaggerのAPI仕様としては不整合な状態です。 これにはIDocumentFilterインターフェイスで対応します。(無理矢理感があるのですが…)

これもStartupクラスのConfigureServicesメソッド内のAddSwaggerGenメソッドを呼ぶ際に登録します。

services.AddSwaggerGen(options =>
{
    options.ParameterFilter<SnakeCaseParameterFilter>();
    options.DocumentFilter<SnakeCaseDocumentFilter>();
    // etc
});

f:id:yamamoWorks:20190429051939p:plain

これでアプリ本体を変えることなく「snake_case」に対応する事ができました。