🧚 A practical web framework for Gleam

Default responses

+261
+1
README.md
··· 64 64 - [Serving static assets](https://github.com/lpil/wisp/tree/main/examples/6-serving-static-assets) 65 65 - [Logging](https://github.com/lpil/wisp/tree/main/examples/7-logging) 66 66 - [Working with cookies](https://github.com/lpil/wisp/tree/main/examples/8-working-with-cookies) 67 + - [Configuring default responses](https://github.com/lpil/wisp/tree/main/examples/9-configuring-default-responses) 67 68 68 69 API documentation is available on [HexDocs](https://hexdocs.pm/wisp/). 69 70
+36
examples/9-configuring-default-responses/README.md
··· 1 + # Wisp Example: Working with form data 2 + 3 + ```sh 4 + gleam run # Run the server 5 + gleam test # Run the tests 6 + ``` 7 + 8 + Wisp has a response body value called `Empty`, which is just that: an empty 9 + body. It can be returned by middleware functions such as `wisp.require_json` 10 + when the request isn't suitable for request handler to run, such as if the 11 + request body contains invalid JSON. 12 + 13 + You likely want your application to return a generic error page rather than an empty body, and this example shows how to do that. 14 + 15 + This example is based off of the [routing example][routing] so read that first. 16 + The additions are detailed here and commented in the code. 17 + 18 + [routing]: https://github.com/lpil/wisp/tree/main/examples/1-routing 19 + 20 + ### `app/router` module 21 + 22 + The `handle_request` function has been updated to return responses with the 23 + `wisp.Empty` body. 24 + 25 + ### `app/web` module 26 + 27 + The `middleware` function has been updated to return default responses when an 28 + `wisp.Empty` response body is returned. 29 + 30 + ### `app_test` module 31 + 32 + Tests have been added to test each of the . 33 + 34 + ### Other files 35 + 36 + No changes have been made to the other files.
+12
examples/9-configuring-default-responses/gleam.toml
··· 1 + name = "app" 2 + version = "1.0.0" 3 + description = "A Wisp example" 4 + 5 + [dependencies] 6 + gleam_stdlib = "~> 0.30" 7 + wisp = { path = "../.." } 8 + mist = "~> 0.13" 9 + gleam_erlang = "~> 0.22" 10 + 11 + [dev-dependencies] 12 + gleeunit = "~> 0.10"
+17
examples/9-configuring-default-responses/src/app.gleam
··· 1 + import gleam/erlang/process 2 + import mist 3 + import wisp 4 + import app/router 5 + 6 + pub fn main() { 7 + wisp.configure_logger() 8 + let secret_key_base = wisp.random_string(64) 9 + 10 + let assert Ok(_) = 11 + wisp.mist_handler(router.handle_request, secret_key_base) 12 + |> mist.new 13 + |> mist.port(8000) 14 + |> mist.start_http 15 + 16 + process.sleep_forever() 17 + }
+24
examples/9-configuring-default-responses/src/app/router.gleam
··· 1 + import app/web 2 + import gleam/string_builder 3 + import wisp.{Request, Response} 4 + 5 + pub fn handle_request(req: Request) -> Response { 6 + use req <- web.middleware(req) 7 + 8 + case wisp.path_segments(req) { 9 + // This request returns a non-empty body. 10 + [] -> { 11 + "<h1>Hello, Joe!</h1>" 12 + |> string_builder.from_string 13 + |> wisp.html_response(200) 14 + } 15 + 16 + // These routes return `wisp.Empty` bodies. 17 + ["internal-server-error"] -> wisp.internal_server_error() 18 + ["unprocessable-entity"] -> wisp.unprocessable_entity() 19 + ["method-not-allowed"] -> wisp.method_not_allowed([]) 20 + ["entity-too-large"] -> wisp.entity_too_large() 21 + ["bad-request"] -> wisp.bad_request() 22 + _ -> wisp.not_found() 23 + } 24 + }
+56
examples/9-configuring-default-responses/src/app/web.gleam
··· 1 + import wisp 2 + import gleam/bool 3 + import gleam/string_builder 4 + 5 + pub fn middleware( 6 + req: wisp.Request, 7 + handle_request: fn(wisp.Request) -> wisp.Response, 8 + ) -> wisp.Response { 9 + let req = wisp.method_override(req) 10 + use <- wisp.log_request(req) 11 + use <- wisp.rescue_crashes 12 + use req <- wisp.handle_head(req) 13 + 14 + // This new middleware has been added to the stack. 15 + // It is defined below. 16 + use <- default_responses 17 + 18 + handle_request(req) 19 + } 20 + 21 + pub fn default_responses(handle_request: fn() -> wisp.Response) -> wisp.Response { 22 + let response = handle_request() 23 + 24 + // The `bool.guard` function is used to return the original request if the 25 + // body is not `wisp.Empty`. 26 + use <- bool.guard(when: response.body != wisp.Empty, return: response) 27 + 28 + // You can use any logic to return appropriate responses depending on what is 29 + // best for your application. 30 + // I'm going to match on the status code and depending on what it is add 31 + // different HTML as the body. This is a good option for most applications. 32 + case response.status { 33 + 404 | 405 -> 34 + "<h1>There's nothing here</h1>" 35 + |> string_builder.from_string 36 + |> wisp.html_body(response, _) 37 + 38 + 400 | 422 -> 39 + "<h1>Bad request</h1>" 40 + |> string_builder.from_string 41 + |> wisp.html_body(response, _) 42 + 43 + 413 -> 44 + "<h1>Request entity too large</h1>" 45 + |> string_builder.from_string 46 + |> wisp.html_body(response, _) 47 + 48 + 500 -> 49 + "<h1>Internal server error</h1>" 50 + |> string_builder.from_string 51 + |> wisp.html_body(response, _) 52 + 53 + // For other status codes redirect to the home page 54 + _ -> wisp.redirect("/") 55 + } 56 + }
+115
examples/9-configuring-default-responses/test/app_test.gleam
··· 1 + import gleeunit 2 + import gleeunit/should 3 + import gleam/string 4 + import wisp/testing 5 + import app/router 6 + 7 + pub fn main() { 8 + gleeunit.main() 9 + } 10 + 11 + pub fn home_test() { 12 + let response = router.handle_request(testing.get("/", [])) 13 + 14 + response.status 15 + |> should.equal(200) 16 + 17 + response.headers 18 + |> should.equal([#("content-type", "text/html")]) 19 + 20 + let assert True = 21 + response 22 + |> testing.string_body 23 + |> string.contains("<h1>Hello, Joe!</h1>") 24 + } 25 + 26 + pub fn internal_server_error_test() { 27 + let response = 28 + router.handle_request(testing.get("/internal-server-error", [])) 29 + 30 + response.status 31 + |> should.equal(500) 32 + 33 + response.headers 34 + |> should.equal([#("content-type", "text/html")]) 35 + 36 + let assert True = 37 + response 38 + |> testing.string_body 39 + |> string.contains("<h1>Internal server error</h1>") 40 + } 41 + 42 + pub fn unprocessable_entity_test() { 43 + let response = router.handle_request(testing.get("/unprocessable-entity", [])) 44 + 45 + response.status 46 + |> should.equal(422) 47 + 48 + response.headers 49 + |> should.equal([#("content-type", "text/html")]) 50 + 51 + let assert True = 52 + response 53 + |> testing.string_body 54 + |> string.contains("<h1>Bad request</h1>") 55 + } 56 + 57 + pub fn bad_request_test() { 58 + let response = router.handle_request(testing.get("/bad-request", [])) 59 + 60 + response.status 61 + |> should.equal(400) 62 + 63 + response.headers 64 + |> should.equal([#("content-type", "text/html")]) 65 + 66 + let assert True = 67 + response 68 + |> testing.string_body 69 + |> string.contains("<h1>Bad request</h1>") 70 + } 71 + 72 + pub fn method_not_allowed_test() { 73 + let response = router.handle_request(testing.get("/method-not-allowed", [])) 74 + 75 + response.status 76 + |> should.equal(405) 77 + 78 + response.headers 79 + |> should.equal([#("allow", ""), #("content-type", "text/html")]) 80 + 81 + let assert True = 82 + response 83 + |> testing.string_body 84 + |> string.contains("<h1>There's nothing here</h1>") 85 + } 86 + 87 + pub fn not_found_test() { 88 + let response = router.handle_request(testing.get("/not-found", [])) 89 + 90 + response.status 91 + |> should.equal(404) 92 + 93 + response.headers 94 + |> should.equal([#("content-type", "text/html")]) 95 + 96 + let assert True = 97 + response 98 + |> testing.string_body 99 + |> string.contains("<h1>There's nothing here</h1>") 100 + } 101 + 102 + pub fn entity_too_large_test() { 103 + let response = router.handle_request(testing.get("/entity-too-large", [])) 104 + 105 + response.status 106 + |> should.equal(413) 107 + 108 + response.headers 109 + |> should.equal([#("content-type", "text/html")]) 110 + 111 + let assert True = 112 + response 113 + |> testing.string_body 114 + |> string.contains("<h1>Request entity too large</h1>") 115 + }