1use crate::response::ApiResponse;
25use axum::http::StatusCode;
26use axum::response::{IntoResponse, Response};
27use axum::Router;
28use serde_json::json;
29
30pub async fn not_found_handler() -> Response {
34 let resp = ApiResponse::error("Not Found");
35 let body = resp.to_json_string();
36 (
37 StatusCode::NOT_FOUND,
38 [(
39 axum::http::header::CONTENT_TYPE,
40 "application/json; charset=utf-8",
41 )],
42 body,
43 )
44 .into_response()
45}
46
47pub async fn internal_error_handler() -> Response {
49 let resp = ApiResponse::error("Internal Error");
50 let body = resp.to_json_string();
51 (
52 StatusCode::INTERNAL_SERVER_ERROR,
53 [(
54 axum::http::header::CONTENT_TYPE,
55 "application/json; charset=utf-8",
56 )],
57 body,
58 )
59 .into_response()
60}
61
62pub async fn method_not_allowed_handler() -> Response {
64 let resp = ApiResponse::error("Method Not Allowed");
65 let body = resp.to_json_string();
66 (
67 StatusCode::METHOD_NOT_ALLOWED,
68 [(
69 axum::http::header::CONTENT_TYPE,
70 "application/json; charset=utf-8",
71 )],
72 body,
73 )
74 .into_response()
75}
76
77pub async fn bad_request_handler() -> Response {
79 let resp = ApiResponse::error("Bad Request");
80 let body = resp.to_json_string();
81 (
82 StatusCode::BAD_REQUEST,
83 [(
84 axum::http::header::CONTENT_TYPE,
85 "application/json; charset=utf-8",
86 )],
87 body,
88 )
89 .into_response()
90}
91
92pub async fn unauthorized_handler() -> Response {
96 let resp = ApiResponse::error_with_code(-1, "Unauthorized", json!({}));
97 let body = resp.to_json_string();
98 (
99 StatusCode::UNAUTHORIZED,
100 [(
101 axum::http::header::CONTENT_TYPE,
102 "application/json; charset=utf-8",
103 )],
104 body,
105 )
106 .into_response()
107}
108
109pub async fn forbidden_handler() -> Response {
111 let resp = ApiResponse::error("Forbidden");
112 let body = resp.to_json_string();
113 (
114 StatusCode::FORBIDDEN,
115 [(
116 axum::http::header::CONTENT_TYPE,
117 "application/json; charset=utf-8",
118 )],
119 body,
120 )
121 .into_response()
122}
123
124pub async fn unprocessable_entity_handler() -> Response {
126 let resp = ApiResponse::error("Unprocessable Entity");
127 let body = resp.to_json_string();
128 (
129 StatusCode::UNPROCESSABLE_ENTITY,
130 [(
131 axum::http::header::CONTENT_TYPE,
132 "application/json; charset=utf-8",
133 )],
134 body,
135 )
136 .into_response()
137}
138
139pub fn error_response(status: StatusCode, msg: &str) -> Response {
141 let resp = ApiResponse::error(msg);
142 let body = resp.to_json_string();
143 (
144 status,
145 [(
146 axum::http::header::CONTENT_TYPE,
147 "application/json; charset=utf-8",
148 )],
149 body,
150 )
151 .into_response()
152}
153
154pub fn error_response_with_code(status: StatusCode, code: i32, msg: &str) -> Response {
156 let resp = ApiResponse::error_with_code(code, msg, json!({}));
157 let body = resp.to_json_string();
158 (
159 status,
160 [(
161 axum::http::header::CONTENT_TYPE,
162 "application/json; charset=utf-8",
163 )],
164 body,
165 )
166 .into_response()
167}
168
169#[derive(Debug, Clone)]
171pub struct HandleError {
172 pub status: StatusCode,
174 pub code: i32,
176 pub msg: String,
178}
179
180impl HandleError {
181 pub fn new(status: StatusCode, code: i32, msg: impl Into<String>) -> Self {
183 Self {
184 status,
185 code,
186 msg: msg.into(),
187 }
188 }
189
190 pub fn not_found(msg: impl Into<String>) -> Self {
192 Self::new(StatusCode::NOT_FOUND, 0, msg)
193 }
194
195 pub fn internal(msg: impl Into<String>) -> Self {
197 Self::new(StatusCode::INTERNAL_SERVER_ERROR, 0, msg)
198 }
199
200 pub fn bad_request(msg: impl Into<String>) -> Self {
202 Self::new(StatusCode::BAD_REQUEST, 0, msg)
203 }
204
205 pub fn unauthorized(msg: impl Into<String>) -> Self {
207 Self::new(StatusCode::UNAUTHORIZED, -1, msg)
208 }
209
210 pub fn forbidden(msg: impl Into<String>) -> Self {
212 Self::new(StatusCode::FORBIDDEN, 0, msg)
213 }
214}
215
216impl IntoResponse for HandleError {
217 fn into_response(self) -> Response {
218 error_response_with_code(self.status, self.code, &self.msg)
219 }
220}
221
222pub fn error_router() -> Router {
227 Router::new().fallback(not_found_handler)
228}
229
230pub fn fallback_router() -> Router {
232 Router::new().fallback(not_found_handler)
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use axum::body::Body;
239 use axum::http::{Method, Request, StatusCode};
240 use http_body_util::BodyExt;
241 use serde_json::Value;
242 use tower::ServiceExt;
243
244 async fn fetch_json(resp: Response) -> (StatusCode, Value) {
245 let status = resp.status();
246 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
247 let json: Value = serde_json::from_slice(&bytes).unwrap();
248 (status, json)
249 }
250
251 async fn send_get(router: Router, uri: &str) -> Response {
252 let req = Request::builder()
253 .method(Method::GET)
254 .uri(uri)
255 .body(Body::empty())
256 .unwrap();
257 router.oneshot(req).await.unwrap()
258 }
259
260 #[tokio::test]
265 async fn test_not_found_handler_status_code() {
266 let resp = not_found_handler().await;
267 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
268 }
269
270 #[tokio::test]
271 async fn test_not_found_handler_body() {
272 let resp = not_found_handler().await;
273 let (status, json) = fetch_json(resp).await;
274 assert_eq!(status, StatusCode::NOT_FOUND);
275 assert_eq!(json["code"], 0);
276 assert_eq!(json["msg"], "Not Found");
277 assert!(json["data"].is_object());
278 }
279
280 #[tokio::test]
281 async fn test_not_found_handler_content_type() {
282 let resp = not_found_handler().await;
283 assert_eq!(
284 resp.headers().get("content-type").unwrap(),
285 "application/json; charset=utf-8"
286 );
287 }
288
289 #[tokio::test]
294 async fn test_internal_error_handler() {
295 let resp = internal_error_handler().await;
296 let (status, json) = fetch_json(resp).await;
297 assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
298 assert_eq!(json["code"], 0);
299 assert_eq!(json["msg"], "Internal Error");
300 }
301
302 #[tokio::test]
307 async fn test_method_not_allowed_handler() {
308 let resp = method_not_allowed_handler().await;
309 let (status, json) = fetch_json(resp).await;
310 assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
311 assert_eq!(json["code"], 0);
312 assert_eq!(json["msg"], "Method Not Allowed");
313 }
314
315 #[tokio::test]
320 async fn test_bad_request_handler() {
321 let resp = bad_request_handler().await;
322 let (status, json) = fetch_json(resp).await;
323 assert_eq!(status, StatusCode::BAD_REQUEST);
324 assert_eq!(json["code"], 0);
325 assert_eq!(json["msg"], "Bad Request");
326 }
327
328 #[tokio::test]
333 async fn test_unauthorized_handler() {
334 let resp = unauthorized_handler().await;
335 let (status, json) = fetch_json(resp).await;
336 assert_eq!(status, StatusCode::UNAUTHORIZED);
337 assert_eq!(json["code"], -1);
339 assert_eq!(json["msg"], "Unauthorized");
340 }
341
342 #[tokio::test]
347 async fn test_forbidden_handler() {
348 let resp = forbidden_handler().await;
349 let (status, json) = fetch_json(resp).await;
350 assert_eq!(status, StatusCode::FORBIDDEN);
351 assert_eq!(json["code"], 0);
352 assert_eq!(json["msg"], "Forbidden");
353 }
354
355 #[tokio::test]
360 async fn test_unprocessable_entity_handler() {
361 let resp = unprocessable_entity_handler().await;
362 let (status, json) = fetch_json(resp).await;
363 assert_eq!(status, StatusCode::UNPROCESSABLE_ENTITY);
364 assert_eq!(json["code"], 0);
365 assert_eq!(json["msg"], "Unprocessable Entity");
366 }
367
368 #[tokio::test]
373 async fn test_error_response_custom() {
374 let resp = error_response(StatusCode::NOT_FOUND, "资源不存在");
375 let (status, json) = fetch_json(resp).await;
376 assert_eq!(status, StatusCode::NOT_FOUND);
377 assert_eq!(json["code"], 0);
378 assert_eq!(json["msg"], "资源不存在");
379 }
380
381 #[tokio::test]
382 async fn test_error_response_with_code_custom() {
383 let resp = error_response_with_code(StatusCode::BAD_REQUEST, 1001, "参数错误");
384 let (status, json) = fetch_json(resp).await;
385 assert_eq!(status, StatusCode::BAD_REQUEST);
386 assert_eq!(json["code"], 1001);
387 assert_eq!(json["msg"], "参数错误");
388 }
389
390 #[tokio::test]
395 async fn test_handle_error_not_found() {
396 let err = HandleError::not_found("用户不存在");
397 let resp = err.into_response();
398 let (status, json) = fetch_json(resp).await;
399 assert_eq!(status, StatusCode::NOT_FOUND);
400 assert_eq!(json["code"], 0);
401 assert_eq!(json["msg"], "用户不存在");
402 }
403
404 #[tokio::test]
405 async fn test_handle_error_internal() {
406 let err = HandleError::internal("数据库错误");
407 let resp = err.into_response();
408 let (status, json) = fetch_json(resp).await;
409 assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
410 assert_eq!(json["code"], 0);
411 assert_eq!(json["msg"], "数据库错误");
412 }
413
414 #[tokio::test]
415 async fn test_handle_error_bad_request() {
416 let err = HandleError::bad_request("参数错误");
417 let resp = err.into_response();
418 let (status, json) = fetch_json(resp).await;
419 assert_eq!(status, StatusCode::BAD_REQUEST);
420 assert_eq!(json["code"], 0);
421 assert_eq!(json["msg"], "参数错误");
422 }
423
424 #[tokio::test]
425 async fn test_handle_error_unauthorized() {
426 let err = HandleError::unauthorized("请先登录");
427 let resp = err.into_response();
428 let (status, json) = fetch_json(resp).await;
429 assert_eq!(status, StatusCode::UNAUTHORIZED);
430 assert_eq!(json["code"], -1);
431 assert_eq!(json["msg"], "请先登录");
432 }
433
434 #[tokio::test]
435 async fn test_handle_error_forbidden() {
436 let err = HandleError::forbidden("无权限");
437 let resp = err.into_response();
438 let (status, json) = fetch_json(resp).await;
439 assert_eq!(status, StatusCode::FORBIDDEN);
440 assert_eq!(json["code"], 0);
441 assert_eq!(json["msg"], "无权限");
442 }
443
444 #[tokio::test]
445 async fn test_handle_error_custom() {
446 let err = HandleError::new(StatusCode::CONFLICT, 4091, "冲突");
447 let resp = err.into_response();
448 let (status, json) = fetch_json(resp).await;
449 assert_eq!(status, StatusCode::CONFLICT);
450 assert_eq!(json["code"], 4091);
451 assert_eq!(json["msg"], "冲突");
452 }
453
454 #[test]
455 fn test_handle_error_clone_debug() {
456 let err = HandleError::not_found("test");
457 let cloned = err.clone();
458 assert_eq!(cloned.msg, "test");
459 let debug = format!("{err:?}");
460 assert!(debug.contains("HandleError"));
461 }
462
463 #[tokio::test]
468 async fn test_error_router_404_fallback() {
469 let router: Router = Router::new()
470 .route("/api", axum::routing::get(|| async { "ok" }))
471 .merge(error_router());
472
473 let resp = send_get(router.clone(), "/api").await;
475 assert_eq!(resp.status(), StatusCode::OK);
476
477 let resp = send_get(router, "/nonexistent").await;
479 let (status, json) = fetch_json(resp).await;
480 assert_eq!(status, StatusCode::NOT_FOUND);
481 assert_eq!(json["code"], 0);
482 assert_eq!(json["msg"], "Not Found");
483 }
484
485 #[tokio::test]
486 async fn test_fallback_router_404() {
487 let router: Router = Router::new()
488 .route("/api", axum::routing::get(|| async { "ok" }))
489 .merge(fallback_router());
490
491 let resp = send_get(router, "/nonexistent").await;
492 assert_eq!(resp.status(), StatusCode::NOT_FOUND);
493 }
494
495 #[tokio::test]
500 async fn test_handle_error_as_handler_return() {
501 async fn handler() -> Result<String, HandleError> {
502 Err(HandleError::not_found("用户不存在"))
503 }
504
505 let router: Router = Router::new().route("/users/{id}", axum::routing::get(handler));
506
507 let req = Request::builder()
508 .method(Method::GET)
509 .uri("/users/999")
510 .body(Body::empty())
511 .unwrap();
512 let resp = router.oneshot(req).await.unwrap();
513 let (status, json) = fetch_json(resp).await;
514 assert_eq!(status, StatusCode::NOT_FOUND);
515 assert_eq!(json["msg"], "用户不存在");
516 }
517
518 #[tokio::test]
519 async fn test_handle_error_success_path() {
520 async fn handler() -> Result<String, HandleError> {
521 Ok("success".to_string())
522 }
523
524 let router: Router = Router::new().route("/ok", axum::routing::get(handler));
525
526 let resp = send_get(router, "/ok").await;
527 assert_eq!(resp.status(), StatusCode::OK);
528 }
529
530 #[tokio::test]
535 async fn test_api_response_vs_handle_error_consistency() {
536 let api_resp = ApiResponse::error("业务错误");
538 let api_response: Response = api_resp.into_response();
539 let (api_status, api_json) = fetch_json(api_response).await;
540 assert_eq!(api_status, StatusCode::OK);
541 assert_eq!(api_json["code"], 0);
542
543 let handle_err = HandleError::bad_request("参数错误");
545 let err_response: Response = handle_err.into_response();
546 let (err_status, err_json) = fetch_json(err_response).await;
547 assert_eq!(err_status, StatusCode::BAD_REQUEST);
548 assert_eq!(err_json["code"], 0);
549
550 }
554
555 #[tokio::test]
560 async fn test_all_error_handlers_return_json() {
561 let handlers: Vec<(Response, StatusCode, &str)> = vec![
562 (
563 not_found_handler().await,
564 StatusCode::NOT_FOUND,
565 "Not Found",
566 ),
567 (
568 internal_error_handler().await,
569 StatusCode::INTERNAL_SERVER_ERROR,
570 "Internal Error",
571 ),
572 (
573 method_not_allowed_handler().await,
574 StatusCode::METHOD_NOT_ALLOWED,
575 "Method Not Allowed",
576 ),
577 (
578 bad_request_handler().await,
579 StatusCode::BAD_REQUEST,
580 "Bad Request",
581 ),
582 (
583 forbidden_handler().await,
584 StatusCode::FORBIDDEN,
585 "Forbidden",
586 ),
587 (
588 unprocessable_entity_handler().await,
589 StatusCode::UNPROCESSABLE_ENTITY,
590 "Unprocessable Entity",
591 ),
592 ];
593
594 for (resp, expected_status, expected_msg) in handlers {
595 let (status, json) = fetch_json(resp).await;
596 assert_eq!(status, expected_status);
597 assert_eq!(json["code"], 0);
598 assert_eq!(json["msg"], expected_msg);
599 assert!(json["data"].is_object());
600 }
601 }
602
603 #[tokio::test]
604 async fn test_unauthorized_returns_code_minus_one() {
605 let resp = unauthorized_handler().await;
607 let (_, json) = fetch_json(resp).await;
608 assert_eq!(json["code"], -1);
609 }
610}