3. Response و Error Handling
چه typeهایی میتوانند response باشند؟
پاسخ بلند
هر نوعی که IntoResponse را پیادهسازی کرده باشد میتواند از یک handler برگردد. Axum برای انواع رایج (String, &'static str, StatusCode, http::Response<Body>, Json<T>، tupleهایی مثل (StatusCode, HeaderMap, body) و غیره) پیادهسازی آماده دارد، بنابراین معمولاً میتوانید مستقیم یک مقدار ساده یا یک tuple برگردانید و Axum آن را به Response تبدیل میکند. اگر نوع دلخواه شما این trait را نداشته باشد، میتوانید آن را خودتان پیادهسازی کنید تا مستقیماً از handler قابل برگشت باشد. :contentReference[oaicite:0]{index=0}
پاسخ کوتاه
هر چیزی که IntoResponse را پیادهسازی کند — Axum برای بسیاری از انواع رایج پیادهسازی آماده دارد. :contentReference[oaicite:1]{index=1}
trait IntoResponse چگونه کار میکند؟
پاسخ بلند
IntoResponse متدی into_response(self) -> Response فراهم میکند که نوع حامل را به یک http::Response<Body> تبدیل میکند. وقتی handler impl IntoResponse یا نوعی که IntoResponse را پیادهسازی کرده برمیگرداند، Axum آن را فراخوانی میکند تا پاسخ HTTP واقعی ساخته شود. این سازوکار امکان انعطافپذیری بالا (مثلاً بازگرداندن tuple برای تعیین status/headers/body) و یکپارچگی با انواع کاربردی مانند Json<T> را فراهم میسازد. اغلب لازم نیست خودتان IntoResponse را بنویسید مگر برای error typeهای سفارشی یا رفتارهای ویژه. :contentReference[oaicite:2]{index=2}
پاسخ کوتاه
این trait مقدار را به یک http::Response تبدیل میکند؛ Axum این تبدیل را برای هر خروجی handler انجام میدهد. :contentReference[oaicite:3]{index=3}
چگونه error مرکزی (global error handling) پیاده میکنید؟
پاسخ بلند
الگوهای معمول برای هندلینگ سراسری در Axum:
-
Middleware / Tower layers — از
tower::ServiceBuilderو لِیِرهایی مثلHandleErrorLayerبرای wrap کردن سرویس استفاده کنید تا خطاهای لایهها (مثل timeout یا fallible middleware) را بگیرید و بهIntoResponseتبدیل کنید.HandleErrorLayerبرای تبدیلBoxErrorیا خطاهای لایه به پاسخ HTTP کاربردی است. :contentReference[oaicite:4]{index=4} -
خطاهای handler بهصورت نوعی که
IntoResponseرا پیادهسازی میکند — هر error type سفارشی را طوری طراحی کنید که خودشIntoResponseرا پیادهسازی کند؛ سپس هر کجاResult<T, E>برگردانید، Axum در صورتErr(e)آن را به پاسخ مناسب تبدیل میکند. این الگو خطاها را در لایه application متحد میسازد (مثلاً تبدیل enum خطاها به JSON+status). :contentReference[oaicite:5]{index=5} -
ترکیب هر دو — middleware برای موارد کلی (logging، تبدیل errorهای لایهای، timeouts) و
IntoResponseبرای mapping دقیق انواع خطای دامنه به status/body.
نکته عملی: middlewareها معمولاً خطاها را به شکل Result یا BoxError برمیگردانند؛ از HandleErrorLayer یا یک layer سفارشی استفاده کنید تا log/metric بگیرید و یک Response یکنواخت بسازید. :contentReference[oaicite:6]{index=6}
پاسخ کوتاه
از ترکیب Tower layers (مثلاً HandleErrorLayer) برای خطاهای لایهای و پیادهسازی IntoResponse روی error typeهای دامنه برای mapping دقیق خطا به status/body استفاده کنید. :contentReference[oaicite:7]{index=7}
تفاوت برگرداندن Result<T, E> و impl IntoResponse چیست؟
پاسخ بلند
-
impl IntoResponse: تابع فقط میتواند یک نوع بازگشتی را داشته باشد؛ اگر بخواهید در شاخههای مختلف انواع متفاوتی برگردانید،impl Traitمحدودیت دارد و گاهی لازم است به صورت صریح یکResponseیا یک common enum برگردانید. علاوه بر این،impl IntoResponseمناسب وقتی است که موفقیت و خطا در یک نوع واحد نمایش داده شوند. :contentReference[oaicite:8]{index=8} -
Result<T, E>: بیان صریحِ احتمال خطاست. Axum برایResultنیز پیادهسازی دارد، اما هر دو شاخه (TوE) بایدIntoResponseرا پیادهسازی کنند تا تبدیل خودکار بهResponseانجام شود. بنابراینResultبه شما اجازه میدهد خطا را جدا نگه دارید و آن را با یکIntoResponseمناسب هندل کنید. اگرEانواع مختلفی داشته باشد، معمولاً یک enum خطا میسازید و برای آنIntoResponseپیادهسازی میکنید. :contentReference[oaicite:9]{index=9}
پاسخ کوتاه
impl IntoResponse برمیگرداند یک نوع قابل تبدیل به پاسخ؛ Result<T, E> صراحتاً خطا را نشان میدهد — ولی برای تبدیل خودکار، هم T و هم E باید IntoResponse را پیادهسازی کنند. :contentReference[oaicite:10]{index=10}
چگونه HTTP status code و body را همزمان کنترل میکنید؟
پاسخ بلند
چند راه معمول:
-
برگرداندن یک tuple مثل
(StatusCode, impl IntoResponse)یا(StatusCode, HeaderMap, body)— Axum این ترکیبها را میشناسد و آنها را بهResponseتبدیل میکند. این سادهترین راه برای تعیین همزمان status و body است. :contentReference[oaicite:11]{index=11} -
ساختن و برگرداندن یک
http::Response<Body>کامل وقتی نیاز به کنترل دقیق headers، extensions یا body streaming دارید. :contentReference[oaicite:12]{index=12} -
در صورت استفاده از
Result<T, E>، نوعEرا طوری پیادهسازی کنید که هنگامErrیک status مناسب و body توضیحی (مثلاً JSON با فیلدهای خطا) تولید کند (یعنیIntoResponseبرایE). :contentReference[oaicite:13]{index=13}
پاسخ کوتاه
برای حالتهای ساده از tuple مثل (StatusCode, body) استفاده کنید؛ برای کنترل کامل از http::Response؛ یا error type را طوری IntoResponse کنید که status+body مناسب تولید کند. :contentReference[oaicite:14]{index=14}
بهترین الگو برای error type در Axum چیست؟
پاسخ بلند
الگوی رایج و مؤثر:
- یک
enumخطا در سطح application که تمام خطاهای منطقی را پوشش میدهد (مثلاًenum AppError { NotFound, Validation(ValidationError), Auth(AuthError), Internal(anyhow::Error) }). - از crates مثل
thiserrorبرای derive کردنErrorو متدهای مفید استفاده کنید. - برای
AppErrorیکimpl IntoResponseبنویسید که status مناسب را تعیین و body (معمولاًJson<{ code, message, details }>) را برمیگرداند. این کار موجب میشود هر handler بتواندResult<T, AppError>برگرداند و mapping سراسری خطاها ساده و یکنواخت باشد. - از logging/observability در نقطهٔ central (مثلاً در
IntoResponseبرایInternalیا در یک middleware) استفاده کنید تا stack/context لاگ شود اما پاسخ به کاربر امن بماند.
این الگو ترکیب type-safety، خوانایی و امکان unit/integration testing را فراهم میکند و با اکوسیستم Axum/Tower بهخوبی کار میکند. :contentReference[oaicite:15]{index=15}
پاسخ کوتاه
یک enum مرکزی برای خطاها + thiserror برای derive + یک impl IntoResponse برای mapping به StatusCode و JSON body. لاگینگ و تبدیل خطاهای داخلی در middleware یا داخل IntoResponse انجام شود. :contentReference[oaicite:16]{index=16}