综合练习
Akka HTTP 迷你 API + Circe JSON。
40 分钟
综合练习
本课整合前 11 课:用 sbt 搭建项目,实现 Pekko HTTP 迷你 API + Circe JSON,编写 ScalaTest,对照 /roadmaps/scala 验收本 track。
学习目标
- 搭建标准 sbt 项目布局
- 实现带 CRUD 的 REST API(内存存储即可)
- 用 Circe 序列化响应,ScalaTest 覆盖核心逻辑
- 对照 Spring Boot 同功能 API 的设计差异
核心概念
项目结构
bookmark-api/
├── build.sbt
├── project/build.properties
└── src/
├── main/scala/
│ ├── Main.scala
│ ├── model.scala
│ ├── store/MemoryStore.scala
│ └── routes/BookmarkRoutes.scala
└── test/scala/
└── BookmarkSuite.scala
main 启动 HTTP;store 放内存实现;routes 放 Pekko HTTP 路由。
REST API 需求
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health | 返回 {"status":"ok"} |
| GET | /api/bookmarks | 列表 |
| POST | /api/bookmarks | 创建 {"url","title"} |
| GET | /api/bookmarks/:id | 详情,404 若不存在 |
| DELETE | /api/bookmarks/:id | 删除 |
核心代码片段
// model.scala
case class Bookmark(id: Long, url: String, title: String)
case class CreateBookmark(url: String, title: String)
// MemoryStore.scala
class MemoryStore:
private var nextId = 1L
private var data = Map.empty[Long, Bookmark]
def list: List[Bookmark] = data.values.toList.sortBy(_.id)
def get(id: Long): Option[Bookmark] = data.get(id)
def create(url: String, title: String): Bookmark =
val b = Bookmark(nextId, url, title)
nextId += 1
data = data + (b.id -> b)
b
def delete(id: Long): Boolean =
if data.contains(id) then data = data - id; true else false
// BookmarkRoutes.scala — POST 解析 Circe JSON,返回 JSON 响应
import io.circe.syntax.*
import io.circe.generic.auto.*
post {
entity(as[String]) { body =>
decode[CreateBookmark](body) match
case Right(req) =>
val b = store.create(req.url, req.title)
complete(StatusCodes.Created, b.asJson.noSpaces)
case Left(err) =>
complete(StatusCodes.BadRequest, err.getMessage)
}
}