scala akka http基本使用

scala的akka框架有一个极简的http service组件,是把原来spray框架集成到akka里面修改而成,本文简单介绍了akka http 的使用,在一些简单web service项目或者将web作为一个简单模块的后台项目中非常适合。

sbt配置

name := "MockServer"

version := "0.1"

scalaVersion := "2.12.3"

libraryDependencies ++= Seq(
    "com.typesafe.akka" %% "akka-http" % "10.0.10",
    "com.typesafe.akka" %% "akka-stream" % "2.5.4",
    "com.typesafe.akka" %% "akka-actor"  % "2.5.4",
    "com.typesafe.akka" %% "akka-http-spray-json" % "10.0.10",
    "ch.megard" %% "akka-http-cors" % "0.2.2"
)

code

httpserver

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import akka.Done
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.headers.HttpOriginRange
import ch.megard.akka.http.cors.scaladsl.CorsDirectives.cors
import ch.megard.akka.http.cors.scaladsl.settings.CorsSettings
import spray.json.DefaultJsonProtocol._

import scala.io.StdIn
import scala.collection.mutable

object MockServer
{
    // cors setting for other origin access
    val settings = CorsSettings.defaultSettings.copy(
        allowedOrigins = HttpOriginRange.* // * refers to all
    )

    // model
    final case class User(name: String, age: Int, addr: String)
    implicit val itemFormat = jsonFormat3(User) // jsonFormatX refers to X number arguments

    final case class UserGroup(items: List[User])
    implicit val orderFormat = jsonFormat1(UserGroup)

    // object variable
    private val userGroup = mutable.ListBuffer[User]()

    def main(args: Array[String]): Unit =
    {
        implicit val system = ActorSystem("mock_system")
        implicit val materializer = ActorMaterializer()

        // needed for the future flatMap/onComplete in the end
        implicit val executionContext = system.dispatcher

        // define routes, notice there are many ways
        val route: Route =
            (path("hello") & get & cors(settings))
            {
                complete("hello akka")

            } ~
            (path("list_all") & cors(settings))
            {
                // simple get
                get
                {
                    // add elem to userGroup, can use this.userGroup += User("jack", 18, "NewYork")
                    userGroup.clear()
                    userGroup += User("jack", 18, "NewYork")
                    userGroup += User("mike", 21, "paris")
                    val user_group = UserGroup(this.userGroup.toList)
                    complete(user_group)
                }
            } ~
            get
            {
                // get by params using akka http path matcher
                (pathPrefix("user" / IntNumber ) & cors(settings))
                {
                    age =>
                    {
                        val user = User("lucy", age.toInt, "tokyo")
                        complete(user)
                    }
                }
            } ~
            post {
                (path("create_user") & cors(settings))
                {
                    entity(as[User])
                    {
                        user =>
                        {
                            println(user)
                            // do sth to process
                            userGroup += user
                            println(userGroup)

                            complete("done")
                        }
                    }
                }
            }


        // bind to ip and port and start server
        val bindingFuture = Http().bindAndHandle(route, "localhost", 7070)

        println(s"Server online at http://localhost:7070/\nPress RETURN to stop...")
        StdIn.readLine() // let it run until user presses return
//        while (1) // or use a dead loop

        // stop server
        bindingFuture
            .flatMap(_.unbind()) // trigger unbinding from the port
            .onComplete(_ => system.terminate()) // and shutdown when done
    }

}

其中,

  • get和post的内容需要写在route里面
  • 如果想要传入传出自定义的数据结构,需要用json序列化和反序列化
  • 为了支持跨域访问,加入了cors的setting
  • 如果要支持https,请参考官网文档

httpclient

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer

import scala.concurrent.Future
import scala.util.{Failure, Success}


object WebClient extends App
{
    implicit val system = ActorSystem()
    implicit val materializer = ActorMaterializer()
    // needed for the future flatMap/onComplete in the end
    implicit val executionContext = system.dispatcher

    val host_url = "http://www.easy-mock.com/mock/59ed7cef591f361bb0d95ad8" // this is a mock service
    val get_api = "user"
    val post_api = "login"
    val post_username_param = "mike"
    val post_password_param = "ethhvhe35"

    // url can be rest or non-rest
    def getReq(url: String): Unit =
    {
        val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = url, method = HttpMethods.GET))
        responseFuture.onComplete {
                case Success(res) => {
                    println(res)
                    Unmarshal(res.entity).to[String].map { json_str =>
                        Right {
                            // here we can deserialize json string to object or list of object
                            // sth like val res_obj = Json.deserialize([Model])(json_str)
                            // or val res_obj = Json.deserialize(Seq[Model])(json_str)

                            println("get result: ", json_str)
                        }
                    }
                }
                case Failure(error) => println(error.getMessage)
            }
    }

    def postReq(url: String): Unit =
    {
        val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = url, method = HttpMethods.POST))
        responseFuture.onComplete {
            case Success(res) => {
                println(res)
                Unmarshal(res.entity).to[String].map { json_str =>
                    Right {
                        // here we can deserialize json string to object or list of object
                        // sth like val res_obj = Json.deserialize([Model])(json_str)
                        // or val res_obj = Json.deserialize(Seq[Model])(json_str)

                        println("post result: ", json_str)
                    }
                }
            }
            case Failure(error) => println(error.getMessage)
        }
    }

    // test get
    getReq(host_url + "/" + get_api)

    // test post
    postReq(s"$host_url/$post_api?username=$post_username_param&password=$post_password_param")
}

其中,

  • 可以将entity解析成json字符串,然后将json字符串绑定到自定义的class获得数据
  • Future传出到外面就失效了,必须带context
  • 也可以写成actor的形式,请参考官网文档

download

github: akka_http_demo

猜你喜欢

转载自blog.csdn.net/u012234115/article/details/78719313