app.get(“/hello/{name}”, ctx -> { // the {} syntax does not allow slashes (‘/‘) as part of the parameter ctx.result(“Hello: “ + ctx.pathParam(“name”)); }); app.get(“/hello/“, ctx -> { // the <> syntax allows slashes (‘/‘) as part of the parameter ctx.result(“Hello: “ + ctx.pathParam(“name”)); }); 处理程序路径也可以包含通配符参数:
app.get(“/path/*”, ctx -> { // will match anything starting with /path/ ctx.result(“You are here because “ + ctx.path() + “ matches “ + ctx.matchedPath()); }); 然而,无法提取通配符的值。如果需要这种行为,可以使用接受 path-parameter ( 而不是 {param-name}) 的斜杠。
处理程序之后 后处理程序在每个请求之后运行 (即使发生了异常)
你可能知道后处理程序是指来自其他库的过滤器、拦截器或中间件。 app.after(ctx -> { // run after all requests }); app.after(“/path/*”, ctx -> { // runs after request to /path/* }); 在某些情况下,你可能只想在请求匹配的情况下运行后处理程序 (而不是 404)。在这种情况下,你可以使用 app.afterMatched 方法:
// Request methods body() // request body as string bodyAsBytes() // request body as array of bytes bodyAsClass(clazz) // request body as specified class (deserialized from JSON) bodyStreamAsClass(clazz) // request body as specified class (memory optimized version of above) bodyValidator(clazz) // request body as validator typed as specified class bodyInputStream() // the underyling input stream of the request uploadedFile(“name”) // uploaded file by name uploadedFiles(“name”) // all uploaded files by name uploadedFiles() // all uploaded files as list uploadedFileMap() // all uploaded files as a “names by files” map formParam(“name”) // form parameter by name, as string formParamAsClass(“name”, clazz) // form parameter by name, as validator typed as specified class formParams(“name”) // list of form parameters by name formParamMap() // map of all form parameters pathParam(“name”) // path parameter by name as string pathParamAsClass(“name”, clazz) // path parameter as validator typed as specified class pathParamMap() // map of all path parameters basicAuthCredentials() // basic auth credentials (or null if not set) attribute(“name”, value) // set an attribute on the request attribute(“name”) // get an attribute on the request attributeOrCompute(“name”, ctx -> {}) // get an attribute or compute it based on the context if absent attributeMap() // map of all attributes on the request contentLength() // content length of the request body contentType() // request content type cookie(“name”) // request cookie by name cookieMap() // map of all request cookies header(“name”) // request header by name (can be used with Header.HEADERNAME) headerAsClass(“name”, clazz) // request header by name, as validator typed as specified class headerMap() // map of all request headers host() // host as string ip() // ip as string isMultipart() // true if the request is multipart isMultipartFormData() // true if the request is multipart/formdata method() // request methods (GET, POST, etc) path() // request path port() // request port protocol() // request protocol queryParam(“name”) // query param by name as string queryParamAsClass(“name”, clazz) // query param by name, as validator typed as specified class queryParamsAsClass(“name”, clazz) // query param list by name, as validator typed as list of specified class queryParams(“name”) // list of query parameters by name queryParamMap() // map of all query parameters queryString() // full query string scheme() // request scheme sessionAttribute(“name”, value) // set a session attribute sessionAttribute(“name”) // get a session attribute consumeSessionAttribute(“name”) // get a session attribute, and set value to null cachedSessionAttribute(“name”, value) // set a session attribute, and cache the value as a request attribute cachedSessionAttribute(“name”) // get a session attribute, and cache the value as a request attribute cachedSessionAttributeOrCompute(…) // same as above, but compute and set if value is absent sessionAttributeMap() // map of all session attributes url() // request url fullUrl() // request url + query string contextPath() // request context path userAgent() // request user agent req() // get the underlying HttpServletRequest
// Response methods result(“result”) // set result stream to specified string (overwrites any previously set result) result(byteArray) // set result stream to specified byte array (overwrites any previously set result) result(inputStream) // set result stream to specified input stream (overwrites any previously set result) future(futureSupplier) // set the result to be a future, see async section (overwrites any previously set result) writeSeekableStream(inputStream) // write content immediately as seekable stream (useful for audio and video) result() // get current result stream as string (if possible), and reset result stream resultInputStream() // get current result stream contentType(“type”) // set the response content type header(“name”, “value”) // set response header by name (can be used with Header.HEADERNAME) redirect(“/path”, code) // redirect to the given path with the given status code status(code) // set the response status code status() // get the response status code cookie(“name”, “value”, maxAge) // set response cookie by name, with value and max-age (optional). cookie(cookie) // set cookie using javalin Cookie class removeCookie(“name”, “/path”) // removes cookie by name and path (optional) json(obj) // calls result(jsonString), and also sets content type to json jsonStream(obj) // calls result(jsonStream), and also sets content type to json html(“html”) // calls result(string), and also sets content type to html render(“/template.tmpl”, model) // calls html(renderedTemplate) res() // get the underlying HttpServletResponse
// Other methods async(runnable) // lifts request out of Jetty’s ThreadPool, and moves it to Javalin’s AsyncThreadPool async(asyncConfig, runnable) // same as above, but with additonal config handlerType() // handler type of the current handler (BEFORE, AFTER, GET, etc) appData(typedKey) // get data from the Javalin instance (see app data section below) with(pluginClass) // get context plugin by class, see plugin section below matchedPath() // get the path that was used to match this request (ex, “/hello/{name}”) endpointHandlerPath() // get the path of the endpoint handler that was used to match this request cookieStore() // see cookie store section below skipRemainingHandlers() // skip all remaining handlers for this request 应用数据 应用数据可以通过 Javalin#create 在 Javalin 实例上注册,然后通过 Context 中的 appData (…) 方法访问。你需要为数据创建一个类型化键,然后将其注册到 Javalin 实例。
// register a custom attribute var myKey = new Key(“my-key”); var app = Javalin.create(config -> { config.appData(myKey, myValue); }); // access a custom attribute var myValue = ctx.appData(myKey); // var will be inferred to MyValue // call a custom method on a custom attribute ctx.appData(myKey).myMethod(); 将键存储为静态字段可能很有帮助,但你也可以在每次需要时重新创建该键。
Cookie Store CookieStore 类为处理程序、请求甚至服务器之间共享信息提供了一种便捷的方式:
ctx.cookieStore().set(key, value); // store any type of value ctx.cookieStore().get(key); // read any type of value ctx.cookieStore().clear(); // clear the cookie-store CookieStore 的工作原理如下:
第一个与传入请求匹配的处理程序将用当前存储在 cookie 中的数据 (如果有的话) 填充 cookie-store-map。 这个映射现在可以用作同一请求周期中处理程序之间的状态,方式与 ctx.attribute () 非常相似。 在请求周期结束时,cookie-store-map 被序列化、base64 编码,并以 cookie 的形式写入响应。这允许您在请求和服务器之间共享该 map (以防您在负载均衡器后面运行多个服务器)。 例如: serverOneApp.post(“/cookie-storer”, ctx -> { ctx.cookieStore().set(“string”, “Hello world!”); ctx.cookieStore().set(“i”, 42); ctx.cookieStore().set(“list”, Arrays.asList(“One”, “Two”, “Three”)); }); serverTwoApp.get(“/cookie-reader”, ctx -> { // runs on a different server than serverOneApp String string = ctx.cookieStore().get(“string”) int i = ctx.cookieStore().get(“i”) List list = ctx.cookieStore().get(“list”) }); 由于客户端存储了 cookie, 因此对 serverTwoApp 的 get 请求将能够检索在发帖中传递给 serverOneApp 的信息。
app.wsBefore(ws -> { // runs before all WebSocket requests }); app.wsBefore(“/path/*”, ws -> { // runs before websocket requests to /path/* }); WsEndpoint 使用 app.wsBefore (path,handler) 声明 WebSocket 终结点。WebSocket 处理程序需要唯一的路径。
app.ws(“/websocket/{path}”, ws -> { ws.onConnect(ctx -> System.out.println(“Connected”)); ws.onMessage(ctx -> { User user = ctx.messageAsClass(User.class); // convert from json ctx.send(user); // convert to json and send back }); ws.onBinaryMessage(ctx -> System.out.println(“Message”)) ws.onClose(ctx -> System.out.println(“Closed”)); ws.onError(ctx -> System.out.println(“Errored”)); }); WsAfter App.wsAfter 添加了一个在 WebSocket 处理程序之后运行的处理程序。你可以为每个 WebSocket 端点设置任意数量的后续处理程序,并且支持所有事件。
app.wsAfter(ws -> { // runs after all WebSocket requests }); app.wsAfter(“/path/*”, ws -> { // runs after websocket requests to /path/* }); WsContext WsContext 对象为您提供了处理 websocket 请求所需的一切。它包含基础的 websocket 会话和 servlet 请求,以及向客户端发送消息的便捷方法。
// Session methods send(obj) // serialize object to json string and send it to client send(“message”) // send string to client send(byteBuffer) // send bytes to client sendAsClass(obj, clazz) // serialize object to json string and send it to client
// Upgrade Context methods (getters) matchedPath() // get the path that was used to match this request (ex, “/hello/{name}”) host() // host as string
queryParam(“name”) // query param by name as string queryParamAsClass(“name”, clazz) // query param parameter by name, as validator typed as specified class queryParams(“name”) // list of query parameters by name queryParamMap() // map of all query parameters queryString() // full query string
pathParam(“name”) // path parameter by name as string pathParamAsClass(“name”, clazz) // path parameter as validator typed as specified class pathParamMap() // map of all path parameters
header(“name”) // request header by name (can be used with Header.HEADERNAME) headerAsClass(“name”, clazz) // request header by name, as validator typed as specified class headerMap() // map of all request headers
cookie(“name”) // request cookie by name cookieMap() // map of all request cookies
attribute(“name”, value) // set an attribute on the request attribute(“name”) // get an attribute on the request attributeMap() // map of all attributes on the request
sessionAttribute(“name”) // get a session attribute sessionAttributeMap() // map of all session attributes
sendPing() // send a ping to the client sendPing(bytes) // send a ping with data to the client enableAutomaticPings() // enable automatic pinging to avoid timeouts enableAutomaticPings(1, HOURS, bytes) // enable automatic pinging with custom interval and/or data disableAutomaticPings() // disable automatic pinging
closeSession() // close the session closeSession(closeStatus) // close the session with a CloseStatus closeSession(400, “reason”) // close the session with a status and reason WsMessageContext message() // receive a string message from the client messageAsClass(clazz) // deserialize message from client WsBinaryMessageContext data() // receive a byte array of data from the client offset() // the offset of the data length() // the length of the data WsCloseContext status() // the int status for why connection was closed reason() // the string reason for why connection was closed WsErrorContext error() // the throwable error that occurred WsConnectContext WsConnectContext 类不向基 WsContext 添加任何内容
ctx.queryParamAsClass(“paramName”, MyClass.class) // creates a Validator for the value of queryParam(“paramName”) ctx.formParamAsClass(“paramName”, MyClass.class) // creates a Validator for the value of formParam(“paramName”) ctx.pathParamAsClass(“paramName”, MyClass.class) // creates a Validator for the value of pathParam(“paramName”) ctx.headerAsClass(“headerName”, MyClass.class) // creates a Validator for the value of header(“paramName”) ctx.bodyValidator(MyClass.class) // creates a Validator for the value of body() 你也可以通过 Validator.create (clazz,value,fieldName) 手动创建自己的验证器。
API 验证器 allowNullable() // turn the Validator into a NullableValidator (must be called first) check(predicate, “error”) // add a check with a ValidationError(“error”) to the Validator check(predicate, validationError) // add a check with a ValidationError to the Validator (can have args for localization) get() // return the validated value as the specified type, or throw ValidationException getOrThrow(exceptionFunction) // return the validated value as the specified type, or throw custom exception getOrDefault() // return default-value if value is null, else call get() errors() // get all the errors of the Validator (as map(“fieldName”, List)) 验证示例 // VALIDATE A SINGLE QUERY PARAMETER WITH A DEFAULT VALUE ///////////////////////////////////////////// Integer myValue = ctx.queryParamAsClass(“value”, Integer.class).getOrDefault(788) // validate value ctx.result(value) // return validated value to the client // GET ?value=a would yield HTTP 400 - {“my-qp”:[{“message”:”TYPE_CONVERSION_FAILED”,”args”:{},”value”:”a”}]} // GET ?value=1 would yield HTTP 200 - 1 (the validated value) // GET ? would yield HTTP 200 - 788 (the default value)
// VALIDATE TWO DEPENDENT QUERY PARAMETERS //////////////////////////////////////////////////////////// Instant fromDate = ctx.queryParamAsClass(“from”, Instant.class).get(); Instant toDate = ctx.queryParamAsClass(“to”, Instant.class) .check(it -> it.isAfter(fromDate), “‘to’ has to be after ‘from’”) .get();
// VALIDATE A JSON BODY /////////////////////////////////////////////////////////////////////////////// MyObject myObject = ctx.bodyValidator(MyObject.class) .check(obj -> obj.myObjectProperty == someValue, “THINGS_MUST_BE_EQUAL”) .get();
// VALIDATE WITH CUSTOM VALIDATIONERROR /////////////////////////////////////////////////////////////// ctx.queryParamAsClass(“param”, Integer.class) .check({ it > 5 }, new ValidationError(“OVER_LIMIT”, Map.of(“limit”, 5))) .get(); // GET ?param=10 would yield HTTP 400 - {“param”:[{“message”:”OVER_LIMIT”,”args”:{“limit”:5},”value”:10}]} 收集多个错误 Validator ageValidator = ctx.queryParamAsClass(“age”, Integer.class) .check(n -> !n.contains(“-“), “ILLEGAL_CHARACTER”)
// Empty map if no errors, otherwise a map with the key “age” and failed check messages in the list. Map<String, List> errors = ageValidator.errors();
// Merges all errors from all validators in the list. Empty map if no errors exist. Map<String, List