Skip to content

Commit 0537c88

Browse files
committed
book: add frameworks section
1 parent 8f7c15f commit 0537c88

File tree

2 files changed

+177
-0
lines changed

2 files changed

+177
-0
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
- [Configuration](./configuration.md)
88
- [Template syntax](./template_syntax.md)
99
- [Filters](./filters.md)
10+
- [Frameworks](./frameworks.md)
1011
- [Performance](./performance.md)
1112
- [Upgrading](./upgrading.md)

book/src/frameworks.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Rinja in web frame
2+
3+
Rinja's [`Template::render()`] returns <code>Result&lt;String, [rinja::Error]&gt;</code>.
4+
To make this result work in your preferred web-framework, you'll need to handle both cases:
5+
converting the `String` to a web-response with the correct `Content-Type`,
6+
and the `Error` case to a proper error message.
7+
8+
While in many cases it will be enough to simply convert the `Error` to
9+
10+
* `Box<dyn std::error::Error + Send + Sync>` using `err.into_box()` or
11+
* `std::io::Error` using `err.into_io_error()`
12+
13+
it is **recommended** to use a custom error type.
14+
This way you can display the error message in your app's layout,
15+
and you are better prepared for the likely case that your app grows in the future.
16+
Maybe you'll need to access a database and handle errors?
17+
Maybe you'll add multiple languages and you want to localize error messages?
18+
19+
The crates [`thiserror`] and [`displaydoc`] can be useful to implement this error type.
20+
21+
[`Template::render()`]: <https://docs.rs/rinja/0.3.5/rinja/trait.Template.html#method.render>
22+
[rinja::Error]: <https://docs.rs/rinja/0.3.5/rinja/enum.Error.html>
23+
[`thiserror`]: <https://crates.io/crates/thiserror>
24+
[`displaydoc`]: <https://crates.io/crates/displaydoc>
25+
26+
## [Actix-Web] and Rinja
27+
28+
[Actix-Web]: <https://crates.io/crates/actix-web>
29+
30+
To convert the `String` to an HTML response, you can use
31+
[`Html::new(_)`](https://docs.rs/actix-web/4.9.0/actix_web/web/struct.Html.html#method.new).
32+
33+
```rust
34+
use actix_web::Responder;
35+
use actix_web::web::Html;
36+
37+
fn handler() -> Result<impl Responder, AppError> {
38+
39+
Ok(Html::new(template.render()?))
40+
}
41+
```
42+
43+
To implement your own error type, you can use this boilerplate code:
44+
45+
```rust
46+
use actix_web::{HttpResponse, Responder};
47+
use actix_web::error::ResponseError;
48+
use actix_web::http::StatusCode;
49+
use actix_web::web::Html;
50+
use rinja::Template;
51+
52+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
53+
enum AppError {
54+
/// could not render template
55+
Render(#[from] rinja::Error),
56+
}
57+
58+
impl ResponseError for AppError {
59+
fn status_code(&self) -> StatusCode {
60+
match &self {
61+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
62+
}
63+
}
64+
}
65+
66+
impl Responder for AppError {
67+
type Body = String;
68+
69+
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
70+
#[derive(Debug, Template)]
71+
#[template(path = "error.html")]
72+
struct Tmpl<'a> { … }
73+
74+
let tmpl = Tmpl { … };
75+
if let Ok(body) = tmpl.render() {
76+
(Html::new(body), self.status_code()).respond_to(req)
77+
} else {
78+
("".to_string(), self.status_code()).respond_to(req)
79+
}
80+
}
81+
}
82+
```
83+
84+
## [Axum] and Rinja
85+
86+
[Axum]: <https://crates.io/crates/axum>
87+
88+
To convert the `String` to an HTML response, you can use
89+
[`Html(_)`](https://docs.rs/axum/0.8.1/axum/response/struct.Html.html).
90+
91+
```rust
92+
use axum::response::{Html, IntoResponse};
93+
94+
async fn handler() -> Result<impl IntoResponse, AppError> {
95+
96+
Ok(Html(template.render()?))
97+
}
98+
```
99+
100+
To implement your own error type, you can use this boilerplate code:
101+
102+
```rust
103+
use axum::response::IntoResponse;
104+
use rinja::Template;
105+
106+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
107+
enum AppError {
108+
/// could not render template
109+
Render(#[from] rinja::Error),
110+
}
111+
112+
impl IntoResponse for AppError {
113+
fn into_response(self) -> Response {
114+
#[derive(Debug, Template)]
115+
#[template(path = "error.html")]
116+
struct Tmpl { … }
117+
118+
let status = match &self {
119+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
120+
};
121+
let tmpl = Tmpl { … };
122+
if let Ok(body) = tmpl.render() {
123+
(status, Html(body)).into_response()
124+
} else {
125+
(status, "Something went wrong").into_response()
126+
}
127+
}
128+
}
129+
```
130+
131+
## [Warp] and Rinja
132+
133+
[Warp]: <https://crates.io/crates/warp>
134+
135+
To convert the `String` to an HTML response, you can use
136+
[`html(_)`](https://docs.rs/warp/0.3.7/warp/reply/fn.html.html).
137+
138+
```rust
139+
use warp::reply::{Reply, html};
140+
141+
fn handler() -> Result<impl Reply, AppError> {
142+
143+
Ok(html(template.render()?))
144+
}
145+
```
146+
147+
To implement your own error type, you can use this boilerplate code:
148+
149+
```rust
150+
use http::StatusCode;
151+
use warp::reply::{Reply, Response, html};
152+
153+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
154+
enum AppError {
155+
/// could not render template
156+
Render(#[from] rinja::Error),
157+
}
158+
159+
impl Reply for AppError {
160+
fn into_response(self) -> Response {
161+
#[derive(Debug, Template)]
162+
#[template(path = "error.html")]
163+
struct Tmpl { … }
164+
165+
let status = match &self {
166+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
167+
};
168+
let template = Tmpl { … };
169+
if let Ok(body) = template.render() {
170+
with_status(html(body), status).into_response()
171+
} else {
172+
status.into_response()
173+
}
174+
}
175+
}
176+
```

0 commit comments

Comments
 (0)