1use axum::http::StatusCode;
31use axum::response::{IntoResponse, Response};
32use serde::Serialize;
33use serde_json::{Map, Value};
34
35#[derive(Debug, Clone)]
55pub struct ApiResponse {
56 pub code: i32,
58 pub msg: String,
60 pub data: Value,
62}
63
64impl ApiResponse {
65 pub fn new(code: i32, msg: impl Into<String>, data: Value) -> Self {
67 Self {
68 code,
69 msg: msg.into(),
70 data,
71 }
72 }
73
74 pub fn success(data: Value, msg: impl Into<String>) -> Self {
78 Self::new(1, msg, data)
79 }
80
81 pub fn success_empty() -> Self {
83 Self::success(Value::Object(Map::new()), "")
84 }
85
86 pub fn error(msg: impl Into<String>) -> Self {
90 Self::new(0, msg, Value::Object(Map::new()))
91 }
92
93 pub fn error_with_data(msg: impl Into<String>, data: Value) -> Self {
95 Self::new(0, msg, data)
96 }
97
98 pub fn error_with_code(code: i32, msg: impl Into<String>, data: Value) -> Self {
102 Self::new(code, msg, data)
103 }
104
105 pub fn to_value(&self) -> Value {
109 let mut map = Map::new();
110 map.insert("code".to_string(), Value::Number(self.code.into()));
111 map.insert("msg".to_string(), Value::String(self.msg.clone()));
112 map.insert("data".to_string(), self.data.clone());
113 Value::Object(map)
114 }
115
116 pub fn to_json_string(&self) -> String {
118 self.to_value().to_string()
119 }
120}
121
122impl Serialize for ApiResponse {
123 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
124 where
125 S: serde::Serializer,
126 {
127 self.to_value().serialize(serializer)
128 }
129}
130
131impl IntoResponse for ApiResponse {
137 fn into_response(self) -> Response {
138 let body = self.to_json_string();
139 (
140 StatusCode::OK,
141 [(
142 axum::http::header::CONTENT_TYPE,
143 "application/json; charset=utf-8",
144 )],
145 body,
146 )
147 .into_response()
148 }
149}
150
151#[tracing::instrument(skip(msg, data))]
155pub fn render_json(code: i32, msg: impl Into<String>, data: Value) -> Response {
156 ApiResponse::new(code, msg, data).into_response()
157}
158
159#[tracing::instrument(skip(data, msg))]
163pub fn render_success(data: Value, msg: impl Into<String>) -> Response {
164 ApiResponse::success(data, msg).into_response()
165}
166
167#[tracing::instrument(skip(msg))]
171pub fn render_error(msg: impl Into<String>) -> Response {
172 ApiResponse::error(msg).into_response()
173}
174
175#[tracing::instrument(skip(msg, data))]
179pub fn render_error_with_code(code: i32, msg: impl Into<String>, data: Value) -> Response {
180 ApiResponse::error_with_code(code, msg, data).into_response()
181}
182
183use axum::http::{header, HeaderMap};
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
240pub enum DefaultResponseType {
241 #[default]
246 Json,
247
248 Html,
252
253 Auto,
258}
259
260impl DefaultResponseType {
261 pub fn respond(&self, data: &Value, headers: &HeaderMap) -> Response {
272 match self {
273 DefaultResponseType::Json => respond(data),
274 DefaultResponseType::Html => respond_html(data.to_string()),
275 DefaultResponseType::Auto => auto_respond(data, headers),
276 }
277 }
278}
279
280pub fn is_json_request(headers: &HeaderMap) -> bool {
305 if let Some(accept) = headers.get(header::ACCEPT) {
306 if let Ok(accept_str) = accept.to_str() {
307 return accept_str.to_lowercase().contains("json");
312 }
313 }
314 false
315}
316
317#[tracing::instrument(skip(data))]
335pub fn respond(data: &Value) -> Response {
336 let body = data.to_string();
337 (
338 StatusCode::OK,
339 [(header::CONTENT_TYPE, "application/json; charset=utf-8")],
340 body,
341 )
342 .into_response()
343}
344
345#[tracing::instrument(skip(content))]
357pub fn respond_html(content: impl Into<String>) -> Response {
358 (
359 StatusCode::OK,
360 [(header::CONTENT_TYPE, "text/html; charset=utf-8")],
361 content.into(),
362 )
363 .into_response()
364}
365
366#[tracing::instrument(skip(content))]
378pub fn respond_text(content: impl Into<String>) -> Response {
379 (
380 StatusCode::OK,
381 [(header::CONTENT_TYPE, "text/plain; charset=utf-8")],
382 content.into(),
383 )
384 .into_response()
385}
386
387#[tracing::instrument(skip(data, headers))]
421pub fn auto_respond(data: &Value, headers: &HeaderMap) -> Response {
422 if is_json_request(headers) {
423 respond(data)
425 } else {
426 let content = match data {
429 Value::Array(_) | Value::Object(_) => "Array".to_string(),
430 Value::String(s) => s.clone(),
431 Value::Null => String::new(),
432 _ => data.to_string(),
433 };
434 respond_html(content)
435 }
436}
437
438#[derive(Debug, Clone)]
471pub struct JsonResponse(pub Value);
472
473impl From<Value> for JsonResponse {
474 fn from(v: Value) -> Self {
475 JsonResponse(v)
476 }
477}
478
479impl IntoResponse for JsonResponse {
480 fn into_response(self) -> Response {
481 respond(&self.0)
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use axum::body::Body;
489 use axum::http::{Method, Request};
490 use http_body_util::BodyExt;
491 use tower::ServiceExt;
492
493 #[test]
498 fn test_api_response_new() {
499 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
500 assert_eq!(resp.code, 1);
501 assert_eq!(resp.msg, "ok");
502 assert!(resp.data.is_object());
503 }
504
505 #[test]
506 fn test_api_response_success() {
507 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
508 assert_eq!(resp.code, 1);
509 assert_eq!(resp.msg, "ok");
510 assert_eq!(resp.data["id"], 1);
511 }
512
513 #[test]
514 fn test_api_response_success_empty() {
515 let resp = ApiResponse::success_empty();
516 assert_eq!(resp.code, 1);
517 assert_eq!(resp.msg, "");
518 assert!(resp.data.is_object());
519 assert!(resp.data.as_object().unwrap().is_empty());
520 }
521
522 #[test]
523 fn test_api_response_error() {
524 let resp = ApiResponse::error("参数错误");
525 assert_eq!(resp.code, 0);
526 assert_eq!(resp.msg, "参数错误");
527 assert!(resp.data.is_object());
528 }
529
530 #[test]
531 fn test_api_response_error_with_data() {
532 let resp = ApiResponse::error_with_data("失败", serde_json::json!({"field": "name"}));
533 assert_eq!(resp.code, 0);
534 assert_eq!(resp.msg, "失败");
535 assert_eq!(resp.data["field"], "name");
536 }
537
538 #[test]
539 fn test_api_response_error_with_code() {
540 let resp = ApiResponse::error_with_code(-1, "未登录", Value::Object(Map::new()));
541 assert_eq!(resp.code, -1);
542 assert_eq!(resp.msg, "未登录");
543 }
544
545 #[test]
546 fn test_api_response_to_value_field_order() {
547 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
548 let value = resp.to_value();
549 let obj = value.as_object().unwrap();
550
551 let keys: Vec<&String> = obj.keys().collect();
553 assert_eq!(keys, vec!["code", "msg", "data"]);
554 }
555
556 #[test]
557 fn test_api_response_to_value_content() {
558 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1, "name": "alice"}));
559 let value = resp.to_value();
560 assert_eq!(value["code"], 1);
561 assert_eq!(value["msg"], "ok");
562 assert_eq!(value["data"]["id"], 1);
563 assert_eq!(value["data"]["name"], "alice");
564 }
565
566 #[test]
567 fn test_api_response_to_json_string() {
568 let resp = ApiResponse::new(1, "ok", serde_json::json!({}));
569 let json_str = resp.to_json_string();
570 let expected = r#"{"code":1,"msg":"ok","data":{}}"#;
572 assert_eq!(json_str, expected);
573 }
574
575 #[test]
576 fn test_api_response_to_json_string_with_data() {
577 let resp = ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok");
578 let json_str = resp.to_json_string();
579 let expected = r#"{"code":1,"msg":"ok","data":{"id":1,"name":"alice"}}"#;
580 assert_eq!(json_str, expected);
581 }
582
583 #[test]
584 fn test_api_response_serialize_via_serde() {
585 let resp = ApiResponse::new(0, "失败", Value::Object(Map::new()));
586 let json_str = serde_json::to_string(&resp).unwrap();
587 assert_eq!(json_str, r#"{"code":0,"msg":"失败","data":{}}"#);
588 }
589
590 #[test]
591 fn test_api_response_clone() {
592 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
593 let cloned = resp.clone();
594 assert_eq!(cloned.code, resp.code);
595 assert_eq!(cloned.msg, resp.msg);
596 assert_eq!(cloned.data, resp.data);
597 }
598
599 #[test]
600 fn test_api_response_debug_format() {
601 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
602 let debug_str = format!("{resp:?}");
603 assert!(debug_str.contains("ApiResponse"));
604 assert!(debug_str.contains("code: 1"));
605 assert!(debug_str.contains("\"ok\""));
606 }
607
608 #[test]
613 fn test_render_json_returns_response() {
614 let resp = render_json(1, "ok", serde_json::json!({}));
615 assert_eq!(resp.status(), StatusCode::OK);
616 assert_eq!(
617 resp.headers().get("content-type").unwrap(),
618 "application/json; charset=utf-8"
619 );
620 }
621
622 #[test]
623 fn test_render_success_returns_response() {
624 let resp = render_success(serde_json::json!({"id": 1}), "ok");
625 assert_eq!(resp.status(), StatusCode::OK);
626 }
627
628 #[test]
629 fn test_render_error_returns_response() {
630 let resp = render_error("参数错误");
631 assert_eq!(resp.status(), StatusCode::OK); }
633
634 #[test]
635 fn test_render_error_with_code_returns_response() {
636 let resp = render_error_with_code(-1, "未登录", serde_json::json!({}));
637 assert_eq!(resp.status(), StatusCode::OK);
638 }
639
640 #[tokio::test]
645 async fn test_api_response_as_handler_return() {
646 async fn handler() -> ApiResponse {
647 ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok")
648 }
649
650 let router = axum::Router::new().route("/", axum::routing::get(handler));
651 let req = Request::builder()
652 .method(Method::GET)
653 .uri("/")
654 .body(Body::empty())
655 .unwrap();
656 let resp = router.oneshot(req).await.unwrap();
657
658 assert_eq!(resp.status(), StatusCode::OK);
659 assert_eq!(
660 resp.headers().get("content-type").unwrap(),
661 "application/json; charset=utf-8"
662 );
663
664 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
665 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
666 let json: Value = serde_json::from_str(&body_str).unwrap();
667
668 assert_eq!(json["code"], 1);
669 assert_eq!(json["msg"], "ok");
670 assert_eq!(json["data"]["id"], 1);
671 assert_eq!(json["data"]["name"], "alice");
672 }
673
674 #[tokio::test]
675 async fn test_render_error_handler_return() {
676 async fn handler() -> Response {
677 render_error("参数错误")
678 }
679
680 let router = axum::Router::new().route("/", axum::routing::post(handler));
681 let req = Request::builder()
682 .method(Method::POST)
683 .uri("/")
684 .body(Body::empty())
685 .unwrap();
686 let resp = router.oneshot(req).await.unwrap();
687
688 assert_eq!(resp.status(), StatusCode::OK);
689
690 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
691 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
692 let json: Value = serde_json::from_str(&body_str).unwrap();
693
694 assert_eq!(json["code"], 0);
695 assert_eq!(json["msg"], "参数错误");
696 assert!(json["data"].is_object());
697 }
698
699 #[tokio::test]
700 async fn test_response_body_exact_format() {
701 async fn handler() -> ApiResponse {
703 ApiResponse::success_empty()
704 }
705
706 let router = axum::Router::new().route("/", axum::routing::get(handler));
707 let req = Request::builder()
708 .method(Method::GET)
709 .uri("/")
710 .body(Body::empty())
711 .unwrap();
712 let resp = router.oneshot(req).await.unwrap();
713
714 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
715 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
716 assert_eq!(body_str, r#"{"code":1,"msg":"","data":{}}"#);
717 }
718
719 #[tokio::test]
720 async fn test_response_with_complex_data() {
721 async fn handler() -> ApiResponse {
722 ApiResponse::success(
723 serde_json::json!({
724 "list": [{"id": 1}, {"id": 2}],
725 "total": 2,
726 "page": 1,
727 "size": 10
728 }),
729 "查询成功",
730 )
731 }
732
733 let router = axum::Router::new().route("/", axum::routing::get(handler));
734 let req = Request::builder()
735 .method(Method::GET)
736 .uri("/")
737 .body(Body::empty())
738 .unwrap();
739 let resp = router.oneshot(req).await.unwrap();
740
741 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
742 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
743 let json: Value = serde_json::from_str(&body_str).unwrap();
744
745 assert_eq!(json["code"], 1);
746 assert_eq!(json["msg"], "查询成功");
747 assert_eq!(json["data"]["total"], 2);
748 assert_eq!(json["data"]["list"][0]["id"], 1);
749 assert_eq!(json["data"]["list"][1]["id"], 2);
750 }
751
752 #[tokio::test]
753 async fn test_response_with_various_error_codes() {
754 let test_cases = vec![
756 (0, "业务失败"),
757 (-1, "未登录"),
758 (-2, "用户不存在"),
759 (-3, "用户被禁用"),
760 (403, "禁止访问"),
761 (404, "资源不存在"),
762 (422, "参数校验失败"),
763 (500, "数据库错误"),
764 ];
765
766 for (code, msg) in test_cases {
767 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
768 let json_str = resp.to_json_string();
769 let json: Value = serde_json::from_str(&json_str).unwrap();
770 assert_eq!(json["code"], code);
771 assert_eq!(json["msg"], msg);
772 }
773 }
774
775 #[test]
800 fn test_php_consistency_render_json_compact_field_order() {
801 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
805 let value = resp.to_value();
806 let obj = value.as_object().unwrap();
807 let keys: Vec<&String> = obj.keys().collect();
808 assert_eq!(
809 keys,
810 vec!["code", "msg", "data"],
811 "字段顺序必须为 code → msg → data(对齐 PHP compact())"
812 );
813 assert_eq!(value["code"], 1);
814 assert_eq!(value["msg"], "ok");
815 assert_eq!(value["data"]["id"], 1);
816 }
817
818 #[test]
819 fn test_php_consistency_render_json_default_values() {
820 let resp = ApiResponse::new(1, "", Value::Object(Map::new()));
823 let json_str = resp.to_json_string();
824 assert_eq!(
825 json_str, r#"{"code":1,"msg":"","data":{}}"#,
826 "默认值必须与 PHP renderJson() 一致:code=1, msg='', data={{}}"
827 );
828 }
829
830 #[test]
831 fn test_php_consistency_render_success_calls_render_json_with_code_1() {
832 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
835 assert_eq!(
836 resp.code, 1,
837 "renderSuccess 必须 code=1(对齐 PHP renderJson(1, ...))"
838 );
839 assert_eq!(resp.msg, "ok");
840 assert_eq!(resp.data["id"], 1);
841
842 let json_str = resp.to_json_string();
844 let expected = r#"{"code":1,"msg":"ok","data":{"id":1}}"#;
845 assert_eq!(json_str, expected);
846 }
847
848 #[test]
849 fn test_php_consistency_render_error_default_code_is_0() {
850 let resp = ApiResponse::error("参数错误");
853 assert_eq!(
854 resp.code, 0,
855 "renderError 默认 code=0(对齐 PHP 默认参数 $code = 0)"
856 );
857 assert_eq!(resp.msg, "参数错误");
858 assert!(
859 resp.data.is_object(),
860 "renderError 默认 data 为空对象(对齐 PHP $data = [])"
861 );
862
863 let response = render_error("参数错误");
865 assert_eq!(response.status(), StatusCode::OK);
866 }
867
868 #[test]
869 fn test_php_consistency_render_error_with_custom_code_aligns_base_exception() {
870 let test_cases = vec![
877 (-1i32, "未登录"),
878 (-2, "用户不存在"),
879 (-3, "用户被禁用"),
880 (0, "业务失败"),
881 ];
882
883 for (code, msg) in test_cases {
884 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
885 let json_str = resp.to_json_string();
886 let json: Value = serde_json::from_str(&json_str).unwrap();
887 assert_eq!(
888 json["code"], code,
889 "自定义错误码必须与 PHP BaseException 约定一致"
890 );
891 assert_eq!(json["msg"], msg);
892 assert!(json.get("data").is_some(), "data 字段必须存在");
894 }
895 }
896
897 #[test]
914 fn test_default_response_type_default_is_json() {
915 let t = DefaultResponseType::default();
917 assert_eq!(t, DefaultResponseType::Json);
918 }
919
920 #[test]
921 fn test_default_response_type_variants_eq() {
922 assert_eq!(DefaultResponseType::Json, DefaultResponseType::Json);
923 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Html);
924 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Auto);
925 assert_ne!(DefaultResponseType::Html, DefaultResponseType::Auto);
926 }
927
928 #[test]
929 fn test_default_response_type_clone_copy() {
930 let t = DefaultResponseType::Json;
931 let t2 = t; assert_eq!(t, t2);
933 let t3 = t;
935 assert_eq!(t, t3);
936 }
937
938 #[test]
939 fn test_default_response_type_debug() {
940 let debug = format!("{:?}", DefaultResponseType::Json);
941 assert!(debug.contains("Json"));
942 let debug = format!("{:?}", DefaultResponseType::Html);
943 assert!(debug.contains("Html"));
944 let debug = format!("{:?}", DefaultResponseType::Auto);
945 assert!(debug.contains("Auto"));
946 }
947
948 #[test]
949 fn test_default_response_type_respond_json() {
950 let headers = HeaderMap::new();
952 let data = serde_json::json!({"id": 1});
953 let resp = DefaultResponseType::Json.respond(&data, &headers);
954 assert_eq!(resp.status(), StatusCode::OK);
955 assert_eq!(
956 resp.headers().get("content-type").unwrap(),
957 "application/json; charset=utf-8"
958 );
959 }
960
961 #[test]
962 fn test_default_response_type_respond_html() {
963 let headers = HeaderMap::new();
965 let data = serde_json::json!({"id": 1});
966 let resp = DefaultResponseType::Html.respond(&data, &headers);
967 assert_eq!(resp.status(), StatusCode::OK);
968 assert_eq!(
969 resp.headers().get("content-type").unwrap(),
970 "text/html; charset=utf-8"
971 );
972 }
973
974 #[tokio::test]
975 async fn test_default_response_type_respond_html_body() {
976 let headers = HeaderMap::new();
977 let data = serde_json::json!({"id": 1});
978 let resp = DefaultResponseType::Html.respond(&data, &headers);
979 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
980 let body = String::from_utf8(bytes.to_vec()).unwrap();
981 assert_eq!(body, r#"{"id":1}"#);
983 }
984
985 #[tokio::test]
986 async fn test_default_response_type_respond_auto_with_json_accept() {
987 let mut headers = HeaderMap::new();
989 headers.insert("accept", "application/json".parse().unwrap());
990 let data = serde_json::json!({"id": 1});
991 let resp = DefaultResponseType::Auto.respond(&data, &headers);
992 assert_eq!(
993 resp.headers().get("content-type").unwrap(),
994 "application/json; charset=utf-8"
995 );
996 }
997
998 #[tokio::test]
999 async fn test_default_response_type_respond_auto_with_html_accept() {
1000 let mut headers = HeaderMap::new();
1002 headers.insert("accept", "text/html".parse().unwrap());
1003 let data = serde_json::json!({"id": 1});
1004 let resp = DefaultResponseType::Auto.respond(&data, &headers);
1005 assert_eq!(
1006 resp.headers().get("content-type").unwrap(),
1007 "text/html; charset=utf-8"
1008 );
1009 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1010 let body = String::from_utf8(bytes.to_vec()).unwrap();
1011 assert_eq!(body, "Array");
1013 }
1014
1015 #[test]
1018 fn test_is_json_request_with_application_json() {
1019 let mut headers = HeaderMap::new();
1020 headers.insert("accept", "application/json".parse().unwrap());
1021 assert!(is_json_request(&headers));
1022 }
1023
1024 #[test]
1025 fn test_is_json_request_with_text_json() {
1026 let mut headers = HeaderMap::new();
1027 headers.insert("accept", "text/json".parse().unwrap());
1028 assert!(is_json_request(&headers));
1029 }
1030
1031 #[test]
1032 fn test_is_json_request_with_vnd_api_json() {
1033 let mut headers = HeaderMap::new();
1034 headers.insert("accept", "application/vnd.api+json".parse().unwrap());
1035 assert!(is_json_request(&headers));
1036 }
1037
1038 #[test]
1039 fn test_is_json_request_with_wildcard() {
1040 let mut headers = HeaderMap::new();
1042 headers.insert("accept", "*/*".parse().unwrap());
1043 assert!(!is_json_request(&headers));
1044 }
1045
1046 #[test]
1047 fn test_is_json_request_with_text_html() {
1048 let mut headers = HeaderMap::new();
1049 headers.insert("accept", "text/html".parse().unwrap());
1050 assert!(!is_json_request(&headers));
1051 }
1052
1053 #[test]
1054 fn test_is_json_request_no_accept_header() {
1055 let headers = HeaderMap::new();
1056 assert!(!is_json_request(&headers));
1057 }
1058
1059 #[test]
1060 fn test_is_json_request_case_insensitive() {
1061 let mut headers = HeaderMap::new();
1063 headers.insert("accept", "APPLICATION/JSON".parse().unwrap());
1064 assert!(is_json_request(&headers));
1065 }
1066
1067 #[test]
1068 fn test_is_json_request_mixed_accept() {
1069 let mut headers = HeaderMap::new();
1071 headers.insert(
1072 "accept",
1073 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1074 .parse()
1075 .unwrap(),
1076 );
1077 assert!(is_json_request(&headers));
1078 }
1079
1080 #[test]
1083 fn test_respond_returns_json_content_type() {
1084 let data = serde_json::json!({"id": 1});
1085 let resp = respond(&data);
1086 assert_eq!(resp.status(), StatusCode::OK);
1087 assert_eq!(
1088 resp.headers().get("content-type").unwrap(),
1089 "application/json; charset=utf-8"
1090 );
1091 }
1092
1093 #[tokio::test]
1094 async fn test_respond_object_body() {
1095 let data = serde_json::json!({"id": 1, "name": "alice"});
1096 let resp = respond(&data);
1097 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1098 let body = String::from_utf8(bytes.to_vec()).unwrap();
1099 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1100 }
1101
1102 #[tokio::test]
1103 async fn test_respond_array_body() {
1104 let data = serde_json::json!([1, 2, 3]);
1105 let resp = respond(&data);
1106 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1107 let body = String::from_utf8(bytes.to_vec()).unwrap();
1108 assert_eq!(body, r#"[1,2,3]"#);
1109 }
1110
1111 #[tokio::test]
1112 async fn test_respond_string_value() {
1113 let data = Value::String("hello".to_string());
1114 let resp = respond(&data);
1115 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1116 let body = String::from_utf8(bytes.to_vec()).unwrap();
1117 assert_eq!(body, r#""hello""#);
1119 }
1120
1121 #[tokio::test]
1122 async fn test_respond_null_value() {
1123 let resp = respond(&Value::Null);
1124 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1125 let body = String::from_utf8(bytes.to_vec()).unwrap();
1126 assert_eq!(body, "null");
1127 }
1128
1129 #[tokio::test]
1130 async fn test_respond_number_value() {
1131 let resp = respond(&serde_json::json!(42));
1132 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1133 let body = String::from_utf8(bytes.to_vec()).unwrap();
1134 assert_eq!(body, "42");
1135 }
1136
1137 #[tokio::test]
1138 async fn test_respond_bool_value() {
1139 let resp = respond(&serde_json::json!(true));
1140 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1141 let body = String::from_utf8(bytes.to_vec()).unwrap();
1142 assert_eq!(body, "true");
1143 }
1144
1145 #[test]
1148 fn test_respond_html_content_type() {
1149 let resp = respond_html("<h1>Hello</h1>");
1150 assert_eq!(resp.status(), StatusCode::OK);
1151 assert_eq!(
1152 resp.headers().get("content-type").unwrap(),
1153 "text/html; charset=utf-8"
1154 );
1155 }
1156
1157 #[tokio::test]
1158 async fn test_respond_html_body() {
1159 let resp = respond_html("<p>test</p>");
1160 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1161 let body = String::from_utf8(bytes.to_vec()).unwrap();
1162 assert_eq!(body, "<p>test</p>");
1163 }
1164
1165 #[tokio::test]
1166 async fn test_respond_html_empty() {
1167 let resp = respond_html("");
1168 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1169 let body = String::from_utf8(bytes.to_vec()).unwrap();
1170 assert_eq!(body, "");
1171 }
1172
1173 #[tokio::test]
1174 async fn test_respond_html_with_unicode() {
1175 let resp = respond_html("<p>你好世界</p>");
1176 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1177 let body = String::from_utf8(bytes.to_vec()).unwrap();
1178 assert_eq!(body, "<p>你好世界</p>");
1179 }
1180
1181 #[test]
1184 fn test_respond_text_content_type() {
1185 let resp = respond_text("plain text");
1186 assert_eq!(resp.status(), StatusCode::OK);
1187 assert_eq!(
1188 resp.headers().get("content-type").unwrap(),
1189 "text/plain; charset=utf-8"
1190 );
1191 }
1192
1193 #[tokio::test]
1194 async fn test_respond_text_body() {
1195 let resp = respond_text("OK");
1196 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1197 let body = String::from_utf8(bytes.to_vec()).unwrap();
1198 assert_eq!(body, "OK");
1199 }
1200
1201 #[tokio::test]
1204 async fn test_auto_respond_json_request_with_object() {
1205 let mut headers = HeaderMap::new();
1207 headers.insert("accept", "application/json".parse().unwrap());
1208 let data = serde_json::json!({"id": 1});
1209 let resp = auto_respond(&data, &headers);
1210 assert_eq!(
1211 resp.headers().get("content-type").unwrap(),
1212 "application/json; charset=utf-8"
1213 );
1214 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1215 let body = String::from_utf8(bytes.to_vec()).unwrap();
1216 assert_eq!(body, r#"{"id":1}"#);
1217 }
1218
1219 #[tokio::test]
1220 async fn test_auto_respond_json_request_with_array() {
1221 let mut headers = HeaderMap::new();
1222 headers.insert("accept", "application/json".parse().unwrap());
1223 let data = serde_json::json!([1, 2, 3]);
1224 let resp = auto_respond(&data, &headers);
1225 assert_eq!(
1226 resp.headers().get("content-type").unwrap(),
1227 "application/json; charset=utf-8"
1228 );
1229 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1230 let body = String::from_utf8(bytes.to_vec()).unwrap();
1231 assert_eq!(body, r#"[1,2,3]"#);
1232 }
1233
1234 #[tokio::test]
1235 async fn test_auto_respond_html_request_with_object_returns_array_literal() {
1236 let mut headers = HeaderMap::new();
1238 headers.insert("accept", "text/html".parse().unwrap());
1239 let data = serde_json::json!({"id": 1});
1240 let resp = auto_respond(&data, &headers);
1241 assert_eq!(
1242 resp.headers().get("content-type").unwrap(),
1243 "text/html; charset=utf-8"
1244 );
1245 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1246 let body = String::from_utf8(bytes.to_vec()).unwrap();
1247 assert_eq!(body, "Array");
1248 }
1249
1250 #[tokio::test]
1251 async fn test_auto_respond_html_request_with_array_returns_array_literal() {
1252 let mut headers = HeaderMap::new();
1254 headers.insert("accept", "text/html".parse().unwrap());
1255 let data = serde_json::json!([1, 2, 3]);
1256 let resp = auto_respond(&data, &headers);
1257 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1258 let body = String::from_utf8(bytes.to_vec()).unwrap();
1259 assert_eq!(body, "Array");
1260 }
1261
1262 #[tokio::test]
1263 async fn test_auto_respond_html_request_with_string_returns_string() {
1264 let mut headers = HeaderMap::new();
1266 headers.insert("accept", "text/html".parse().unwrap());
1267 let data = Value::String("hello".to_string());
1268 let resp = auto_respond(&data, &headers);
1269 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1270 let body = String::from_utf8(bytes.to_vec()).unwrap();
1271 assert_eq!(body, "hello");
1272 }
1273
1274 #[tokio::test]
1275 async fn test_auto_respond_html_request_with_null_returns_empty() {
1276 let mut headers = HeaderMap::new();
1277 headers.insert("accept", "text/html".parse().unwrap());
1278 let resp = auto_respond(&Value::Null, &headers);
1279 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1280 let body = String::from_utf8(bytes.to_vec()).unwrap();
1281 assert_eq!(body, "");
1282 }
1283
1284 #[tokio::test]
1285 async fn test_auto_respond_html_request_with_number_returns_number_string() {
1286 let mut headers = HeaderMap::new();
1287 headers.insert("accept", "text/html".parse().unwrap());
1288 let resp = auto_respond(&serde_json::json!(42), &headers);
1289 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1290 let body = String::from_utf8(bytes.to_vec()).unwrap();
1291 assert_eq!(body, "42");
1292 }
1293
1294 #[tokio::test]
1295 async fn test_auto_respond_no_accept_header_returns_html() {
1296 let headers = HeaderMap::new();
1298 let data = serde_json::json!({"id": 1});
1299 let resp = auto_respond(&data, &headers);
1300 assert_eq!(
1301 resp.headers().get("content-type").unwrap(),
1302 "text/html; charset=utf-8"
1303 );
1304 }
1305
1306 #[tokio::test]
1307 async fn test_auto_respond_wildcard_accept_returns_html() {
1308 let mut headers = HeaderMap::new();
1310 headers.insert("accept", "*/*".parse().unwrap());
1311 let data = serde_json::json!({"id": 1});
1312 let resp = auto_respond(&data, &headers);
1313 assert_eq!(
1314 resp.headers().get("content-type").unwrap(),
1315 "text/html; charset=utf-8"
1316 );
1317 }
1318
1319 #[tokio::test]
1322 async fn test_json_response_into_response_object() {
1323 async fn handler() -> JsonResponse {
1324 JsonResponse(serde_json::json!({"id": 1, "name": "alice"}))
1325 }
1326
1327 let router = axum::Router::new().route("/", axum::routing::get(handler));
1328 let req = Request::builder()
1329 .method(Method::GET)
1330 .uri("/")
1331 .body(Body::empty())
1332 .unwrap();
1333 let resp = router.oneshot(req).await.unwrap();
1334
1335 assert_eq!(resp.status(), StatusCode::OK);
1336 assert_eq!(
1337 resp.headers().get("content-type").unwrap(),
1338 "application/json; charset=utf-8"
1339 );
1340 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1341 let body = String::from_utf8(bytes.to_vec()).unwrap();
1342 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1343 }
1344
1345 #[tokio::test]
1346 async fn test_json_response_into_response_array() {
1347 async fn handler() -> JsonResponse {
1348 JsonResponse(serde_json::json!([1, 2, 3]))
1349 }
1350
1351 let router = axum::Router::new().route("/", axum::routing::get(handler));
1352 let req = Request::builder()
1353 .method(Method::GET)
1354 .uri("/")
1355 .body(Body::empty())
1356 .unwrap();
1357 let resp = router.oneshot(req).await.unwrap();
1358
1359 assert_eq!(
1360 resp.headers().get("content-type").unwrap(),
1361 "application/json; charset=utf-8"
1362 );
1363 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1364 let body = String::from_utf8(bytes.to_vec()).unwrap();
1365 assert_eq!(body, r#"[1,2,3]"#);
1366 }
1367
1368 #[tokio::test]
1369 async fn test_json_response_into_response_string() {
1370 async fn handler() -> JsonResponse {
1371 JsonResponse(Value::String("hello".to_string()))
1372 }
1373
1374 let router = axum::Router::new().route("/", axum::routing::get(handler));
1375 let req = Request::builder()
1376 .method(Method::GET)
1377 .uri("/")
1378 .body(Body::empty())
1379 .unwrap();
1380 let resp = router.oneshot(req).await.unwrap();
1381
1382 assert_eq!(
1384 resp.headers().get("content-type").unwrap(),
1385 "application/json; charset=utf-8"
1386 );
1387 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1388 let body = String::from_utf8(bytes.to_vec()).unwrap();
1389 assert_eq!(body, r#""hello""#);
1390 }
1391
1392 #[tokio::test]
1393 async fn test_json_response_into_response_null() {
1394 async fn handler() -> JsonResponse {
1395 JsonResponse(Value::Null)
1396 }
1397
1398 let router = axum::Router::new().route("/", axum::routing::get(handler));
1399 let req = Request::builder()
1400 .method(Method::GET)
1401 .uri("/")
1402 .body(Body::empty())
1403 .unwrap();
1404 let resp = router.oneshot(req).await.unwrap();
1405
1406 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1407 let body = String::from_utf8(bytes.to_vec()).unwrap();
1408 assert_eq!(body, "null");
1409 }
1410
1411 #[tokio::test]
1412 async fn test_json_response_into_response_post_handler() {
1413 async fn handler() -> JsonResponse {
1415 JsonResponse(serde_json::json!({
1416 "code": 1,
1417 "msg": "success",
1418 "data": {"id": 12345, "status": "paid"}
1419 }))
1420 }
1421
1422 let router = axum::Router::new().route("/api/order", axum::routing::post(handler));
1423 let req = Request::builder()
1424 .method(Method::POST)
1425 .uri("/api/order")
1426 .body(Body::empty())
1427 .unwrap();
1428 let resp = router.oneshot(req).await.unwrap();
1429
1430 assert_eq!(resp.status(), StatusCode::OK);
1431 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1432 let body = String::from_utf8(bytes.to_vec()).unwrap();
1433 let json: Value = serde_json::from_str(&body).unwrap();
1434 assert_eq!(json["code"], 1);
1435 assert_eq!(json["msg"], "success");
1436 assert_eq!(json["data"]["id"], 12345);
1437 assert_eq!(json["data"]["status"], "paid");
1438 }
1439
1440 #[test]
1441 fn test_json_response_from_value() {
1442 let value = serde_json::json!({"id": 1});
1444 let json_resp: JsonResponse = value.clone().into();
1445 assert_eq!(json_resp.0, value);
1446 }
1447
1448 #[test]
1449 fn test_json_response_clone_debug() {
1450 let resp = JsonResponse(serde_json::json!({"id": 1}));
1451 let cloned = resp.clone();
1452 assert_eq!(resp.0, cloned.0);
1453
1454 let debug = format!("{resp:?}");
1455 assert!(debug.contains("JsonResponse"));
1456 }
1457
1458 #[test]
1481 fn test_r5_php_isjson_accept_application_json() {
1482 let mut headers = HeaderMap::new();
1484 headers.insert("accept", "application/json".parse().unwrap());
1485 assert!(
1486 is_json_request(&headers),
1487 "Accept: application/json 时 isJson() 必须返回 true(对齐 PHP)"
1488 );
1489 }
1490
1491 #[test]
1492 fn test_r5_php_isjson_accept_text_html() {
1493 let mut headers = HeaderMap::new();
1495 headers.insert("accept", "text/html".parse().unwrap());
1496 assert!(
1497 !is_json_request(&headers),
1498 "Accept: text/html 时 isJson() 必须返回 false(对齐 PHP)"
1499 );
1500 }
1501
1502 #[test]
1503 fn test_r5_php_isjson_accept_wildcard() {
1504 let mut headers = HeaderMap::new();
1506 headers.insert("accept", "*/*".parse().unwrap());
1507 assert!(
1508 !is_json_request(&headers),
1509 "Accept: */* 时 isJson() 必须返回 false(对齐 PHP type() 无匹配 MIME)"
1510 );
1511 }
1512
1513 #[test]
1514 fn test_r5_php_isjson_no_accept_header() {
1515 let headers = HeaderMap::new();
1517 assert!(
1518 !is_json_request(&headers),
1519 "无 Accept 头时 isJson() 必须返回 false(对齐 PHP)"
1520 );
1521 }
1522
1523 #[tokio::test]
1524 async fn test_r5_php_autoresponse_json_type_with_array() {
1525 let mut headers = HeaderMap::new();
1528 headers.insert("accept", "application/json".parse().unwrap());
1529 let data = serde_json::json!([1, 2, 3]);
1530 let resp = auto_respond(&data, &headers);
1531
1532 assert_eq!(
1534 resp.headers().get("content-type").unwrap(),
1535 "application/json; charset=utf-8",
1536 "PHP autoResponse + isJson=true 时必须返回 JSON 类型"
1537 );
1538
1539 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1541 let body = String::from_utf8(bytes.to_vec()).unwrap();
1542 assert_eq!(
1543 body, "[1,2,3]",
1544 "PHP autoResponse + isJson=true 时数组必须被 json_encode"
1545 );
1546 }
1547
1548 #[tokio::test]
1549 async fn test_r5_php_autoresponse_html_type_with_array_returns_array_literal() {
1550 let mut headers = HeaderMap::new();
1554 headers.insert("accept", "text/html".parse().unwrap());
1555 let data = serde_json::json!([1, 2, 3]);
1556 let resp = auto_respond(&data, &headers);
1557
1558 assert_eq!(
1560 resp.headers().get("content-type").unwrap(),
1561 "text/html; charset=utf-8",
1562 "PHP autoResponse + isJson=false 时必须返回 HTML 类型"
1563 );
1564
1565 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1567 let body = String::from_utf8(bytes.to_vec()).unwrap();
1568 assert_eq!(
1569 body, "Array",
1570 "PHP autoResponse + isJson=false 时数组必须输出字面量 'Array'(PHP bug 复刻)"
1571 );
1572 }
1573
1574 #[tokio::test]
1575 async fn test_r5_php_autoresponse_html_type_with_object_returns_array_literal() {
1576 let mut headers = HeaderMap::new();
1579 headers.insert("accept", "text/html".parse().unwrap());
1580 let data = serde_json::json!({"name": "alice", "age": 30});
1581 let resp = auto_respond(&data, &headers);
1582
1583 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1584 let body = String::from_utf8(bytes.to_vec()).unwrap();
1585 assert_eq!(
1586 body, "Array",
1587 "PHP autoResponse + isJson=false 时关联数组也输出字面量 'Array'(PHP bug 复刻)"
1588 );
1589 }
1590
1591 #[tokio::test]
1592 async fn test_r5_php_autoresponse_html_type_with_string_returns_string() {
1593 let mut headers = HeaderMap::new();
1596 headers.insert("accept", "text/html".parse().unwrap());
1597 let data = Value::String("Hello World".to_string());
1598 let resp = auto_respond(&data, &headers);
1599
1600 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1601 let body = String::from_utf8(bytes.to_vec()).unwrap();
1602 assert_eq!(
1603 body, "Hello World",
1604 "PHP autoResponse + isJson=false + 字符串时必须原样输出字符串内容"
1605 );
1606 }
1607
1608 #[tokio::test]
1609 async fn test_r5_php_autoresponse_no_accept_header_returns_html() {
1610 let headers = HeaderMap::new();
1612 let data = serde_json::json!({"id": 1});
1613 let resp = auto_respond(&data, &headers);
1614
1615 assert_eq!(
1616 resp.headers().get("content-type").unwrap(),
1617 "text/html; charset=utf-8",
1618 "无 Accept 头时 PHP isJson() 返回 false,必须返回 HTML 类型"
1619 );
1620 }
1621
1622 #[tokio::test]
1623 async fn test_r5_php_autoresponse_wildcard_accept_returns_html() {
1624 let mut headers = HeaderMap::new();
1626 headers.insert("accept", "*/*".parse().unwrap());
1627 let data = serde_json::json!({"id": 1});
1628 let resp = auto_respond(&data, &headers);
1629
1630 assert_eq!(
1631 resp.headers().get("content-type").unwrap(),
1632 "text/html; charset=utf-8",
1633 "Accept: */* 时 PHP isJson() 返回 false,必须返回 HTML 类型"
1634 );
1635 }
1636
1637 #[tokio::test]
1638 async fn test_r5_php_autoresponse_mixed_accept_with_json() {
1639 let mut headers = HeaderMap::new();
1642 headers.insert(
1643 "accept",
1644 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1645 .parse()
1646 .unwrap(),
1647 );
1648 let data = serde_json::json!({"id": 1});
1649 let resp = auto_respond(&data, &headers);
1650
1651 assert_eq!(
1652 resp.headers().get("content-type").unwrap(),
1653 "application/json; charset=utf-8",
1654 "Accept 头含 json MIME 时 PHP isJson() 返回 true,必须返回 JSON 类型"
1655 );
1656 }
1657
1658 #[test]
1659 fn test_r5_php_isjson_case_insensitive_alignment() {
1660 let mut headers_upper = HeaderMap::new();
1663 headers_upper.insert("accept", "APPLICATION/JSON".parse().unwrap());
1664 assert!(
1665 is_json_request(&headers_upper),
1666 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1667 );
1668
1669 let mut headers_mixed = HeaderMap::new();
1670 headers_mixed.insert("accept", "Application/Json".parse().unwrap());
1671 assert!(
1672 is_json_request(&headers_mixed),
1673 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1674 );
1675 }
1676
1677 #[tokio::test]
1678 async fn test_r5_php_default_response_type_json_is_project_main_strategy() {
1679 let headers = HeaderMap::new(); let data = serde_json::json!({"id": 1, "name": "alice"});
1684
1685 let resp = DefaultResponseType::Json.respond(&data, &headers);
1687 assert_eq!(
1688 resp.headers().get("content-type").unwrap(),
1689 "application/json; charset=utf-8",
1690 "项目主策略:默认返回 JSON,不受 Accept 头影响"
1691 );
1692
1693 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1694 let body = String::from_utf8(bytes.to_vec()).unwrap();
1695 assert_eq!(
1696 body, r#"{"id":1,"name":"alice"}"#,
1697 "项目主策略:默认返回 JSON 编码的内容"
1698 );
1699 }
1700
1701 #[tokio::test]
1702 async fn test_r5_php_json_response_default_json_strategy() {
1703 async fn handler() -> JsonResponse {
1707 JsonResponse(serde_json::json!({"code": 1, "msg": "ok", "data": {"id": 1}}))
1708 }
1709
1710 let router = axum::Router::new().route("/", axum::routing::get(handler));
1711
1712 let req = Request::builder()
1714 .method(Method::GET)
1715 .uri("/")
1716 .header("accept", "text/html")
1717 .body(Body::empty())
1718 .unwrap();
1719 let resp = router.oneshot(req).await.unwrap();
1720
1721 assert_eq!(
1722 resp.headers().get("content-type").unwrap(),
1723 "application/json; charset=utf-8",
1724 "项目主策略:JsonResponse IntoResponse 始终返回 JSON,不受 Accept 头影响"
1725 );
1726
1727 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1728 let body = String::from_utf8(bytes.to_vec()).unwrap();
1729 assert_eq!(body, r#"{"code":1,"msg":"ok","data":{"id":1}}"#);
1730 }
1731}