zino_http/response/
mod.rs

1//! Constructing responses and rejections.
2//!
3//! # Examples
4//!
5//! ```rust
6//! use zino_core::{application::ApplicationCode, error::Error, SharedString};
7//! use zino_http::response::{Response, StatusCode};
8//!
9//! #[derive(Debug, Clone, Copy, Default, PartialEq)]
10//! #[repr(i32)]
11//! pub enum AppCode {
12//!     #[default]
13//!     Success = 20000,
14//!     InvalidInput = 40001,
15//!     InternalError = 50000,
16//! }
17//!
18//! impl ApplicationCode for AppCode {
19//!     #[inline]
20//!     fn code(&self) -> i32 {
21//!         *self as i32
22//!     }
23//!
24//!     #[inline]
25//!     fn message(&self) -> SharedString {
26//!         match self {
27//!             AppCode::Success => "success".into(),
28//!             AppCode::InvalidInput => "invalid input".into(),
29//!             AppCode::InternalError => "internal error".into(),
30//!         }
31//!     }
32//! }
33//!
34//! async fn send_success_response() -> Result<Response<StatusCode>, Error> {
35//!     let mut res = Response::default();
36//!     res.set_app_code(&AppCode::Success);
37//!     Ok(res)
38//! }
39//! ```
40
41use crate::{
42    helper,
43    request::RequestContext,
44    timing::{ServerTiming, TimingMetric},
45};
46use bytes::Bytes;
47use etag::EntityTag;
48use http::{HeaderMap, HeaderName};
49use serde::Serialize;
50use std::{
51    marker::PhantomData,
52    mem,
53    time::{Duration, Instant},
54};
55use zino_core::{
56    JsonValue, SharedString, Uuid, application::ApplicationCode, error::Error,
57    extension::JsonValueExt, trace::TraceContext, validation::Validation,
58};
59use zino_storage::NamedFile;
60
61#[cfg(feature = "inertia")]
62use crate::inertia::InertiaPage;
63
64#[cfg(feature = "cookie")]
65use cookie::{Cookie, SameSite};
66
67mod rejection;
68mod response_code;
69mod webhook;
70
71pub use rejection::{ExtractRejection, Rejection};
72pub use response_code::ResponseCode;
73pub use webhook::WebHook;
74
75/// An HTTP status code for http v0.2.
76#[cfg(feature = "http02")]
77pub type StatusCode = http02::StatusCode;
78
79/// An HTTP status code.
80#[cfg(not(feature = "http02"))]
81pub type StatusCode = http::StatusCode;
82
83/// A function pointer of transforming the response data.
84pub type DataTransformer = fn(data: &JsonValue) -> Result<Bytes, Error>;
85
86/// An HTTP response.
87#[derive(Debug, Clone, Serialize)]
88#[serde(rename_all = "snake_case")]
89pub struct Response<S: ResponseCode> {
90    /// A URI reference that identifies the problem type.
91    #[serde(rename = "type")]
92    #[serde(skip_serializing_if = "Option::is_none")]
93    type_uri: Option<SharedString>,
94    /// A short, human-readable summary of the problem type.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    title: Option<SharedString>,
97    /// Status code.
98    #[serde(rename = "status")]
99    status_code: u16,
100    /// Application code.
101    #[serde(rename = "code")]
102    #[serde(skip_serializing_if = "Option::is_none")]
103    app_code: Option<i32>,
104    /// A human-readable explanation specific to this occurrence of the problem.
105    #[serde(skip_serializing_if = "Option::is_none")]
106    detail: Option<SharedString>,
107    /// A URI reference that identifies the specific occurrence of the problem.
108    #[serde(skip_serializing_if = "Option::is_none")]
109    instance: Option<SharedString>,
110    /// Indicates the response is successful or not.
111    success: bool,
112    /// A context-specific descriptive message for successful response.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    message: Option<SharedString>,
115    /// Start time.
116    #[serde(skip)]
117    start_time: Instant,
118    /// Request ID.
119    #[serde(skip_serializing_if = "Uuid::is_nil")]
120    request_id: Uuid,
121    /// JSON data.
122    #[serde(rename = "data")]
123    #[serde(skip_serializing_if = "JsonValue::is_null")]
124    json_data: JsonValue,
125    /// Bytes data.
126    #[serde(skip)]
127    bytes_data: Bytes,
128    /// Transformer of the response data.
129    #[serde(skip)]
130    data_transformer: Option<DataTransformer>,
131    /// Content type.
132    #[serde(skip)]
133    content_type: Option<SharedString>,
134    /// Trace context.
135    #[serde(skip)]
136    trace_context: Option<TraceContext>,
137    /// Server timing.
138    #[serde(skip)]
139    server_timing: ServerTiming,
140    /// Custom headers.
141    #[serde(skip)]
142    headers: HeaderMap<String>,
143    /// Phantom type of response code.
144    #[serde(skip)]
145    phantom: PhantomData<S>,
146}
147
148impl<S: ResponseCode> Response<S> {
149    /// Creates a new instance.
150    pub fn new(code: S) -> Self {
151        let success = code.is_success();
152        let message = code.message();
153        let mut res = Self {
154            type_uri: code.type_uri(),
155            title: code.title(),
156            status_code: code.status_code(),
157            app_code: None,
158            detail: None,
159            instance: None,
160            success,
161            message: None,
162            start_time: Instant::now(),
163            request_id: Uuid::nil(),
164            json_data: JsonValue::Null,
165            bytes_data: Bytes::new(),
166            data_transformer: None,
167            content_type: None,
168            trace_context: None,
169            server_timing: ServerTiming::new(),
170            headers: HeaderMap::default(),
171            phantom: PhantomData,
172        };
173        if success {
174            res.message = message;
175        } else {
176            res.detail = message;
177        }
178        res
179    }
180
181    /// Creates a new instance with the request context.
182    pub fn with_context<Ctx: RequestContext>(code: S, ctx: &Ctx) -> Self {
183        let success = code.is_success();
184        let message = code.message();
185        let mut res = Self {
186            type_uri: code.type_uri(),
187            title: code.title(),
188            status_code: code.status_code(),
189            app_code: None,
190            detail: None,
191            instance: (!success).then(|| ctx.instance().into()),
192            success,
193            message: None,
194            start_time: ctx.start_time(),
195            request_id: ctx.request_id(),
196            json_data: JsonValue::Null,
197            bytes_data: Bytes::new(),
198            data_transformer: None,
199            content_type: None,
200            trace_context: None,
201            server_timing: ServerTiming::new(),
202            headers: HeaderMap::default(),
203            phantom: PhantomData,
204        };
205        if success {
206            res.message = message;
207        } else {
208            res.detail = message;
209        }
210        res.trace_context = Some(ctx.new_trace_context());
211        res
212    }
213
214    /// Provides the request context for the response.
215    pub fn context<Ctx: RequestContext>(mut self, ctx: &Ctx) -> Self {
216        self.instance = (!self.is_success()).then(|| ctx.instance().into());
217        self.start_time = ctx.start_time();
218        self.request_id = ctx.request_id();
219        self.trace_context = Some(ctx.new_trace_context());
220        self
221    }
222
223    /// Renders a template with the data and sets it as the reponse.
224    #[cfg(feature = "view")]
225    pub fn render<T: Serialize>(mut self, template_name: &str, data: T) -> Self {
226        let result = serde_json::to_value(data)
227            .map_err(|err| err.into())
228            .and_then(|value| {
229                if let JsonValue::Object(map) = value {
230                    crate::view::render(template_name, map)
231                } else {
232                    Err(zino_core::warn!("invalid template data"))
233                }
234            });
235        match result {
236            Ok(content) => {
237                self.json_data = content.into();
238                self.bytes_data = Bytes::new();
239                self.content_type = Some("text/html; charset=utf-8".into());
240            }
241            Err(err) => {
242                let code = S::INTERNAL_SERVER_ERROR;
243                self.type_uri = code.type_uri();
244                self.title = code.title();
245                self.status_code = code.status_code();
246                self.success = false;
247                self.detail = Some(err.to_string().into());
248                self.json_data = JsonValue::Null;
249                self.bytes_data = Bytes::new();
250            }
251        }
252        self
253    }
254
255    /// Sets the response code.
256    pub fn set_code(&mut self, code: S) {
257        let success = code.is_success();
258        let message = code.message();
259        self.type_uri = code.type_uri();
260        self.title = code.title();
261        self.status_code = code.status_code();
262        self.success = success;
263        if success {
264            self.detail = None;
265            self.message = message;
266        } else {
267            self.detail = message;
268            self.message = None;
269        }
270    }
271
272    /// Sets the status code.
273    #[inline]
274    pub fn set_status_code(&mut self, status_code: impl Into<u16>) {
275        self.status_code = status_code.into();
276    }
277
278    /// Sets the application code.
279    #[inline]
280    pub fn set_app_code<C: ApplicationCode>(&mut self, app_code: &C) {
281        self.app_code = Some(app_code.code());
282        self.message = Some(app_code.message());
283    }
284
285    /// Sets a URI reference that identifies the specific occurrence of the problem.
286    #[inline]
287    pub fn set_instance(&mut self, instance: impl Into<SharedString>) {
288        self.instance = Some(instance.into());
289    }
290
291    /// Sets the message. If the response is not successful,
292    /// it should be a human-readable explanation specific to this occurrence of the problem.
293    pub fn set_message(&mut self, message: impl Into<SharedString>) {
294        fn inner<S: ResponseCode>(res: &mut Response<S>, message: SharedString) {
295            if res.is_success() {
296                res.detail = None;
297                res.message = Some(message);
298            } else {
299                res.detail = Some(message);
300                res.message = None;
301            }
302        }
303        inner::<S>(self, message.into())
304    }
305
306    /// Sets the error message.
307    pub fn set_error_message(&mut self, error: impl Into<Error>) {
308        fn inner<S: ResponseCode>(res: &mut Response<S>, error: Error) {
309            let message = error.to_string().into();
310            if res.is_success() {
311                res.detail = None;
312                res.message = Some(message);
313            } else {
314                res.detail = Some(message);
315                res.message = None;
316            }
317        }
318        inner::<S>(self, error.into())
319    }
320
321    /// Sets the response data.
322    #[inline]
323    pub fn set_data<T: Serialize>(&mut self, data: &T) {
324        match serde_json::to_value(data) {
325            Ok(value) => {
326                self.json_data = value;
327                self.bytes_data = Bytes::new();
328            }
329            Err(err) => self.set_error_message(err),
330        }
331    }
332
333    /// Sets the JSON data.
334    #[inline]
335    pub fn set_json_data(&mut self, data: impl Into<JsonValue>) {
336        self.json_data = data.into();
337        self.bytes_data = Bytes::new();
338    }
339
340    /// Sets the bytes data.
341    #[inline]
342    pub fn set_bytes_data(&mut self, data: impl Into<Bytes>) {
343        self.json_data = JsonValue::Null;
344        self.bytes_data = data.into();
345    }
346
347    /// Sets the response data for the validation.
348    #[inline]
349    pub fn set_validation_data(&mut self, validation: Validation) {
350        self.json_data = validation.into_map().into();
351        self.bytes_data = Bytes::new();
352    }
353
354    /// Sets a transformer for the response data.
355    #[inline]
356    pub fn set_data_transformer(&mut self, transformer: DataTransformer) {
357        self.data_transformer = Some(transformer);
358    }
359
360    /// Sets the content type.
361    ///
362    /// # Note
363    ///
364    /// Currently, we have built-in support for the following values:
365    ///
366    /// - `application/json`
367    /// - `application/jsonlines`
368    /// - `application/octet-stream`
369    /// - `application/problem+json`
370    /// - `application/x-www-form-urlencoded`
371    /// - `text/csv`
372    /// - `text/html`
373    /// - `text/plain`
374    #[inline]
375    pub fn set_content_type(&mut self, content_type: impl Into<SharedString>) {
376        self.content_type = Some(content_type.into());
377    }
378
379    /// Sets the form data as the response body.
380    #[inline]
381    pub fn set_form_response(&mut self, data: impl Into<JsonValue>) {
382        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
383            res.set_json_data(data);
384            res.set_content_type("application/x-www-form-urlencoded");
385            res.set_data_transformer(|data| {
386                let mut bytes = Vec::new();
387                serde_qs::to_writer(&data, &mut bytes)?;
388                Ok(bytes.into())
389            });
390        }
391        inner::<S>(self, data.into())
392    }
393
394    /// Sets the JSON data as the response body.
395    #[inline]
396    pub fn set_json_response(&mut self, data: impl Into<JsonValue>) {
397        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
398            res.set_json_data(data);
399            res.set_data_transformer(|data| Ok(serde_json::to_vec(&data)?.into()));
400        }
401        inner::<S>(self, data.into())
402    }
403
404    /// Sets the JSON Lines data as the response body.
405    #[inline]
406    pub fn set_jsonlines_response(&mut self, data: impl Into<JsonValue>) {
407        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
408            res.set_json_data(data);
409            res.set_content_type("application/jsonlines; charset=utf-8");
410            res.set_data_transformer(|data| Ok(data.to_jsonlines(Vec::new())?.into()));
411        }
412        inner::<S>(self, data.into())
413    }
414
415    /// Sets the CSV data as the response body.
416    #[inline]
417    pub fn set_csv_response(&mut self, data: impl Into<JsonValue>) {
418        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
419            res.set_json_data(data);
420            res.set_content_type("text/csv; charset=utf-8");
421            res.set_data_transformer(|data| Ok(data.to_csv(Vec::new())?.into()));
422        }
423        inner::<S>(self, data.into())
424    }
425
426    /// Sets the plain text as the response body.
427    #[inline]
428    pub fn set_text_response(&mut self, data: impl Into<String>) {
429        self.set_json_data(data.into());
430        self.set_content_type("text/plain; charset=utf-8");
431    }
432
433    /// Sets the bytes data as the response body.
434    #[inline]
435    pub fn set_bytes_response(&mut self, data: impl Into<Bytes>) {
436        self.set_bytes_data(data);
437        self.set_content_type("application/octet-stream");
438    }
439
440    /// Sets the request ID.
441    #[inline]
442    pub(crate) fn set_request_id(&mut self, request_id: Uuid) {
443        self.request_id = request_id;
444    }
445
446    /// Sets the trace context from headers.
447    #[inline]
448    pub(crate) fn set_trace_context(&mut self, trace_context: Option<TraceContext>) {
449        self.trace_context = trace_context;
450    }
451
452    /// Sets the start time.
453    #[inline]
454    pub(crate) fn set_start_time(&mut self, start_time: Instant) {
455        self.start_time = start_time;
456    }
457
458    /// Sends a cookie to the user agent.
459    #[cfg(feature = "cookie")]
460    #[inline]
461    pub fn set_cookie(&mut self, cookie: &Cookie<'_>) {
462        self.insert_header("set-cookie", cookie.to_string());
463    }
464
465    /// Clears a cookie for the given name.
466    #[cfg(feature = "cookie")]
467    #[inline]
468    pub fn clear_cookie(&mut self, name: impl Into<SharedString>) {
469        let cookie = Cookie::build((name, ""))
470            .path("/")
471            .http_only(true)
472            .secure(true)
473            .same_site(SameSite::Lax)
474            .removal()
475            .build();
476        self.insert_header("set-cookie", cookie.to_string());
477    }
478
479    /// Records a server timing metric entry.
480    pub fn record_server_timing(
481        &mut self,
482        name: impl Into<SharedString>,
483        description: impl Into<Option<SharedString>>,
484        duration: impl Into<Option<Duration>>,
485    ) {
486        fn inner<S: ResponseCode>(
487            res: &mut Response<S>,
488            name: SharedString,
489            description: Option<SharedString>,
490            duration: Option<Duration>,
491        ) {
492            let metric = TimingMetric::new(name, description, duration);
493            res.server_timing.push(metric);
494        }
495        inner::<S>(self, name.into(), description.into(), duration.into())
496    }
497
498    /// Inserts a custom header.
499    #[inline]
500    pub fn insert_header(&mut self, name: &'static str, value: impl ToString) {
501        self.headers
502            .insert(HeaderName::from_static(name), value.to_string());
503    }
504
505    /// Gets a custome header with the given name.
506    #[inline]
507    pub fn get_header(&self, name: &str) -> Option<&str> {
508        self.headers
509            .iter()
510            .find_map(|(key, value)| (key == name).then_some(value.as_str()))
511    }
512
513    /// Returns the status code as `u16`.
514    #[inline]
515    pub fn status_code(&self) -> u16 {
516        self.status_code
517    }
518
519    /// Returns the optional application code.
520    #[inline]
521    pub fn app_code(&self) -> Option<i32> {
522        self.app_code
523    }
524
525    /// Returns `true` if the response is successful or `false` otherwise.
526    #[inline]
527    pub fn is_success(&self) -> bool {
528        self.success
529    }
530
531    /// Returns `true` if the response has a request context.
532    #[inline]
533    pub fn has_context(&self) -> bool {
534        self.trace_context.is_some() && !self.request_id.is_nil()
535    }
536
537    /// Returns the message.
538    #[inline]
539    pub fn message(&self) -> Option<&str> {
540        self.detail
541            .as_ref()
542            .or(self.message.as_ref())
543            .map(|s| s.as_ref())
544    }
545
546    /// Returns the request ID.
547    #[inline]
548    pub fn request_id(&self) -> Uuid {
549        self.request_id
550    }
551
552    /// Returns the trace ID.
553    #[inline]
554    pub fn trace_id(&self) -> Uuid {
555        if let Some(ref trace_context) = self.trace_context {
556            Uuid::from_u128(trace_context.trace_id())
557        } else {
558            Uuid::nil()
559        }
560    }
561
562    /// Returns the content type.
563    #[inline]
564    pub fn content_type(&self) -> &str {
565        self.content_type.as_deref().unwrap_or_else(|| {
566            if !self.bytes_data.is_empty() {
567                "application/octet-stream"
568            } else if self.is_success() {
569                "application/json; charset=utf-8"
570            } else {
571                "application/problem+json; charset=utf-8"
572            }
573        })
574    }
575
576    /// Returns a reference to the custom headers.
577    #[inline]
578    pub fn headers(&self) -> &HeaderMap<String> {
579        &self.headers
580    }
581
582    /// Returns a mutable reference to the custom headers.
583    #[inline]
584    pub fn headers_mut(&mut self) -> &mut HeaderMap<String> {
585        &mut self.headers
586    }
587
588    /// Returns the trace context in the form `(traceparent, tracestate)`.
589    pub fn trace_context(&self) -> (String, String) {
590        if let Some(ref trace_context) = self.trace_context {
591            (trace_context.traceparent(), trace_context.tracestate())
592        } else {
593            let mut trace_context = TraceContext::new();
594            trace_context.record_trace_state();
595            (trace_context.traceparent(), trace_context.tracestate())
596        }
597    }
598
599    /// Returns the server timing.
600    #[inline]
601    pub fn server_timing(&self) -> String {
602        self.server_timing.to_string()
603    }
604
605    /// Reads the response into a byte buffer.
606    pub fn read_bytes(&mut self) -> Result<Bytes, Error> {
607        let has_bytes_data = !self.bytes_data.is_empty();
608        let has_json_data = !self.json_data.is_null();
609        let bytes_opt = if has_bytes_data {
610            Some(mem::take(&mut self.bytes_data))
611        } else if has_json_data {
612            if let Some(transformer) = self.data_transformer.as_ref() {
613                Some(transformer(&self.json_data)?)
614            } else {
615                None
616            }
617        } else {
618            None
619        };
620        if let Some(bytes) = bytes_opt {
621            let etag = EntityTag::from_data(&bytes);
622            self.insert_header("x-etag", etag);
623            return Ok(bytes);
624        }
625
626        let content_type = self.content_type();
627        let (bytes, etag_opt) = if crate::helper::check_json_content_type(content_type) {
628            let (capacity, etag_opt) = if has_json_data {
629                let data = serde_json::to_vec(&self.json_data)?;
630                let etag = EntityTag::from_data(&data);
631                (data.len() + 128, Some(etag))
632            } else {
633                (128, None)
634            };
635            let mut bytes = Vec::with_capacity(capacity);
636            serde_json::to_writer(&mut bytes, &self)?;
637            (bytes, etag_opt)
638        } else if has_json_data {
639            let bytes = if content_type.starts_with("text/csv") {
640                self.json_data.to_csv(Vec::new())?
641            } else if content_type.starts_with("application/jsonlines") {
642                self.json_data.to_jsonlines(Vec::new())?
643            } else {
644                let text = if let JsonValue::String(s) = &mut self.json_data {
645                    mem::take(s)
646                } else {
647                    self.json_data.to_string()
648                };
649                text.into_bytes()
650            };
651            (bytes, None)
652        } else {
653            (Vec::new(), None)
654        };
655        let etag = etag_opt.unwrap_or_else(|| EntityTag::from_data(&bytes));
656        self.insert_header("x-etag", etag);
657        Ok(bytes.into())
658    }
659
660    /// Gets the response time.
661    ///
662    /// # Note
663    ///
664    /// It should only be called when the response will finish.
665    pub fn response_time(&self) -> Duration {
666        let start_time = self.start_time;
667        #[cfg(feature = "metrics")]
668        {
669            let labels = [("status_code", self.status_code().to_string())];
670            metrics::gauge!("zino_http_requests_in_flight").decrement(1.0);
671            metrics::counter!("zino_http_responses_total", &labels).increment(1);
672            metrics::histogram!("zino_http_requests_duration_seconds", &labels,)
673                .record(start_time.elapsed().as_secs_f64());
674        }
675        start_time.elapsed()
676    }
677
678    /// Sends a file to the client.
679    pub fn send_file(&mut self, file: NamedFile) {
680        let mut displayed_inline = false;
681        if let Some(content_type) = file.content_type() {
682            displayed_inline = helper::displayed_inline(content_type);
683            self.set_content_type(content_type.to_string());
684        }
685        if !displayed_inline && let Some(file_name) = file.file_name() {
686            self.insert_header(
687                "content-disposition",
688                format!(r#"attachment; filename="{file_name}""#),
689            );
690        }
691        self.insert_header("etag", file.etag());
692        self.set_bytes_data(Bytes::from(file));
693    }
694
695    /// Sends an Inertia page to the client.
696    #[cfg(feature = "inertia")]
697    pub fn send_inertia_page(&mut self, mut page: InertiaPage) {
698        if page.version().is_empty() {
699            page.set_version(zino_core::datetime::DateTime::current_timestamp().to_string());
700        }
701        self.insert_header("vary", "x-inertia");
702        self.insert_header("x-inertia", true);
703        if let Some(url) = page.redirect_url() {
704            self.insert_header("x-inertia-location", url);
705        } else {
706            self.set_json_response(page.into_json_response());
707        }
708    }
709
710    /// Emits the response for the request.
711    pub fn emit<Ctx: RequestContext>(mut self, ctx: &Ctx) -> Self {
712        if ctx
713            .get_header("prefer")
714            .is_some_and(|s| s.split(';').any(|p| p.trim() == "return=data-only"))
715        {
716            self.set_data_transformer(|data| Ok(serde_json::to_vec(&data)?.into()));
717        }
718        #[cfg(feature = "inertia")]
719        if self.get_header("x-inertia").is_none()
720            && ctx.get_header("x-inertia-partial-component").is_some()
721        {
722            match InertiaPage::partial_reload(ctx) {
723                Ok(mut page) => {
724                    if let JsonValue::Object(data) = &mut self.json_data {
725                        page.append_props(data);
726                    }
727                    self.send_inertia_page(page);
728                }
729                Err(err) => self.set_error_message(err),
730            }
731        }
732        self
733    }
734
735    /// Consumes `self` and returns the custom headers.
736    pub fn finalize(mut self) -> HeaderMap<String> {
737        let request_id = self.request_id();
738        if !request_id.is_nil() {
739            self.insert_header("x-request-id", request_id.to_string());
740        }
741
742        let (traceparent, tracestate) = self.trace_context();
743        self.insert_header("traceparent", traceparent);
744        self.insert_header("tracestate", tracestate);
745
746        let duration = self.response_time();
747        self.record_server_timing("total", None, Some(duration));
748        self.insert_header("server-timing", self.server_timing());
749        self.headers
750    }
751}
752
753impl Response<StatusCode> {
754    /// Constructs a new response with status `200 OK`.
755    #[inline]
756    pub fn ok() -> Self {
757        Response::new(StatusCode::OK)
758    }
759
760    /// Constructs a new response with status `201 Created`.
761    #[inline]
762    pub fn created() -> Self {
763        Response::new(StatusCode::CREATED)
764    }
765
766    /// Constructs a new response with status `303 See Other`.
767    #[inline]
768    pub fn redirect(uri: &str) -> Self {
769        let mut res = Response::new(StatusCode::SEE_OTHER);
770        res.insert_header("location", uri);
771        res
772    }
773
774    /// Constructs a new response with status `307 Temporary Redirect`.
775    #[inline]
776    pub fn temporary_redirect(uri: &str) -> Self {
777        let mut res = Response::new(StatusCode::TEMPORARY_REDIRECT);
778        res.insert_header("location", uri);
779        res
780    }
781
782    /// Constructs a new response with status `308 Permanent Redirect`.
783    #[inline]
784    pub fn permanent_redirect(uri: &str) -> Self {
785        let mut res = Response::new(StatusCode::PERMANENT_REDIRECT);
786        res.insert_header("location", uri);
787        res
788    }
789
790    /// Constructs a new response with status `400 Bad Request`.
791    #[inline]
792    pub fn bad_request() -> Self {
793        Response::new(StatusCode::BAD_REQUEST)
794    }
795
796    /// Constructs a new response with status `401 Unauthorized`.
797    #[inline]
798    pub fn unauthorized() -> Self {
799        Response::new(StatusCode::UNAUTHORIZED)
800    }
801
802    /// Constructs a new response with status `403 Forbidden`.
803    #[inline]
804    pub fn forbidden() -> Self {
805        Response::new(StatusCode::FORBIDDEN)
806    }
807
808    /// Constructs a new response with status `404 Not Found`.
809    #[inline]
810    pub fn not_found() -> Self {
811        Response::new(StatusCode::NOT_FOUND)
812    }
813
814    /// Constructs a new response with status `405 Method Not Allowed`.
815    #[inline]
816    pub fn method_not_allowed() -> Self {
817        Response::new(StatusCode::METHOD_NOT_ALLOWED)
818    }
819
820    /// Constructs a new response with status `409 Conflict`.
821    #[inline]
822    pub fn conflict() -> Self {
823        Response::new(StatusCode::CONFLICT)
824    }
825
826    /// Constructs a new response with status `500 Internal Server Error`.
827    #[inline]
828    pub fn internal_server_error() -> Self {
829        Response::new(StatusCode::INTERNAL_SERVER_ERROR)
830    }
831
832    /// Constructs a new response with status `503 Service Unavailable`.
833    #[inline]
834    pub fn service_unavailable() -> Self {
835        Response::new(StatusCode::SERVICE_UNAVAILABLE)
836    }
837}
838
839impl<S: ResponseCode> Default for Response<S> {
840    #[inline]
841    fn default() -> Self {
842        Self::new(S::OK)
843    }
844}
845
846impl<S: ResponseCode> From<Validation> for Response<S> {
847    fn from(validation: Validation) -> Self {
848        if validation.is_success() {
849            Self::new(S::OK)
850        } else {
851            let mut res = Self::new(S::BAD_REQUEST);
852            res.set_validation_data(validation);
853            res
854        }
855    }
856}