LoginSignup
2
0

More than 3 years have passed since last update.

Akka-HttpとSwaggerを連携させる

Last updated at Posted at 2019-09-07

連携させるのにハマったので備忘録です。

使用するライブラリ

libraryDependencies ++= Seq(
  "com.typesafe.akka"            %% "akka-http"         % "10.1.7",
  "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.0.3",
  "javax.ws.rs"                  % "javax.ws.rs-api"    % "2.0.1",
  "ch.megard"                    %% "akka-http-cors"    % "0.4.1"
)

APIエンドポイントの定義

/itemsでレスポンスを返すAPIを作成します

ItemContoller
package json_server.http.controller

import akka.http.scaladsl.model.{ ContentType, HttpEntity, HttpResponse, MediaTypes, StatusCodes }
import akka.http.scaladsl.server.{ Directives, Route }
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.{ Content, Schema }
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import javax.ws.rs._

@Path("/items")
@Consumes(Array("application/json"))
@Produces(Array("application/json"))
class ItemController extends Directives {

  def route: Route = create

  @GET
  @Operation(
    summary = "Get Items",
    description = "",
    requestBody = new RequestBody(content = Array(new Content(schema = new Schema()))),
    responses = Array(
      new ApiResponse(responseCode = "200",
                      description = "Create response",
                      content = Array(new Content(schema = new Schema()))),
      new ApiResponse(responseCode = "500", description = "Internal server error")
    )
  )
  def create: Route = path("items") {
    get {
      complete(
        HttpResponse(entity = HttpEntity(ContentType(MediaTypes.`application/json`), """
          {"items":"pen"}
          """))
      )
    }
  }
}

SwaggerDocServiceの定義

SwaggerDocService.scala

package json_server.http

import com.github.swagger.akka.SwaggerHttpService
import com.typesafe.config.ConfigFactory

class SwaggerDocService(val apis: Set[Class[_]]) extends SwaggerHttpService {

  val conf = ConfigFactory.load

  val address = conf.getString(MY_HOST)    // 0.0.0.0
  val port    = conf.getInt(MY_PORT)       // 5000

  override val apiClasses          = apis
  override val host                = s"$address:$port"
  override val apiDocsPath         = "api-docs"         // このパスでyaml/jsonを生成する
  override val schemes             = List("https")      // httpsを使いたい場合はhttpsを指定
}

Routeの定義

Routes.scala
package json_server.http

import akka.actor.ActorSystem
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{ ExceptionHandler, Route }
import akka.util.Timeout
import ch.megard.akka.http.cors.scaladsl.CorsDirectives._ 

import json_server.http.controller.ItemControlle

import scala.util.control.NonFatal

class Routes()(implicit system: ActorSystem, timeout: Timeout) extends SprayJsonSupport {

  /***
    * Custom Error Handler
    */
  implicit def customExceptionHandler: ExceptionHandler = ExceptionHandler {
    case NonFatal(ex) =>
      extractLog { implicit log =>
        extractUri { uri =>
          val errorMessage = s"ERROR!! URI: $uri, REASON: ${ex.getMessage}"

          log.error(errorMessage)

          complete(StatusCodes.InternalServerError -> errorMessage)
        }
      }
  }

  /***
    * Routing
    */
  val routes: Route = (cors() | handleExceptions(customExceptionHandler)) { // Route作成時にcors()を呼ばないとSwaggerでExecuteした時にレスポンスの内容が画面に表示されない
    extractLog { implicit log =>
      extractUri { uri =>
        extractMethod { method =>
          log.info(s"call api. method: ${method.value}, uri: $uri")

          path("swagger") {
            getFromResource("swagger/index.html")
          } ~ 
          // swaggerディレクトリ下のファイルにアクセス可能にする
          getFromResourceDirectory("swagger") ~ 
          // APIエンドポイントとなるクラスをSwaggerHttpService.apiClassesに渡す
          new SwaggerDocService(Set(classOf[ItemController])).routes ~ 
          // エンドポイントのルーティングを追加
          new ItemController().route
        }
      }
    }
  }
}

swagger.yamlを生成する

ブラウザで以下のURLにアクセスする。

https://localhost:5000/api-docs/swagger.yaml

すると、以下のyaml形式のテキストがブラウザに表示される。

openapi: 3.0.1
info:
  title: ""
  description: ""
  termsOfService: ""
  version: ""
servers:
- url: https://0.0.0.0:5000
security: []
paths:
  /items:
    get:
      summary: Get Items
      operationId: create
      requestBody:
        content:
          application/json: {}
      responses:
        200:
          description: Create response
          content:
            application/json: {}
        500:
          description: Internal server error
components:
  schemas:
    Function1RequestContextFutureRouteResult:
      type: object

resources下にswagger.yamlを作成し、この内容をコピペする。

Swaggerの実行

ブラウザで以下のURLにアクセスすると、Swagger UIが起動する。

https://localhost:5000/swagger

ペットストアのAPIサービスらしい。
ワークスペース 1_003.png

画面上部のテキストボックスに、ペットストア用のSwaggerリソースhttps://petstore.swagger.io/v2/swagger.jsonが指定されているので、先程作成したswagger.yamlを指定する。
実行するとSwaggerでエンドポイントにアクセスすることができるようになる。
範囲を選択_001.png

毎回swagger.yamlを指定するのが面倒なので、以下のURLで画面を表示すればswagger.yamlを読み込んだ状態でスタートできる。

https://localhost:5000/swagger?https://localhost:5000/swagger.yaml

参考:
akka-httpにswagger2.xを組み込む方法

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0