路由与 HttpRoutes
以函数式方式定义端点。
路由与 HttpRoutes 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
What http4s Is
http4s is a purely functional HTTP library for Scala, built on cats-effect and fs2 streams. Requests and responses are immutable values, and effects are captured in a polymorphic effect type F[_] such as IO.
Instead of mutating a servlet, you describe an HTTP service as a function from a request to an effectful optional response.
The HttpRoutes Type
The core abstraction is HttpRoutes[F], an alias for Kleisli[OptionT[F, *], Request[F], Response[F]]. The OptionT models a route that may not match, returning no response.
You rarely write that signature by hand. Instead you build routes with the HttpRoutes.of constructor and a partial function.
import cats.effect.IO
import org.http4s._
import org.http4s.dsl.io._
val routes: HttpRoutes[IO] = HttpRoutes.of[IO] {
case GET -> Root / "hello" => Ok("hi")
}The http4s DSL
The DSL import org.http4s.dsl.io._ brings in pattern extractors like GET, Root, and the path separator /, plus response builders like Ok and NotFound.
A route is a PartialFunction[Request[F], F[Response[F]]]. Cases that do not match simply fall through.
import org.http4s.dsl.io._
HttpRoutes.of[IO] {
case GET -> Root => Ok("root")
case GET -> Root / "ping" => Ok("pong")
}Matching Paths
Path patterns read left to right. Root is the leading slash, and each / "segment" matches one literal path segment.
The arrow -> separates the HTTP method from the path. So GET -> Root / "users" matches GET /users.
HttpRoutes.of[IO] {
case GET -> Root / "users" => Ok("all users")
case GET -> Root / "users" / "me" => Ok("current user")
}Path Variables
A bare lowercase binder in a path captures that segment as a String. Here id binds whatever appears after /users/.
Captured segments are always strings; you parse them to richer types yourself or use extractor objects shown next.
HttpRoutes.of[IO] {
case GET -> Root / "users" / id =>
Ok(s"user $id")
}Typed Path Extractors
http4s ships extractors such as IntVar and LongVar that match only when the segment parses to that type. A non-numeric segment makes the case fall through to the next.
You can define custom extractors with an unapply for domain types like UUIDs.
HttpRoutes.of[IO] {
case GET -> Root / "users" / IntVar(id) =>
Ok(s"numeric user $id")
}Method Matching
The method extractor before -> can be any of GET, POST, PUT, DELETE, PATCH, and more. The same path with different methods becomes separate cases.
If a path matches but the method does not, http4s automatically responds 405 Method Not Allowed.
HttpRoutes.of[IO] {
case GET -> Root / "items" => Ok("list")
case POST -> Root / "items" => Created("made")
case DELETE -> Root / "items" / IntVar(i) => NoContent()
}Combining Routes
Because HttpRoutes[F] forms a SemigroupK, you compose multiple route groups with the <+> operator. The first group that produces a response wins.
This lets you split routes by feature into separate values and merge them in one place.
import cats.syntax.semigroupk._
val all = userRoutes <+> itemRoutes <+> healthRoutesRoutes to HttpApp
A server needs a total function, not a partial one. HttpRoutes[F] is converted to HttpApp[F] with .orNotFound, which supplies a 404 when no route matches.
HttpApp[F] is Kleisli[F, Request[F], Response[F]] — always returning a response.
import org.http4s.HttpApp
val app: HttpApp[IO] = routes.orNotFoundMiddleware Wrapping
Middleware are functions HttpRoutes[F] => HttpRoutes[F] (or over HttpApp). They wrap a service to add logging, CORS, gzip, or auth without touching route logic.
Built-in examples include Logger, CORS, and GZip from org.http4s.server.middleware.
import org.http4s.server.middleware.Logger
val logged = Logger.httpApp(logHeaders = true, logBody = false)(app)Query Parameters in Routes
Query parameters are matched with matcher objects extending QueryParamDecoderMatcher. They appear after a :? in the path pattern.
Use OptionalQueryParamDecoderMatcher for parameters that may be absent, yielding an Option.
object NameParam extends QueryParamDecoderMatcher[String]("name")
HttpRoutes.of[IO] {
case GET -> Root / "hi" :? NameParam(n) => Ok(s"hi $n")
}Quick Check
Test your understanding of route composition.
Recap
You built routes with HttpRoutes.of and the DSL, matched methods and paths, captured variables with IntVar and matchers, and composed groups with <+>.
Routes are partial; .orNotFound makes them total HttpApp, and middleware wraps services for cross-cutting concerns.
常见问题解答
「路由与 HttpRoutes」课时是免费的吗?
是的 — 「路由与 HttpRoutes」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
「路由与 HttpRoutes」这节课中我会学到什么?
以函数式方式定义端点。 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「路由与 HttpRoutes」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。