Skip to content

Commit 63ca3e5

Browse files
committed
book: add frameworks section
1 parent 6212bdf commit 63ca3e5

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-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+
- [Web-frameworks](./frameworks.md)
1011
- [Performance](./performance.md)
1112
- [Upgrading](./upgrading.md)

book/src/frameworks.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Working with web-frameworks
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
27+
28+
To convert the `String` to an HTML response, you can use
29+
[`Html::new(_)`](https://docs.rs/actix-web/4.9.0/actix_web/web/struct.Html.html#method.new).
30+
31+
```rust
32+
use actix_web::Responder;
33+
use actix_web::web::Html;
34+
35+
fn handler() -> Result<impl Responder, AppError> {
36+
37+
Ok(Html::new(template.render()?))
38+
}
39+
```
40+
41+
To implement your own error type, you can use this boilerplate code:
42+
43+
```rust
44+
use actix_web::{HttpResponse, Responder};
45+
use actix_web::error::ResponseError;
46+
use actix_web::http::StatusCode;
47+
use actix_web::web::Html;
48+
use rinja::Template;
49+
50+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
51+
enum AppError {
52+
/// could not render template
53+
Render(#[from] rinja::Error),
54+
}
55+
56+
impl ResponseError for AppError {
57+
fn status_code(&self) -> StatusCode {
58+
match &self {
59+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
60+
}
61+
}
62+
}
63+
64+
impl Responder for AppError {
65+
type Body = String;
66+
67+
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
68+
#[derive(Debug, Template)]
69+
#[template(path = "error.html")]
70+
struct Tmpl { … }
71+
72+
let tmpl = Tmpl { … };
73+
if let Ok(body) = tmpl.render() {
74+
(Html::new(body), self.status_code()).respond_to(req)
75+
} else {
76+
(String::new(), self.status_code()).respond_to(req)
77+
}
78+
}
79+
}
80+
```
81+
82+
## Axum
83+
84+
To convert the `String` to an HTML response, you can use
85+
[`Html(_)`](https://docs.rs/axum/0.8.1/axum/response/struct.Html.html).
86+
87+
```rust
88+
use axum::response::{Html, IntoResponse};
89+
90+
async fn handler() -> Result<impl IntoResponse, AppError> {
91+
92+
Ok(Html(template.render()?))
93+
}
94+
```
95+
96+
To implement your own error type, you can use this boilerplate code:
97+
98+
```rust
99+
use axum::response::IntoResponse;
100+
use rinja::Template;
101+
102+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
103+
enum AppError {
104+
/// could not render template
105+
Render(#[from] rinja::Error),
106+
}
107+
108+
impl IntoResponse for AppError {
109+
fn into_response(self) -> Response {
110+
#[derive(Debug, Template)]
111+
#[template(path = "error.html")]
112+
struct Tmpl { … }
113+
114+
let status = match &self {
115+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
116+
};
117+
let tmpl = Tmpl { … };
118+
if let Ok(body) = tmpl.render() {
119+
(status, Html(body)).into_response()
120+
} else {
121+
(status, "Something went wrong").into_response()
122+
}
123+
}
124+
}
125+
```
126+
127+
## Rocket
128+
129+
To convert the `String` to an HTML response, you can use
130+
[`RawHtml(_)`](https://docs.rs/rocket/latest/rocket/response/content/struct.RawHtml.html).
131+
132+
```rust
133+
use rocket::get;
134+
use rocket::response::content::RawHtml;
135+
use rocket::response::Responder;
136+
137+
#[get(…)]
138+
fn handler<'r>() -> Result<impl Responder<'r, 'static>, AppError> {
139+
140+
Ok(RawHtml(template.render()?))
141+
}
142+
```
143+
144+
To implement your own error type, you can use this boilerplate code:
145+
146+
```rust
147+
use rinja::Template;
148+
use rocket::http::Status;
149+
use rocket::response::content::RawHtml;
150+
use rocket::response::Responder;
151+
use rocket::{Request, Response};
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<'r> Responder<'r, 'static> for AppError {
160+
fn respond_to(
161+
self,
162+
request: &'r Request<'_>,
163+
) -> Result<Response<'static>, Status> {
164+
#[derive(Debug, Template)]
165+
#[template(path = "error.html")]
166+
struct Tmpl { … }
167+
168+
let status = match &self {
169+
AppError::Render(_) => Status::InternalServerError,
170+
};
171+
let template = Tmpl { … };
172+
if let Ok(body) = template.render() {
173+
(status, RawHtml(body)).respond_to(request)
174+
} else {
175+
(status, "internal server error").respond_to(request)
176+
}
177+
}
178+
}
179+
```
180+
181+
## Warp
182+
183+
To convert the `String` to an HTML response, you can use
184+
[`html(_)`](https://docs.rs/warp/0.3.7/warp/reply/fn.html.html).
185+
186+
```rust
187+
use warp::reply::{Reply, html};
188+
189+
fn handler() -> Result<impl Reply, AppError> {
190+
191+
Ok(html(template.render()?))
192+
}
193+
```
194+
195+
To implement your own error type, you can use this boilerplate code:
196+
197+
```rust
198+
use http::StatusCode;
199+
use warp::reply::{Reply, Response, html};
200+
201+
#[derive(Debug, displaydoc::Display, thiserror::Error)]
202+
enum AppError {
203+
/// could not render template
204+
Render(#[from] rinja::Error),
205+
}
206+
207+
impl Reply for AppError {
208+
fn into_response(self) -> Response {
209+
#[derive(Debug, Template)]
210+
#[template(path = "error.html")]
211+
struct Tmpl { … }
212+
213+
let status = match &self {
214+
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
215+
};
216+
let template = Tmpl { … };
217+
if let Ok(body) = template.render() {
218+
with_status(html(body), status).into_response()
219+
} else {
220+
status.into_response()
221+
}
222+
}
223+
}
224+
```

0 commit comments

Comments
 (0)