🧚 A practical web framework for Gleam

charset=utf-8

Closes https://github.com/gleam-wisp/wisp/issues/86

+5
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 + ## v0.16.0 - Unreleased 4 + 5 + - HTML and JSON body functions now include `charset=utf-8` in the content-type 6 + header. 7 + 3 8 ## v0.15.0 - 2024-05-12 4 9 5 10 - The `mist` version constraint has been increased to >= 1.2.0.
+20 -7
src/wisp.gleam
··· 268 268 /// ``` 269 269 /// 270 270 pub fn html_response(html: StringBuilder, status: Int) -> Response { 271 - HttpResponse(status, [#("content-type", "text/html")], Text(html)) 271 + HttpResponse( 272 + status, 273 + [#("content-type", "text/html; charset=utf-8")], 274 + Text(html), 275 + ) 272 276 } 273 277 274 278 /// Create a JSON response. ··· 285 289 /// ``` 286 290 /// 287 291 pub fn json_response(json: StringBuilder, status: Int) -> Response { 288 - HttpResponse(status, [#("content-type", "application/json")], Text(json)) 292 + HttpResponse( 293 + status, 294 + [#("content-type", "application/json; charset=utf-8")], 295 + Text(json), 296 + ) 289 297 } 290 298 291 299 /// Set the body of a response to a given HTML document, and set the ··· 299 307 /// let body = string_builder.from_string("<h1>Hello, Joe!</h1>") 300 308 /// response(201) 301 309 /// |> html_body(body) 302 - /// // -> Response(201, [#("content-type", "text/html")], Text(body)) 310 + /// // -> Response(201, [#("content-type", "text/html; charset=utf-8")], Text(body)) 303 311 /// ``` 304 312 /// 305 313 pub fn html_body(response: Response, html: StringBuilder) -> Response { 306 314 response 307 315 |> response.set_body(Text(html)) 308 - |> response.set_header("content-type", "text/html") 316 + |> response.set_header("content-type", "text/html; charset=utf-8") 309 317 } 310 318 311 319 /// Set the body of a response to a given JSON document, and set the ··· 319 327 /// let body = string_builder.from_string("{\"name\": \"Joe\"}") 320 328 /// response(201) 321 329 /// |> json_body(body) 322 - /// // -> Response(201, [#("content-type", "application/json")], Text(body)) 330 + /// // -> Response(201, [#("content-type", "application/json; charset=utf-8")], Text(body)) 323 331 /// ``` 324 332 /// 325 333 pub fn json_body(response: Response, json: StringBuilder) -> Response { 326 334 response 327 335 |> response.set_body(Text(json)) 328 - |> response.set_header("content-type", "application/json") 336 + |> response.set_header("content-type", "application/json; charset=utf-8") 329 337 } 330 338 331 339 /// Set the body of a response to a given string builder. ··· 1598 1606 |> result.unwrap("") 1599 1607 |> marceau.extension_to_mime_type 1600 1608 1609 + let content_type = case mime_type { 1610 + "application/json" | "text/" <> _ -> mime_type <> "; charset=utf-8" 1611 + _ -> mime_type 1612 + } 1613 + 1601 1614 case simplifile.is_file(path) { 1602 1615 Ok(True) -> 1603 1616 response.new(200) 1604 - |> response.set_header("content-type", mime_type) 1617 + |> response.set_header("content-type", content_type) 1605 1618 |> response.set_body(File(path)) 1606 1619 _ -> handler() 1607 1620 }
test/fixture.dat

This is a binary file and will not be displayed.

+28 -10
test/wisp_test.gleam
··· 123 123 response.status 124 124 |> should.equal(201) 125 125 response.headers 126 - |> should.equal([#("content-type", "application/json")]) 126 + |> should.equal([#("content-type", "application/json; charset=utf-8")]) 127 127 response 128 128 |> testing.string_body 129 129 |> should.equal("{\"one\":1,\"two\":2}") ··· 135 135 response.status 136 136 |> should.equal(200) 137 137 response.headers 138 - |> should.equal([#("content-type", "text/html")]) 138 + |> should.equal([#("content-type", "text/html; charset=utf-8")]) 139 139 response 140 140 |> testing.string_body 141 141 |> should.equal("Hello, world!") ··· 149 149 response.status 150 150 |> should.equal(405) 151 151 response.headers 152 - |> should.equal([#("allow", "GET"), #("content-type", "text/html")]) 152 + |> should.equal([ 153 + #("allow", "GET"), 154 + #("content-type", "text/html; charset=utf-8"), 155 + ]) 153 156 response 154 157 |> testing.string_body 155 158 |> should.equal("Hello, world!") ··· 359 362 response.status 360 363 |> should.equal(200) 361 364 response.headers 362 - |> should.equal([#("content-type", "text/plain")]) 365 + |> should.equal([#("content-type", "text/plain; charset=utf-8")]) 363 366 response.body 364 367 |> should.equal(wisp.File("./test/fixture.txt")) 365 368 ··· 370 373 response.status 371 374 |> should.equal(200) 372 375 response.headers 373 - |> should.equal([#("content-type", "application/json")]) 376 + |> should.equal([#("content-type", "application/json; charset=utf-8")]) 374 377 response.body 375 378 |> should.equal(wisp.File("./test/fixture.json")) 376 379 380 + // Get some other file 381 + let response = 382 + testing.get("/stuff/test/fixture.dat", []) 383 + |> handler 384 + response.status 385 + |> should.equal(200) 386 + response.headers 387 + |> should.equal([#("content-type", "application/octet-stream")]) 388 + response.body 389 + |> should.equal(wisp.File("./test/fixture.dat")) 390 + 377 391 // Get something not handled by the static file server 378 392 let response = 379 393 testing.get("/stuff/this-does-not-exist", []) ··· 397 411 response.status 398 412 |> should.equal(200) 399 413 response.headers 400 - |> should.equal([#("content-type", "text/plain")]) 414 + |> should.equal([#("content-type", "text/plain; charset=utf-8")]) 401 415 response.body 402 416 |> should.equal(wisp.File("./test/fixture.txt")) 403 417 } ··· 413 427 response.status 414 428 |> should.equal(200) 415 429 response.headers 416 - |> should.equal([#("content-type", "text/plain")]) 430 + |> should.equal([#("content-type", "text/plain; charset=utf-8")]) 417 431 response.body 418 432 |> should.equal(wisp.File("./test/fixture.txt")) 419 433 } ··· 733 747 |> handler(Error(Nil)) 734 748 |> should.equal(Response( 735 749 201, 736 - [#("content-type", "text/html")], 750 + [#("content-type", "text/html; charset=utf-8")], 737 751 wisp.Text(string_builder.from_string("Hello!")), 738 752 )) 739 753 740 754 testing.get("/", []) 741 755 |> request.set_method(http.Head) 742 756 |> handler(Ok("HEAD")) 743 - |> should.equal(Response(201, [#("content-type", "text/html")], wisp.Empty)) 757 + |> should.equal(Response( 758 + 201, 759 + [#("content-type", "text/html; charset=utf-8")], 760 + wisp.Empty, 761 + )) 744 762 745 763 testing.get("/", []) 746 764 |> request.set_method(http.Post) ··· 885 903 |> wisp.json_body(string_builder.from_string("{\"one\":1,\"two\":2}")) 886 904 |> should.equal(Response( 887 905 200, 888 - [#("content-type", "application/json")], 906 + [#("content-type", "application/json; charset=utf-8")], 889 907 wisp.Text(string_builder.from_string("{\"one\":1,\"two\":2}")), 890 908 )) 891 909 }