diff --git a/index.html b/index.html index 11bc9044d8..7ba292159e 100644 --- a/index.html +++ b/index.html @@ -114,7 +114,7 @@ .Styles-subtleLink{ text-decoration: none; } -
package app
object MinimalApplication extends cask.MainRoutes{
@cask.get("/")
@@ -132,7 +132,7 @@
Cask is a simple Scala web framework inspired by Python's Flask project. It aims to bring simplicity, flexibility and ease-of-use to Scala webservers, avoiding cryptic DSLs or complicated asynchrony.
The easiest way to begin using Cask is by downloading the example project above.
@@ -193,7 +193,7 @@The rough outline of how the minimal example works should be easy to understand:
You can split up your routes into separate cask.Routes
objects as makes sense and pass them all into cask.Main
.
package app
@@ -254,7 +254,7 @@
You can bind path segments to endpoint parameters by declaring them as parameters. These are either:
You can bind query parameters to your endpoint method via parameters of the form:
package app
@@ -399,7 +399,7 @@
If you need to handle a JSON-encoded POST request, you can use the @cask.postJson
decorator. This assumes the posted request body is a JSON dict, and uses its keys to populate the endpoint's parameters, either as raw ujson.Value
s or deserialized into Seq[Int]
s or other things. Deserialization is handled using the uPickle JSON library, though you could write your own version of postJson
to work with any other JSON library of your choice.
Similarly, you can mark endpoints as @cask.postForm
, in which case the endpoints params will be taken from the form-encoded POST body either raw (as cask.FormValue
s) or deserialized into simple data structures. Use cask.FormFile
if you want the given form value to be a file upload.
Cookies are most easily read by declaring a : cask.Cookie
parameter; the parameter name is used to fetch the cookie you are interested in. Cookies can be stored by setting the cookie
attribute in the response, and deleted simply by setting expires = java.time.Instant.EPOCH
(i.e. to have expired a long time ago)
package app
@@ -457,7 +457,7 @@
You can ask Cask to serve static files by defining a @cask.staticFiles
endpoint. This will match any subpath of the value returned by the endpoint (e.g. above /static/file.txt
, /static/folder/file.txt
, etc.) and return the file contents from the corresponding file on disk (and 404 otherwise).
Similarly, @cask.staticResources
attempts to serve a request based on the JVM resource path, returning the data if a resource is present and a 404 otherwise.
package app
object RedirectAbort extends cask.MainRoutes{
@@ -503,7 +503,7 @@
Cask provides some convenient helpers cask.Redirect
and cask.Abort
which you can return; these are simple wrappers around cask.Request
, and simply set up the relevant headers or status code for you.
Cask doesn't come bundled with HTML templating functionality, but it makes it really easy to use community-standard libraries like Scalatags to render your HTML. Simply adding the relevant ivy"com.lihaoyi::scalatags:0.9.1"
dependency to your build.sc
file is enough to render Scalatags templates:
If you prefer to use the Twirl templating engine, you can use that too:
package app
@@ -542,7 +542,7 @@
With the following app/views/hello.scala.html
:
@(titleTxt: String)
@@ -605,7 +605,7 @@
You can write extra decorator annotations that stack on top of the existing @cask.get
/@cask.post
to provide additional arguments or validation. This is done by implementing the cask.Decorator
interface and it's getRawParams
function. getRawParams
:
This is convenient for cases where you want a set of decorators to apply broadly across your web application, and do not want to repeat them over and over at every single endpoint.
package app
@@ -696,7 +696,7 @@
When you need more flexibility than decorators allow, you can define your own custom cask.Endpoint
s to replace the default set that Cask provides. This allows you to
Cask provides a useful @cask.decorators.compress
decorator that gzips or deflates a response body if possible. This is useful if you don't have a proxy like Nginx or similar in front of your server to perform the compression for you.
Like all decorators, @cask.decorators.compress
can be defined on a level of a set of cask.Routes
:
Or globally, in your cask.Main
:
package app
@@ -770,7 +770,7 @@
}
package app
@@ -792,7 +792,7 @@
Cask's Websocket endpoints are very similar to Cask's HTTP endpoints. Annotated with @cask.websocket
instead of @cask.get
or @cask.post
, the primary difference is that instead of only returning a cask.Response
, you now have an option of returning a cask.WsHandler
.
The cask.WsHandler
allows you to pro-actively start sending websocket messages once a connection has been made, via the channel: WsChannelActor
it exposes, and lets you react to messages via the cask.WsActor
you create. You can use these two APIs to perform full bi-directional, asynchronous communications, as websockets are intended to be used for. Note that all messages received on a each individual Websocket connection by your cask.WsActor
are handled in a single-threaded fashion by default: this means you can work with local mutable state in your @cask.websocket
endpoint without worrying about race conditions or multithreading. If you want further parallelism, you can explicitly spin off scala.concurrent.Future
s or other cask.BatchActor
s to perform that parallel processing.
It leaves it up to you to manage open channels, react to incoming messages, or pro-actively send them out, mostly using the underlying Undertow webserver interface. While Cask does not model streams, backpressure, iteratees, or provide any higher level API, it should not be difficult to take the Cask API and build whatever higher-level abstractions you prefer to use.
If you are separating your cask.Routes
from your cask.Main
, you need to inject in a cask.Logger
to handle errors reported when handling websocket requests:
package app
object TodoMvcApi extends cask.MainRoutes{
@@ -902,7 +902,7 @@
This is a simple self-contained example of using Cask to write an in-memory API server for the common TodoMVC example app.
This minimal example intentionally does not contain javascript, HTML, styles, etc.. Those can be managed via the normal mechanism for Serving Static Files.
This example demonstrates how to use Cask to write a TodoMVC API server that persists it's state in a database rather than in memory. We use the ScalaSql database access library to write a @transactional
decorator that automatically opens one transaction per call to an endpoint, ensuring that database queries are properly committed on success or rolled-back on error. Note that because the default database connector propagates its transaction context in a thread-local, @transactional
does not need to pass the ctx
object into each endpoint as an additional parameter list, and so we simply leave it out.
While this example is specific to ScalaSql, you can easily modify the @transactional
decorator to make it work with whatever database access library you happen to be using. For libraries which need an implicit transaction, it can be passed into each endpoint function as an additional parameter list as described in Extending Endpoints with Decorators. work with whatever database access library you happen to be using. For libraries which need an implicit transaction, it can be passed into each endpoint function as an additional parameter list as described in Extending Endpoints with Decorators.
About the Author: Haoyi is a software engineer, an early contributor to Scala.js, and the author of many open-source Scala tools such as Cask, the Ammonite REPL and FastParse.
If you've enjoy using Cask, or enjoyed using Haoyi's other open source libraries, please chip in (or get your Company to chip in!) via Patreon so he can continue his open-source work