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;
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(|mut value| {
229                if let Some(data) = value.as_object_mut() {
230                    let mut map = zino_core::Map::new();
231                    map.append(data);
232                    crate::view::render(template_name, map)
233                } else {
234                    Err(zino_core::warn!("invalid template data"))
235                }
236            });
237        match result {
238            Ok(content) => {
239                self.json_data = content.into();
240                self.bytes_data = Bytes::new();
241                self.content_type = Some("text/html; charset=utf-8".into());
242            }
243            Err(err) => {
244                let code = S::INTERNAL_SERVER_ERROR;
245                self.type_uri = code.type_uri();
246                self.title = code.title();
247                self.status_code = code.status_code();
248                self.success = false;
249                self.detail = Some(err.to_string().into());
250                self.json_data = JsonValue::Null;
251                self.bytes_data = Bytes::new();
252            }
253        }
254        self
255    }
256
257    /// Sets the response code.
258    pub fn set_code(&mut self, code: S) {
259        let success = code.is_success();
260        let message = code.message();
261        self.type_uri = code.type_uri();
262        self.title = code.title();
263        self.status_code = code.status_code();
264        self.success = success;
265        if success {
266            self.detail = None;
267            self.message = message;
268        } else {
269            self.detail = message;
270            self.message = None;
271        }
272    }
273
274    /// Sets the status code.
275    #[inline]
276    pub fn set_status_code(&mut self, status_code: impl Into<u16>) {
277        self.status_code = status_code.into();
278    }
279
280    /// Sets the application code.
281    #[inline]
282    pub fn set_app_code<C: ApplicationCode>(&mut self, app_code: &C) {
283        self.app_code = Some(app_code.code());
284        self.message = Some(app_code.message());
285    }
286
287    /// Sets a URI reference that identifies the specific occurrence of the problem.
288    #[inline]
289    pub fn set_instance(&mut self, instance: impl Into<SharedString>) {
290        self.instance = Some(instance.into());
291    }
292
293    /// Sets the message. If the response is not successful,
294    /// it should be a human-readable explanation specific to this occurrence of the problem.
295    pub fn set_message(&mut self, message: impl Into<SharedString>) {
296        fn inner<S: ResponseCode>(res: &mut Response<S>, message: SharedString) {
297            if res.is_success() {
298                res.detail = None;
299                res.message = Some(message);
300            } else {
301                res.detail = Some(message);
302                res.message = None;
303            }
304        }
305        inner::<S>(self, message.into())
306    }
307
308    /// Sets the error message.
309    pub fn set_error_message(&mut self, error: impl Into<Error>) {
310        fn inner<S: ResponseCode>(res: &mut Response<S>, error: Error) {
311            let message = error.to_string().into();
312            if res.is_success() {
313                res.detail = None;
314                res.message = Some(message);
315            } else {
316                res.detail = Some(message);
317                res.message = None;
318            }
319        }
320        inner::<S>(self, error.into())
321    }
322
323    /// Sets the response data.
324    #[inline]
325    pub fn set_data<T: Serialize>(&mut self, data: &T) {
326        match serde_json::to_value(data) {
327            Ok(value) => {
328                self.json_data = value;
329                self.bytes_data = Bytes::new();
330            }
331            Err(err) => self.set_error_message(err),
332        }
333    }
334
335    /// Sets the JSON data.
336    #[inline]
337    pub fn set_json_data(&mut self, data: impl Into<JsonValue>) {
338        self.json_data = data.into();
339        self.bytes_data = Bytes::new();
340    }
341
342    /// Sets the bytes data.
343    #[inline]
344    pub fn set_bytes_data(&mut self, data: impl Into<Bytes>) {
345        self.json_data = JsonValue::Null;
346        self.bytes_data = data.into();
347    }
348
349    /// Sets the response data for the validation.
350    #[inline]
351    pub fn set_validation_data(&mut self, validation: Validation) {
352        self.json_data = validation.into_map().into();
353        self.bytes_data = Bytes::new();
354    }
355
356    /// Sets a transformer for the response data.
357    #[inline]
358    pub fn set_data_transformer(&mut self, transformer: DataTransformer) {
359        self.data_transformer = Some(transformer);
360    }
361
362    /// Sets the content type.
363    ///
364    /// # Note
365    ///
366    /// Currently, we have built-in support for the following values:
367    ///
368    /// - `application/json`
369    /// - `application/jsonlines`
370    /// - `application/octet-stream`
371    /// - `application/problem+json`
372    /// - `application/x-www-form-urlencoded`
373    /// - `text/csv`
374    /// - `text/html`
375    /// - `text/plain`
376    #[inline]
377    pub fn set_content_type(&mut self, content_type: impl Into<SharedString>) {
378        self.content_type = Some(content_type.into());
379    }
380
381    /// Sets the form data as the response body.
382    #[inline]
383    pub fn set_form_response(&mut self, data: impl Into<JsonValue>) {
384        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
385            res.set_json_data(data);
386            res.set_content_type("application/x-www-form-urlencoded");
387            res.set_data_transformer(|data| {
388                let mut bytes = Vec::new();
389                serde_qs::to_writer(&data, &mut bytes)?;
390                Ok(bytes.into())
391            });
392        }
393        inner::<S>(self, data.into())
394    }
395
396    /// Sets the JSON data as the response body.
397    #[inline]
398    pub fn set_json_response(&mut self, data: impl Into<JsonValue>) {
399        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
400            res.set_json_data(data);
401            res.set_data_transformer(|data| Ok(serde_json::to_vec(&data)?.into()));
402        }
403        inner::<S>(self, data.into())
404    }
405
406    /// Sets the JSON Lines data as the response body.
407    #[inline]
408    pub fn set_jsonlines_response(&mut self, data: impl Into<JsonValue>) {
409        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
410            res.set_json_data(data);
411            res.set_content_type("application/jsonlines; charset=utf-8");
412            res.set_data_transformer(|data| Ok(data.to_jsonlines(Vec::new())?.into()));
413        }
414        inner::<S>(self, data.into())
415    }
416
417    /// Sets the CSV data as the response body.
418    #[inline]
419    pub fn set_csv_response(&mut self, data: impl Into<JsonValue>) {
420        fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
421            res.set_json_data(data);
422            res.set_content_type("text/csv; charset=utf-8");
423            res.set_data_transformer(|data| Ok(data.to_csv(Vec::new())?.into()));
424        }
425        inner::<S>(self, data.into())
426    }
427
428    /// Sets the plain text as the response body.
429    #[inline]
430    pub fn set_text_response(&mut self, data: impl Into<String>) {
431        self.set_json_data(data.into());
432        self.set_content_type("text/plain; charset=utf-8");
433    }
434
435    /// Sets the bytes data as the response body.
436    #[inline]
437    pub fn set_bytes_response(&mut self, data: impl Into<Bytes>) {
438        self.set_bytes_data(data);
439        self.set_content_type("application/octet-stream");
440    }
441
442    /// Sets the request ID.
443    #[inline]
444    pub(crate) fn set_request_id(&mut self, request_id: Uuid) {
445        self.request_id = request_id;
446    }
447
448    /// Sets the trace context from headers.
449    #[inline]
450    pub(crate) fn set_trace_context(&mut self, trace_context: Option<TraceContext>) {
451        self.trace_context = trace_context;
452    }
453
454    /// Sets the start time.
455    #[inline]
456    pub(crate) fn set_start_time(&mut self, start_time: Instant) {
457        self.start_time = start_time;
458    }
459
460    /// Sends a cookie to the user agent.
461    #[cfg(feature = "cookie")]
462    #[inline]
463    pub fn set_cookie(&mut self, cookie: &Cookie<'_>) {
464        self.insert_header("set-cookie", cookie.to_string());
465    }
466
467    /// Records a server timing metric entry.
468    pub fn record_server_timing(
469        &mut self,
470        name: impl Into<SharedString>,
471        description: impl Into<Option<SharedString>>,
472        duration: impl Into<Option<Duration>>,
473    ) {
474        fn inner<S: ResponseCode>(
475            res: &mut Response<S>,
476            name: SharedString,
477            description: Option<SharedString>,
478            duration: Option<Duration>,
479        ) {
480            let metric = TimingMetric::new(name, description, duration);
481            res.server_timing.push(metric);
482        }
483        inner::<S>(self, name.into(), description.into(), duration.into())
484    }
485
486    /// Inserts a custom header.
487    #[inline]
488    pub fn insert_header(&mut self, name: &'static str, value: impl ToString) {
489        self.headers
490            .insert(HeaderName::from_static(name), value.to_string());
491    }
492
493    /// Gets a custome header with the given name.
494    #[inline]
495    pub fn get_header(&self, name: &str) -> Option<&str> {
496        self.headers
497            .iter()
498            .find_map(|(key, value)| (key == name).then_some(value.as_str()))
499    }
500
501    /// Returns the status code as `u16`.
502    #[inline]
503    pub fn status_code(&self) -> u16 {
504        self.status_code
505    }
506
507    /// Returns the optional application code.
508    #[inline]
509    pub fn app_code(&self) -> Option<i32> {
510        self.app_code
511    }
512
513    /// Returns `true` if the response is successful or `false` otherwise.
514    #[inline]
515    pub fn is_success(&self) -> bool {
516        self.success
517    }
518
519    /// Returns `true` if the response has a request context.
520    #[inline]
521    pub fn has_context(&self) -> bool {
522        self.trace_context.is_some() && !self.request_id.is_nil()
523    }
524
525    /// Returns the message.
526    #[inline]
527    pub fn message(&self) -> Option<&str> {
528        self.detail
529            .as_ref()
530            .or(self.message.as_ref())
531            .map(|s| s.as_ref())
532    }
533
534    /// Returns the request ID.
535    #[inline]
536    pub fn request_id(&self) -> Uuid {
537        self.request_id
538    }
539
540    /// Returns the trace ID.
541    #[inline]
542    pub fn trace_id(&self) -> Uuid {
543        if let Some(ref trace_context) = self.trace_context {
544            Uuid::from_u128(trace_context.trace_id())
545        } else {
546            Uuid::nil()
547        }
548    }
549
550    /// Returns the content type.
551    #[inline]
552    pub fn content_type(&self) -> &str {
553        self.content_type.as_deref().unwrap_or_else(|| {
554            if !self.bytes_data.is_empty() {
555                "application/octet-stream"
556            } else if self.is_success() {
557                "application/json; charset=utf-8"
558            } else {
559                "application/problem+json; charset=utf-8"
560            }
561        })
562    }
563
564    /// Returns a reference to the custom headers.
565    #[inline]
566    pub fn headers(&self) -> &HeaderMap<String> {
567        &self.headers
568    }
569
570    /// Returns a mutable reference to the custom headers.
571    #[inline]
572    pub fn headers_mut(&mut self) -> &mut HeaderMap<String> {
573        &mut self.headers
574    }
575
576    /// Returns the trace context in the form `(traceparent, tracestate)`.
577    pub fn trace_context(&self) -> (String, String) {
578        if let Some(ref trace_context) = self.trace_context {
579            (trace_context.traceparent(), trace_context.tracestate())
580        } else {
581            let mut trace_context = TraceContext::new();
582            trace_context.record_trace_state();
583            (trace_context.traceparent(), trace_context.tracestate())
584        }
585    }
586
587    /// Returns the server timing.
588    #[inline]
589    pub fn server_timing(&self) -> String {
590        self.server_timing.to_string()
591    }
592
593    /// Reads the response into a byte buffer.
594    pub fn read_bytes(&mut self) -> Result<Bytes, Error> {
595        let has_bytes_data = !self.bytes_data.is_empty();
596        let has_json_data = !self.json_data.is_null();
597        let bytes_opt = if has_bytes_data {
598            Some(mem::take(&mut self.bytes_data))
599        } else if has_json_data {
600            if let Some(transformer) = self.data_transformer.as_ref() {
601                Some(transformer(&self.json_data)?)
602            } else {
603                None
604            }
605        } else {
606            None
607        };
608        if let Some(bytes) = bytes_opt {
609            let etag = EntityTag::from_data(&bytes);
610            self.insert_header("x-etag", etag);
611            return Ok(bytes);
612        }
613
614        let content_type = self.content_type();
615        let (bytes, etag_opt) = if crate::helper::check_json_content_type(content_type) {
616            let (capacity, etag_opt) = if has_json_data {
617                let data = serde_json::to_vec(&self.json_data)?;
618                let etag = EntityTag::from_data(&data);
619                (data.len() + 128, Some(etag))
620            } else {
621                (128, None)
622            };
623            let mut bytes = Vec::with_capacity(capacity);
624            serde_json::to_writer(&mut bytes, &self)?;
625            (bytes, etag_opt)
626        } else if has_json_data {
627            let bytes = if content_type.starts_with("text/csv") {
628                self.json_data.to_csv(Vec::new())?
629            } else if content_type.starts_with("application/jsonlines") {
630                self.json_data.to_jsonlines(Vec::new())?
631            } else {
632                let text = if let JsonValue::String(s) = &mut self.json_data {
633                    mem::take(s)
634                } else {
635                    self.json_data.to_string()
636                };
637                text.into_bytes()
638            };
639            (bytes, None)
640        } else {
641            (Vec::new(), None)
642        };
643        let etag = etag_opt.unwrap_or_else(|| EntityTag::from_data(&bytes));
644        self.insert_header("x-etag", etag);
645        Ok(bytes.into())
646    }
647
648    /// Gets the response time.
649    ///
650    /// # Note
651    ///
652    /// It should only be called when the response will finish.
653    pub fn response_time(&self) -> Duration {
654        let start_time = self.start_time;
655        #[cfg(feature = "metrics")]
656        {
657            let labels = [("status_code", self.status_code().to_string())];
658            metrics::gauge!("zino_http_requests_in_flight").decrement(1.0);
659            metrics::counter!("zino_http_responses_total", &labels).increment(1);
660            metrics::histogram!("zino_http_requests_duration_seconds", &labels,)
661                .record(start_time.elapsed().as_secs_f64());
662        }
663        start_time.elapsed()
664    }
665
666    /// Sends a file to the client.
667    pub fn send_file(&mut self, file: NamedFile) {
668        let mut displayed_inline = false;
669        if let Some(content_type) = file.content_type() {
670            displayed_inline = helper::displayed_inline(content_type);
671            self.set_content_type(content_type.to_string());
672        }
673        if !displayed_inline {
674            if let Some(file_name) = file.file_name() {
675                self.insert_header(
676                    "content-disposition",
677                    format!(r#"attachment; filename="{file_name}""#),
678                );
679            }
680        }
681        self.insert_header("etag", file.etag());
682        self.set_bytes_data(Bytes::from(file));
683    }
684
685    /// Sends an Inertia page to the client.
686    #[cfg(feature = "inertia")]
687    pub fn send_inertia_page(&mut self, mut page: InertiaPage) {
688        if page.version().is_empty() {
689            page.set_version(zino_core::datetime::DateTime::current_timestamp().to_string());
690        }
691        self.insert_header("vary", "x-inertia");
692        self.insert_header("x-insertia", true);
693        if let Some(url) = page.redirect_url() {
694            self.insert_header("x-inertia-location", url);
695        } else {
696            self.set_json_response(page.into_json_response());
697        }
698    }
699
700    /// Emits the response for the request.
701    pub fn emit<Ctx: RequestContext>(mut self, ctx: &Ctx) -> Self {
702        if ctx
703            .get_header("prefer")
704            .is_some_and(|s| s.split(';').any(|p| p.trim() == "return=data-only"))
705        {
706            self.set_data_transformer(|data| Ok(serde_json::to_vec(&data)?.into()));
707        }
708        #[cfg(feature = "inertia")]
709        if self.get_header("x-inertia").is_none()
710            && ctx.get_header("x-inertia-partial-component").is_some()
711        {
712            match InertiaPage::partial_reload(ctx) {
713                Ok(mut page) => {
714                    if let JsonValue::Object(data) = &mut self.json_data {
715                        page.append_props(data);
716                    }
717                    self.send_inertia_page(page);
718                }
719                Err(err) => self.set_error_message(err),
720            }
721        }
722        self
723    }
724
725    /// Consumes `self` and returns the custom headers.
726    pub fn finalize(mut self) -> HeaderMap<String> {
727        let request_id = self.request_id();
728        if !request_id.is_nil() {
729            self.insert_header("x-request-id", request_id.to_string());
730        }
731
732        let (traceparent, tracestate) = self.trace_context();
733        self.insert_header("traceparent", traceparent);
734        self.insert_header("tracestate", tracestate);
735
736        let duration = self.response_time();
737        self.record_server_timing("total", None, Some(duration));
738        self.insert_header("server-timing", self.server_timing());
739        self.headers
740    }
741}
742
743impl Response<StatusCode> {
744    /// Constructs a new response with status `200 OK`.
745    #[inline]
746    pub fn ok() -> Self {
747        Response::new(StatusCode::OK)
748    }
749
750    /// Constructs a new response with status `201 Created`.
751    #[inline]
752    pub fn created() -> Self {
753        Response::new(StatusCode::CREATED)
754    }
755
756    /// Constructs a new response with status `400 Bad Request`.
757    #[inline]
758    pub fn bad_request() -> Self {
759        Response::new(StatusCode::BAD_REQUEST)
760    }
761
762    /// Constructs a new response with status `401 Unauthorized`.
763    #[inline]
764    pub fn unauthorized() -> Self {
765        Response::new(StatusCode::UNAUTHORIZED)
766    }
767
768    /// Constructs a new response with status `403 Forbidden`.
769    #[inline]
770    pub fn forbidden() -> Self {
771        Response::new(StatusCode::FORBIDDEN)
772    }
773
774    /// Constructs a new response with status `404 Not Found`.
775    #[inline]
776    pub fn not_found() -> Self {
777        Response::new(StatusCode::NOT_FOUND)
778    }
779
780    /// Constructs a new response with status `405 Method Not Allowed`.
781    #[inline]
782    pub fn method_not_allowed() -> Self {
783        Response::new(StatusCode::METHOD_NOT_ALLOWED)
784    }
785
786    /// Constructs a new response with status `409 Conflict`.
787    #[inline]
788    pub fn conflict() -> Self {
789        Response::new(StatusCode::CONFLICT)
790    }
791
792    /// Constructs a new response with status `500 Internal Server Error`.
793    #[inline]
794    pub fn internal_server_error() -> Self {
795        Response::new(StatusCode::INTERNAL_SERVER_ERROR)
796    }
797
798    /// Constructs a new response with status `503 Service Unavailable`.
799    #[inline]
800    pub fn service_unavailable() -> Self {
801        Response::new(StatusCode::SERVICE_UNAVAILABLE)
802    }
803}
804
805impl<S: ResponseCode> Default for Response<S> {
806    #[inline]
807    fn default() -> Self {
808        Self::new(S::OK)
809    }
810}
811
812impl<S: ResponseCode> From<Validation> for Response<S> {
813    fn from(validation: Validation) -> Self {
814        if validation.is_success() {
815            Self::new(S::OK)
816        } else {
817            let mut res = Self::new(S::BAD_REQUEST);
818            res.set_validation_data(validation);
819            res
820        }
821    }
822}