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 pub fn to_json_bytes(&self) -> bytes::Bytes {
129 let vec = serde_json::to_vec(&self.to_value())
130 .expect("ApiResponse::to_json_bytes: serde_json::to_vec infallible for Value");
131 bytes::Bytes::from(vec)
132 }
133}
134
135impl Serialize for ApiResponse {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: serde::Serializer,
139 {
140 self.to_value().serialize(serializer)
141 }
142}
143
144impl IntoResponse for ApiResponse {
150 fn into_response(self) -> Response {
151 let body = self.to_json_string();
152 (
153 StatusCode::OK,
154 [(
155 axum::http::header::CONTENT_TYPE,
156 "application/json; charset=utf-8",
157 )],
158 body,
159 )
160 .into_response()
161 }
162}
163
164#[tracing::instrument(skip(msg, data))]
168pub fn render_json(code: i32, msg: impl Into<String>, data: Value) -> Response {
169 ApiResponse::new(code, msg, data).into_response()
170}
171
172#[tracing::instrument(skip(data, msg))]
176pub fn render_success(data: Value, msg: impl Into<String>) -> Response {
177 ApiResponse::success(data, msg).into_response()
178}
179
180#[tracing::instrument(skip(msg))]
184pub fn render_error(msg: impl Into<String>) -> Response {
185 ApiResponse::error(msg).into_response()
186}
187
188#[tracing::instrument(skip(msg, data))]
192pub fn render_error_with_code(code: i32, msg: impl Into<String>, data: Value) -> Response {
193 ApiResponse::error_with_code(code, msg, data).into_response()
194}
195
196use axum::http::{header, HeaderMap};
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
253pub enum DefaultResponseType {
254 #[default]
259 Json,
260
261 Html,
265
266 Auto,
271}
272
273impl DefaultResponseType {
274 pub fn respond(&self, data: &Value, headers: &HeaderMap) -> Response {
285 match self {
286 DefaultResponseType::Json => respond(data),
287 DefaultResponseType::Html => respond_html(data.to_string()),
288 DefaultResponseType::Auto => auto_respond(data, headers),
289 }
290 }
291}
292
293pub fn is_json_request(headers: &HeaderMap) -> bool {
318 if let Some(accept) = headers.get(header::ACCEPT) {
319 if let Ok(accept_str) = accept.to_str() {
320 return accept_str.to_lowercase().contains("json");
325 }
326 }
327 false
328}
329
330#[tracing::instrument(skip(data))]
348pub fn respond(data: &Value) -> Response {
349 let body = data.to_string();
350 (
351 StatusCode::OK,
352 [(header::CONTENT_TYPE, "application/json; charset=utf-8")],
353 body,
354 )
355 .into_response()
356}
357
358#[tracing::instrument(skip(content))]
370pub fn respond_html(content: impl Into<String>) -> Response {
371 (
372 StatusCode::OK,
373 [(header::CONTENT_TYPE, "text/html; charset=utf-8")],
374 content.into(),
375 )
376 .into_response()
377}
378
379#[tracing::instrument(skip(content))]
391pub fn respond_text(content: impl Into<String>) -> Response {
392 (
393 StatusCode::OK,
394 [(header::CONTENT_TYPE, "text/plain; charset=utf-8")],
395 content.into(),
396 )
397 .into_response()
398}
399
400#[tracing::instrument(skip(data, headers))]
434pub fn auto_respond(data: &Value, headers: &HeaderMap) -> Response {
435 if is_json_request(headers) {
436 respond(data)
438 } else {
439 let content = match data {
442 Value::Array(_) | Value::Object(_) => "Array".to_string(),
443 Value::String(s) => s.clone(),
444 Value::Null => String::new(),
445 _ => data.to_string(),
446 };
447 respond_html(content)
448 }
449}
450
451#[derive(Debug, Clone)]
484pub struct JsonResponse(pub Value);
485
486impl From<Value> for JsonResponse {
487 fn from(v: Value) -> Self {
488 JsonResponse(v)
489 }
490}
491
492impl IntoResponse for JsonResponse {
493 fn into_response(self) -> Response {
494 respond(&self.0)
495 }
496}
497
498const JSONP_CALLBACK_PATTERN: &str = r"^[a-zA-Z_][a-zA-Z0-9_.]*$";
513
514pub fn is_valid_jsonp_callback(callback: &str) -> bool {
527 if callback.is_empty() || callback.len() > 128 {
528 return false;
529 }
530 regex::Regex::new(JSONP_CALLBACK_PATTERN)
531 .map(|re| re.is_match(callback))
532 .unwrap_or(false)
533}
534
535#[tracing::instrument(skip(data))]
553pub fn respond_jsonp(callback: &str, data: &Value) -> Response {
554 if !is_valid_jsonp_callback(callback) {
555 return (
556 StatusCode::BAD_REQUEST,
557 [(header::CONTENT_TYPE, "text/plain; charset=utf-8")],
558 "Invalid JSONP callback name".to_string(),
559 )
560 .into_response();
561 }
562
563 let json_str = data.to_string();
564 let body = format!("{callback}({json_str});");
565
566 (
567 StatusCode::OK,
568 [(
569 header::CONTENT_TYPE,
570 "application/javascript; charset=utf-8",
571 )],
572 body,
573 )
574 .into_response()
575}
576
577#[derive(Debug, Clone)]
593pub struct JsonpResponse(pub String, pub Value);
594
595impl IntoResponse for JsonpResponse {
596 fn into_response(self) -> Response {
597 respond_jsonp(&self.0, &self.1)
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604 use axum::body::Body;
605 use axum::http::{Method, Request};
606 use http_body_util::BodyExt;
607 use tower::ServiceExt;
608
609 #[test]
614 fn test_api_response_new() {
615 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
616 assert_eq!(resp.code, 1);
617 assert_eq!(resp.msg, "ok");
618 assert!(resp.data.is_object());
619 }
620
621 #[test]
622 fn test_api_response_success() {
623 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
624 assert_eq!(resp.code, 1);
625 assert_eq!(resp.msg, "ok");
626 assert_eq!(resp.data["id"], 1);
627 }
628
629 #[test]
630 fn test_api_response_success_empty() {
631 let resp = ApiResponse::success_empty();
632 assert_eq!(resp.code, 1);
633 assert_eq!(resp.msg, "");
634 assert!(resp.data.is_object());
635 assert!(resp.data.as_object().unwrap().is_empty());
636 }
637
638 #[test]
639 fn test_api_response_error() {
640 let resp = ApiResponse::error("参数错误");
641 assert_eq!(resp.code, 0);
642 assert_eq!(resp.msg, "参数错误");
643 assert!(resp.data.is_object());
644 }
645
646 #[test]
647 fn test_api_response_error_with_data() {
648 let resp = ApiResponse::error_with_data("失败", serde_json::json!({"field": "name"}));
649 assert_eq!(resp.code, 0);
650 assert_eq!(resp.msg, "失败");
651 assert_eq!(resp.data["field"], "name");
652 }
653
654 #[test]
655 fn test_api_response_error_with_code() {
656 let resp = ApiResponse::error_with_code(-1, "未登录", Value::Object(Map::new()));
657 assert_eq!(resp.code, -1);
658 assert_eq!(resp.msg, "未登录");
659 }
660
661 #[test]
662 fn test_api_response_to_value_field_order() {
663 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
664 let value = resp.to_value();
665 let obj = value.as_object().unwrap();
666
667 let keys: Vec<&String> = obj.keys().collect();
669 assert_eq!(keys, vec!["code", "msg", "data"]);
670 }
671
672 #[test]
673 fn test_api_response_to_value_content() {
674 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1, "name": "alice"}));
675 let value = resp.to_value();
676 assert_eq!(value["code"], 1);
677 assert_eq!(value["msg"], "ok");
678 assert_eq!(value["data"]["id"], 1);
679 assert_eq!(value["data"]["name"], "alice");
680 }
681
682 #[test]
683 fn test_api_response_to_json_string() {
684 let resp = ApiResponse::new(1, "ok", serde_json::json!({}));
685 let json_str = resp.to_json_string();
686 let expected = r#"{"code":1,"msg":"ok","data":{}}"#;
688 assert_eq!(json_str, expected);
689 }
690
691 #[test]
692 fn test_api_response_to_json_string_with_data() {
693 let resp = ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok");
694 let json_str = resp.to_json_string();
695 let expected = r#"{"code":1,"msg":"ok","data":{"id":1,"name":"alice"}}"#;
696 assert_eq!(json_str, expected);
697 }
698
699 #[test]
700 fn test_api_response_to_json_bytes() {
701 let resp = ApiResponse::new(1, "ok", serde_json::json!({}));
702 let json_bytes = resp.to_json_bytes();
703 let expected = r#"{"code":1,"msg":"ok","data":{}}"#;
704 assert_eq!(json_bytes.as_ref(), expected.as_bytes());
705 }
706
707 #[test]
708 fn test_api_response_to_json_bytes_with_data() {
709 let resp = ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok");
710 let json_bytes = resp.to_json_bytes();
711 let expected = r#"{"code":1,"msg":"ok","data":{"id":1,"name":"alice"}}"#;
712 assert_eq!(json_bytes.as_ref(), expected.as_bytes());
713 }
714
715 #[test]
716 fn test_api_response_to_json_bytes_matches_string() {
717 let resp = ApiResponse::new(-1, "未登录", serde_json::json!({"token": null}));
719 let json_str = resp.to_json_string();
720 let json_bytes = resp.to_json_bytes();
721 assert_eq!(json_bytes.as_ref(), json_str.as_bytes());
722 }
723
724 #[test]
725 fn test_api_response_to_json_bytes_empty() {
726 let resp = ApiResponse::success_empty();
727 let json_bytes = resp.to_json_bytes();
728 let expected = r#"{"code":1,"msg":"","data":{}}"#;
729 assert_eq!(json_bytes.as_ref(), expected.as_bytes());
730 }
731
732 #[test]
733 fn test_api_response_serialize_via_serde() {
734 let resp = ApiResponse::new(0, "失败", Value::Object(Map::new()));
735 let json_str = serde_json::to_string(&resp).unwrap();
736 assert_eq!(json_str, r#"{"code":0,"msg":"失败","data":{}}"#);
737 }
738
739 #[test]
740 fn test_api_response_clone() {
741 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
742 let cloned = resp.clone();
743 assert_eq!(cloned.code, resp.code);
744 assert_eq!(cloned.msg, resp.msg);
745 assert_eq!(cloned.data, resp.data);
746 }
747
748 #[test]
749 fn test_api_response_debug_format() {
750 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
751 let debug_str = format!("{resp:?}");
752 assert!(debug_str.contains("ApiResponse"));
753 assert!(debug_str.contains("code: 1"));
754 assert!(debug_str.contains("\"ok\""));
755 }
756
757 #[test]
762 fn test_render_json_returns_response() {
763 let resp = render_json(1, "ok", serde_json::json!({}));
764 assert_eq!(resp.status(), StatusCode::OK);
765 assert_eq!(
766 resp.headers().get("content-type").unwrap(),
767 "application/json; charset=utf-8"
768 );
769 }
770
771 #[test]
772 fn test_render_success_returns_response() {
773 let resp = render_success(serde_json::json!({"id": 1}), "ok");
774 assert_eq!(resp.status(), StatusCode::OK);
775 }
776
777 #[test]
778 fn test_render_error_returns_response() {
779 let resp = render_error("参数错误");
780 assert_eq!(resp.status(), StatusCode::OK); }
782
783 #[test]
784 fn test_render_error_with_code_returns_response() {
785 let resp = render_error_with_code(-1, "未登录", serde_json::json!({}));
786 assert_eq!(resp.status(), StatusCode::OK);
787 }
788
789 #[tokio::test]
794 async fn test_api_response_as_handler_return() {
795 async fn handler() -> ApiResponse {
796 ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok")
797 }
798
799 let router = axum::Router::new().route("/", axum::routing::get(handler));
800 let req = Request::builder()
801 .method(Method::GET)
802 .uri("/")
803 .body(Body::empty())
804 .unwrap();
805 let resp = router.oneshot(req).await.unwrap();
806
807 assert_eq!(resp.status(), StatusCode::OK);
808 assert_eq!(
809 resp.headers().get("content-type").unwrap(),
810 "application/json; charset=utf-8"
811 );
812
813 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
814 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
815 let json: Value = serde_json::from_str(&body_str).unwrap();
816
817 assert_eq!(json["code"], 1);
818 assert_eq!(json["msg"], "ok");
819 assert_eq!(json["data"]["id"], 1);
820 assert_eq!(json["data"]["name"], "alice");
821 }
822
823 #[tokio::test]
824 async fn test_render_error_handler_return() {
825 async fn handler() -> Response {
826 render_error("参数错误")
827 }
828
829 let router = axum::Router::new().route("/", axum::routing::post(handler));
830 let req = Request::builder()
831 .method(Method::POST)
832 .uri("/")
833 .body(Body::empty())
834 .unwrap();
835 let resp = router.oneshot(req).await.unwrap();
836
837 assert_eq!(resp.status(), StatusCode::OK);
838
839 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
840 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
841 let json: Value = serde_json::from_str(&body_str).unwrap();
842
843 assert_eq!(json["code"], 0);
844 assert_eq!(json["msg"], "参数错误");
845 assert!(json["data"].is_object());
846 }
847
848 #[tokio::test]
849 async fn test_response_body_exact_format() {
850 async fn handler() -> ApiResponse {
852 ApiResponse::success_empty()
853 }
854
855 let router = axum::Router::new().route("/", axum::routing::get(handler));
856 let req = Request::builder()
857 .method(Method::GET)
858 .uri("/")
859 .body(Body::empty())
860 .unwrap();
861 let resp = router.oneshot(req).await.unwrap();
862
863 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
864 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
865 assert_eq!(body_str, r#"{"code":1,"msg":"","data":{}}"#);
866 }
867
868 #[tokio::test]
869 async fn test_response_with_complex_data() {
870 async fn handler() -> ApiResponse {
871 ApiResponse::success(
872 serde_json::json!({
873 "list": [{"id": 1}, {"id": 2}],
874 "total": 2,
875 "page": 1,
876 "size": 10
877 }),
878 "查询成功",
879 )
880 }
881
882 let router = axum::Router::new().route("/", axum::routing::get(handler));
883 let req = Request::builder()
884 .method(Method::GET)
885 .uri("/")
886 .body(Body::empty())
887 .unwrap();
888 let resp = router.oneshot(req).await.unwrap();
889
890 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
891 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
892 let json: Value = serde_json::from_str(&body_str).unwrap();
893
894 assert_eq!(json["code"], 1);
895 assert_eq!(json["msg"], "查询成功");
896 assert_eq!(json["data"]["total"], 2);
897 assert_eq!(json["data"]["list"][0]["id"], 1);
898 assert_eq!(json["data"]["list"][1]["id"], 2);
899 }
900
901 #[tokio::test]
902 async fn test_response_with_various_error_codes() {
903 let test_cases = vec![
905 (0, "业务失败"),
906 (-1, "未登录"),
907 (-2, "用户不存在"),
908 (-3, "用户被禁用"),
909 (403, "禁止访问"),
910 (404, "资源不存在"),
911 (422, "参数校验失败"),
912 (500, "数据库错误"),
913 ];
914
915 for (code, msg) in test_cases {
916 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
917 let json_str = resp.to_json_string();
918 let json: Value = serde_json::from_str(&json_str).unwrap();
919 assert_eq!(json["code"], code);
920 assert_eq!(json["msg"], msg);
921 }
922 }
923
924 #[test]
949 fn test_php_consistency_render_json_compact_field_order() {
950 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
954 let value = resp.to_value();
955 let obj = value.as_object().unwrap();
956 let keys: Vec<&String> = obj.keys().collect();
957 assert_eq!(
958 keys,
959 vec!["code", "msg", "data"],
960 "字段顺序必须为 code → msg → data(对齐 PHP compact())"
961 );
962 assert_eq!(value["code"], 1);
963 assert_eq!(value["msg"], "ok");
964 assert_eq!(value["data"]["id"], 1);
965 }
966
967 #[test]
968 fn test_php_consistency_render_json_default_values() {
969 let resp = ApiResponse::new(1, "", Value::Object(Map::new()));
972 let json_str = resp.to_json_string();
973 assert_eq!(
974 json_str, r#"{"code":1,"msg":"","data":{}}"#,
975 "默认值必须与 PHP renderJson() 一致:code=1, msg='', data={{}}"
976 );
977 }
978
979 #[test]
980 fn test_php_consistency_render_success_calls_render_json_with_code_1() {
981 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
984 assert_eq!(
985 resp.code, 1,
986 "renderSuccess 必须 code=1(对齐 PHP renderJson(1, ...))"
987 );
988 assert_eq!(resp.msg, "ok");
989 assert_eq!(resp.data["id"], 1);
990
991 let json_str = resp.to_json_string();
993 let expected = r#"{"code":1,"msg":"ok","data":{"id":1}}"#;
994 assert_eq!(json_str, expected);
995 }
996
997 #[test]
998 fn test_php_consistency_render_error_default_code_is_0() {
999 let resp = ApiResponse::error("参数错误");
1002 assert_eq!(
1003 resp.code, 0,
1004 "renderError 默认 code=0(对齐 PHP 默认参数 $code = 0)"
1005 );
1006 assert_eq!(resp.msg, "参数错误");
1007 assert!(
1008 resp.data.is_object(),
1009 "renderError 默认 data 为空对象(对齐 PHP $data = [])"
1010 );
1011
1012 let response = render_error("参数错误");
1014 assert_eq!(response.status(), StatusCode::OK);
1015 }
1016
1017 #[test]
1018 fn test_php_consistency_render_error_with_custom_code_aligns_base_exception() {
1019 let test_cases = vec![
1026 (-1i32, "未登录"),
1027 (-2, "用户不存在"),
1028 (-3, "用户被禁用"),
1029 (0, "业务失败"),
1030 ];
1031
1032 for (code, msg) in test_cases {
1033 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
1034 let json_str = resp.to_json_string();
1035 let json: Value = serde_json::from_str(&json_str).unwrap();
1036 assert_eq!(
1037 json["code"], code,
1038 "自定义错误码必须与 PHP BaseException 约定一致"
1039 );
1040 assert_eq!(json["msg"], msg);
1041 assert!(json.get("data").is_some(), "data 字段必须存在");
1043 }
1044 }
1045
1046 #[test]
1063 fn test_default_response_type_default_is_json() {
1064 let t = DefaultResponseType::default();
1066 assert_eq!(t, DefaultResponseType::Json);
1067 }
1068
1069 #[test]
1070 fn test_default_response_type_variants_eq() {
1071 assert_eq!(DefaultResponseType::Json, DefaultResponseType::Json);
1072 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Html);
1073 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Auto);
1074 assert_ne!(DefaultResponseType::Html, DefaultResponseType::Auto);
1075 }
1076
1077 #[test]
1078 fn test_default_response_type_clone_copy() {
1079 let t = DefaultResponseType::Json;
1080 let t2 = t; assert_eq!(t, t2);
1082 let t3 = t;
1084 assert_eq!(t, t3);
1085 }
1086
1087 #[test]
1088 fn test_default_response_type_debug() {
1089 let debug = format!("{:?}", DefaultResponseType::Json);
1090 assert!(debug.contains("Json"));
1091 let debug = format!("{:?}", DefaultResponseType::Html);
1092 assert!(debug.contains("Html"));
1093 let debug = format!("{:?}", DefaultResponseType::Auto);
1094 assert!(debug.contains("Auto"));
1095 }
1096
1097 #[test]
1098 fn test_default_response_type_respond_json() {
1099 let headers = HeaderMap::new();
1101 let data = serde_json::json!({"id": 1});
1102 let resp = DefaultResponseType::Json.respond(&data, &headers);
1103 assert_eq!(resp.status(), StatusCode::OK);
1104 assert_eq!(
1105 resp.headers().get("content-type").unwrap(),
1106 "application/json; charset=utf-8"
1107 );
1108 }
1109
1110 #[test]
1111 fn test_default_response_type_respond_html() {
1112 let headers = HeaderMap::new();
1114 let data = serde_json::json!({"id": 1});
1115 let resp = DefaultResponseType::Html.respond(&data, &headers);
1116 assert_eq!(resp.status(), StatusCode::OK);
1117 assert_eq!(
1118 resp.headers().get("content-type").unwrap(),
1119 "text/html; charset=utf-8"
1120 );
1121 }
1122
1123 #[tokio::test]
1124 async fn test_default_response_type_respond_html_body() {
1125 let headers = HeaderMap::new();
1126 let data = serde_json::json!({"id": 1});
1127 let resp = DefaultResponseType::Html.respond(&data, &headers);
1128 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1129 let body = String::from_utf8(bytes.to_vec()).unwrap();
1130 assert_eq!(body, r#"{"id":1}"#);
1132 }
1133
1134 #[tokio::test]
1135 async fn test_default_response_type_respond_auto_with_json_accept() {
1136 let mut headers = HeaderMap::new();
1138 headers.insert("accept", "application/json".parse().unwrap());
1139 let data = serde_json::json!({"id": 1});
1140 let resp = DefaultResponseType::Auto.respond(&data, &headers);
1141 assert_eq!(
1142 resp.headers().get("content-type").unwrap(),
1143 "application/json; charset=utf-8"
1144 );
1145 }
1146
1147 #[tokio::test]
1148 async fn test_default_response_type_respond_auto_with_html_accept() {
1149 let mut headers = HeaderMap::new();
1151 headers.insert("accept", "text/html".parse().unwrap());
1152 let data = serde_json::json!({"id": 1});
1153 let resp = DefaultResponseType::Auto.respond(&data, &headers);
1154 assert_eq!(
1155 resp.headers().get("content-type").unwrap(),
1156 "text/html; charset=utf-8"
1157 );
1158 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1159 let body = String::from_utf8(bytes.to_vec()).unwrap();
1160 assert_eq!(body, "Array");
1162 }
1163
1164 #[test]
1167 fn test_is_json_request_with_application_json() {
1168 let mut headers = HeaderMap::new();
1169 headers.insert("accept", "application/json".parse().unwrap());
1170 assert!(is_json_request(&headers));
1171 }
1172
1173 #[test]
1174 fn test_is_json_request_with_text_json() {
1175 let mut headers = HeaderMap::new();
1176 headers.insert("accept", "text/json".parse().unwrap());
1177 assert!(is_json_request(&headers));
1178 }
1179
1180 #[test]
1181 fn test_is_json_request_with_vnd_api_json() {
1182 let mut headers = HeaderMap::new();
1183 headers.insert("accept", "application/vnd.api+json".parse().unwrap());
1184 assert!(is_json_request(&headers));
1185 }
1186
1187 #[test]
1188 fn test_is_json_request_with_wildcard() {
1189 let mut headers = HeaderMap::new();
1191 headers.insert("accept", "*/*".parse().unwrap());
1192 assert!(!is_json_request(&headers));
1193 }
1194
1195 #[test]
1196 fn test_is_json_request_with_text_html() {
1197 let mut headers = HeaderMap::new();
1198 headers.insert("accept", "text/html".parse().unwrap());
1199 assert!(!is_json_request(&headers));
1200 }
1201
1202 #[test]
1203 fn test_is_json_request_no_accept_header() {
1204 let headers = HeaderMap::new();
1205 assert!(!is_json_request(&headers));
1206 }
1207
1208 #[test]
1209 fn test_is_json_request_case_insensitive() {
1210 let mut headers = HeaderMap::new();
1212 headers.insert("accept", "APPLICATION/JSON".parse().unwrap());
1213 assert!(is_json_request(&headers));
1214 }
1215
1216 #[test]
1217 fn test_is_json_request_mixed_accept() {
1218 let mut headers = HeaderMap::new();
1220 headers.insert(
1221 "accept",
1222 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1223 .parse()
1224 .unwrap(),
1225 );
1226 assert!(is_json_request(&headers));
1227 }
1228
1229 #[test]
1232 fn test_respond_returns_json_content_type() {
1233 let data = serde_json::json!({"id": 1});
1234 let resp = respond(&data);
1235 assert_eq!(resp.status(), StatusCode::OK);
1236 assert_eq!(
1237 resp.headers().get("content-type").unwrap(),
1238 "application/json; charset=utf-8"
1239 );
1240 }
1241
1242 #[tokio::test]
1243 async fn test_respond_object_body() {
1244 let data = serde_json::json!({"id": 1, "name": "alice"});
1245 let resp = respond(&data);
1246 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1247 let body = String::from_utf8(bytes.to_vec()).unwrap();
1248 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1249 }
1250
1251 #[tokio::test]
1252 async fn test_respond_array_body() {
1253 let data = serde_json::json!([1, 2, 3]);
1254 let resp = respond(&data);
1255 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1256 let body = String::from_utf8(bytes.to_vec()).unwrap();
1257 assert_eq!(body, r#"[1,2,3]"#);
1258 }
1259
1260 #[tokio::test]
1261 async fn test_respond_string_value() {
1262 let data = Value::String("hello".to_string());
1263 let resp = respond(&data);
1264 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1265 let body = String::from_utf8(bytes.to_vec()).unwrap();
1266 assert_eq!(body, r#""hello""#);
1268 }
1269
1270 #[tokio::test]
1271 async fn test_respond_null_value() {
1272 let resp = respond(&Value::Null);
1273 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1274 let body = String::from_utf8(bytes.to_vec()).unwrap();
1275 assert_eq!(body, "null");
1276 }
1277
1278 #[tokio::test]
1279 async fn test_respond_number_value() {
1280 let resp = respond(&serde_json::json!(42));
1281 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1282 let body = String::from_utf8(bytes.to_vec()).unwrap();
1283 assert_eq!(body, "42");
1284 }
1285
1286 #[tokio::test]
1287 async fn test_respond_bool_value() {
1288 let resp = respond(&serde_json::json!(true));
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, "true");
1292 }
1293
1294 #[test]
1297 fn test_respond_html_content_type() {
1298 let resp = respond_html("<h1>Hello</h1>");
1299 assert_eq!(resp.status(), StatusCode::OK);
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_respond_html_body() {
1308 let resp = respond_html("<p>test</p>");
1309 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1310 let body = String::from_utf8(bytes.to_vec()).unwrap();
1311 assert_eq!(body, "<p>test</p>");
1312 }
1313
1314 #[tokio::test]
1315 async fn test_respond_html_empty() {
1316 let resp = respond_html("");
1317 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1318 let body = String::from_utf8(bytes.to_vec()).unwrap();
1319 assert_eq!(body, "");
1320 }
1321
1322 #[tokio::test]
1323 async fn test_respond_html_with_unicode() {
1324 let resp = respond_html("<p>你好世界</p>");
1325 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1326 let body = String::from_utf8(bytes.to_vec()).unwrap();
1327 assert_eq!(body, "<p>你好世界</p>");
1328 }
1329
1330 #[test]
1333 fn test_respond_text_content_type() {
1334 let resp = respond_text("plain text");
1335 assert_eq!(resp.status(), StatusCode::OK);
1336 assert_eq!(
1337 resp.headers().get("content-type").unwrap(),
1338 "text/plain; charset=utf-8"
1339 );
1340 }
1341
1342 #[tokio::test]
1343 async fn test_respond_text_body() {
1344 let resp = respond_text("OK");
1345 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1346 let body = String::from_utf8(bytes.to_vec()).unwrap();
1347 assert_eq!(body, "OK");
1348 }
1349
1350 #[tokio::test]
1353 async fn test_auto_respond_json_request_with_object() {
1354 let mut headers = HeaderMap::new();
1356 headers.insert("accept", "application/json".parse().unwrap());
1357 let data = serde_json::json!({"id": 1});
1358 let resp = auto_respond(&data, &headers);
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#"{"id":1}"#);
1366 }
1367
1368 #[tokio::test]
1369 async fn test_auto_respond_json_request_with_array() {
1370 let mut headers = HeaderMap::new();
1371 headers.insert("accept", "application/json".parse().unwrap());
1372 let data = serde_json::json!([1, 2, 3]);
1373 let resp = auto_respond(&data, &headers);
1374 assert_eq!(
1375 resp.headers().get("content-type").unwrap(),
1376 "application/json; charset=utf-8"
1377 );
1378 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1379 let body = String::from_utf8(bytes.to_vec()).unwrap();
1380 assert_eq!(body, r#"[1,2,3]"#);
1381 }
1382
1383 #[tokio::test]
1384 async fn test_auto_respond_html_request_with_object_returns_array_literal() {
1385 let mut headers = HeaderMap::new();
1387 headers.insert("accept", "text/html".parse().unwrap());
1388 let data = serde_json::json!({"id": 1});
1389 let resp = auto_respond(&data, &headers);
1390 assert_eq!(
1391 resp.headers().get("content-type").unwrap(),
1392 "text/html; charset=utf-8"
1393 );
1394 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1395 let body = String::from_utf8(bytes.to_vec()).unwrap();
1396 assert_eq!(body, "Array");
1397 }
1398
1399 #[tokio::test]
1400 async fn test_auto_respond_html_request_with_array_returns_array_literal() {
1401 let mut headers = HeaderMap::new();
1403 headers.insert("accept", "text/html".parse().unwrap());
1404 let data = serde_json::json!([1, 2, 3]);
1405 let resp = auto_respond(&data, &headers);
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, "Array");
1409 }
1410
1411 #[tokio::test]
1412 async fn test_auto_respond_html_request_with_string_returns_string() {
1413 let mut headers = HeaderMap::new();
1415 headers.insert("accept", "text/html".parse().unwrap());
1416 let data = Value::String("hello".to_string());
1417 let resp = auto_respond(&data, &headers);
1418 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1419 let body = String::from_utf8(bytes.to_vec()).unwrap();
1420 assert_eq!(body, "hello");
1421 }
1422
1423 #[tokio::test]
1424 async fn test_auto_respond_html_request_with_null_returns_empty() {
1425 let mut headers = HeaderMap::new();
1426 headers.insert("accept", "text/html".parse().unwrap());
1427 let resp = auto_respond(&Value::Null, &headers);
1428 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1429 let body = String::from_utf8(bytes.to_vec()).unwrap();
1430 assert_eq!(body, "");
1431 }
1432
1433 #[tokio::test]
1434 async fn test_auto_respond_html_request_with_number_returns_number_string() {
1435 let mut headers = HeaderMap::new();
1436 headers.insert("accept", "text/html".parse().unwrap());
1437 let resp = auto_respond(&serde_json::json!(42), &headers);
1438 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1439 let body = String::from_utf8(bytes.to_vec()).unwrap();
1440 assert_eq!(body, "42");
1441 }
1442
1443 #[tokio::test]
1444 async fn test_auto_respond_no_accept_header_returns_html() {
1445 let headers = HeaderMap::new();
1447 let data = serde_json::json!({"id": 1});
1448 let resp = auto_respond(&data, &headers);
1449 assert_eq!(
1450 resp.headers().get("content-type").unwrap(),
1451 "text/html; charset=utf-8"
1452 );
1453 }
1454
1455 #[tokio::test]
1456 async fn test_auto_respond_wildcard_accept_returns_html() {
1457 let mut headers = HeaderMap::new();
1459 headers.insert("accept", "*/*".parse().unwrap());
1460 let data = serde_json::json!({"id": 1});
1461 let resp = auto_respond(&data, &headers);
1462 assert_eq!(
1463 resp.headers().get("content-type").unwrap(),
1464 "text/html; charset=utf-8"
1465 );
1466 }
1467
1468 #[tokio::test]
1471 async fn test_json_response_into_response_object() {
1472 async fn handler() -> JsonResponse {
1473 JsonResponse(serde_json::json!({"id": 1, "name": "alice"}))
1474 }
1475
1476 let router = axum::Router::new().route("/", axum::routing::get(handler));
1477 let req = Request::builder()
1478 .method(Method::GET)
1479 .uri("/")
1480 .body(Body::empty())
1481 .unwrap();
1482 let resp = router.oneshot(req).await.unwrap();
1483
1484 assert_eq!(resp.status(), StatusCode::OK);
1485 assert_eq!(
1486 resp.headers().get("content-type").unwrap(),
1487 "application/json; charset=utf-8"
1488 );
1489 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1490 let body = String::from_utf8(bytes.to_vec()).unwrap();
1491 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1492 }
1493
1494 #[tokio::test]
1495 async fn test_json_response_into_response_array() {
1496 async fn handler() -> JsonResponse {
1497 JsonResponse(serde_json::json!([1, 2, 3]))
1498 }
1499
1500 let router = axum::Router::new().route("/", axum::routing::get(handler));
1501 let req = Request::builder()
1502 .method(Method::GET)
1503 .uri("/")
1504 .body(Body::empty())
1505 .unwrap();
1506 let resp = router.oneshot(req).await.unwrap();
1507
1508 assert_eq!(
1509 resp.headers().get("content-type").unwrap(),
1510 "application/json; charset=utf-8"
1511 );
1512 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1513 let body = String::from_utf8(bytes.to_vec()).unwrap();
1514 assert_eq!(body, r#"[1,2,3]"#);
1515 }
1516
1517 #[tokio::test]
1518 async fn test_json_response_into_response_string() {
1519 async fn handler() -> JsonResponse {
1520 JsonResponse(Value::String("hello".to_string()))
1521 }
1522
1523 let router = axum::Router::new().route("/", axum::routing::get(handler));
1524 let req = Request::builder()
1525 .method(Method::GET)
1526 .uri("/")
1527 .body(Body::empty())
1528 .unwrap();
1529 let resp = router.oneshot(req).await.unwrap();
1530
1531 assert_eq!(
1533 resp.headers().get("content-type").unwrap(),
1534 "application/json; charset=utf-8"
1535 );
1536 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1537 let body = String::from_utf8(bytes.to_vec()).unwrap();
1538 assert_eq!(body, r#""hello""#);
1539 }
1540
1541 #[tokio::test]
1542 async fn test_json_response_into_response_null() {
1543 async fn handler() -> JsonResponse {
1544 JsonResponse(Value::Null)
1545 }
1546
1547 let router = axum::Router::new().route("/", axum::routing::get(handler));
1548 let req = Request::builder()
1549 .method(Method::GET)
1550 .uri("/")
1551 .body(Body::empty())
1552 .unwrap();
1553 let resp = router.oneshot(req).await.unwrap();
1554
1555 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1556 let body = String::from_utf8(bytes.to_vec()).unwrap();
1557 assert_eq!(body, "null");
1558 }
1559
1560 #[tokio::test]
1561 async fn test_json_response_into_response_post_handler() {
1562 async fn handler() -> JsonResponse {
1564 JsonResponse(serde_json::json!({
1565 "code": 1,
1566 "msg": "success",
1567 "data": {"id": 12345, "status": "paid"}
1568 }))
1569 }
1570
1571 let router = axum::Router::new().route("/api/order", axum::routing::post(handler));
1572 let req = Request::builder()
1573 .method(Method::POST)
1574 .uri("/api/order")
1575 .body(Body::empty())
1576 .unwrap();
1577 let resp = router.oneshot(req).await.unwrap();
1578
1579 assert_eq!(resp.status(), StatusCode::OK);
1580 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1581 let body = String::from_utf8(bytes.to_vec()).unwrap();
1582 let json: Value = serde_json::from_str(&body).unwrap();
1583 assert_eq!(json["code"], 1);
1584 assert_eq!(json["msg"], "success");
1585 assert_eq!(json["data"]["id"], 12345);
1586 assert_eq!(json["data"]["status"], "paid");
1587 }
1588
1589 #[test]
1590 fn test_json_response_from_value() {
1591 let value = serde_json::json!({"id": 1});
1593 let json_resp: JsonResponse = value.clone().into();
1594 assert_eq!(json_resp.0, value);
1595 }
1596
1597 #[test]
1598 fn test_json_response_clone_debug() {
1599 let resp = JsonResponse(serde_json::json!({"id": 1}));
1600 let cloned = resp.clone();
1601 assert_eq!(resp.0, cloned.0);
1602
1603 let debug = format!("{resp:?}");
1604 assert!(debug.contains("JsonResponse"));
1605 }
1606
1607 #[test]
1630 fn test_r5_php_isjson_accept_application_json() {
1631 let mut headers = HeaderMap::new();
1633 headers.insert("accept", "application/json".parse().unwrap());
1634 assert!(
1635 is_json_request(&headers),
1636 "Accept: application/json 时 isJson() 必须返回 true(对齐 PHP)"
1637 );
1638 }
1639
1640 #[test]
1641 fn test_r5_php_isjson_accept_text_html() {
1642 let mut headers = HeaderMap::new();
1644 headers.insert("accept", "text/html".parse().unwrap());
1645 assert!(
1646 !is_json_request(&headers),
1647 "Accept: text/html 时 isJson() 必须返回 false(对齐 PHP)"
1648 );
1649 }
1650
1651 #[test]
1652 fn test_r5_php_isjson_accept_wildcard() {
1653 let mut headers = HeaderMap::new();
1655 headers.insert("accept", "*/*".parse().unwrap());
1656 assert!(
1657 !is_json_request(&headers),
1658 "Accept: */* 时 isJson() 必须返回 false(对齐 PHP type() 无匹配 MIME)"
1659 );
1660 }
1661
1662 #[test]
1663 fn test_r5_php_isjson_no_accept_header() {
1664 let headers = HeaderMap::new();
1666 assert!(
1667 !is_json_request(&headers),
1668 "无 Accept 头时 isJson() 必须返回 false(对齐 PHP)"
1669 );
1670 }
1671
1672 #[tokio::test]
1673 async fn test_r5_php_autoresponse_json_type_with_array() {
1674 let mut headers = HeaderMap::new();
1677 headers.insert("accept", "application/json".parse().unwrap());
1678 let data = serde_json::json!([1, 2, 3]);
1679 let resp = auto_respond(&data, &headers);
1680
1681 assert_eq!(
1683 resp.headers().get("content-type").unwrap(),
1684 "application/json; charset=utf-8",
1685 "PHP autoResponse + isJson=true 时必须返回 JSON 类型"
1686 );
1687
1688 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1690 let body = String::from_utf8(bytes.to_vec()).unwrap();
1691 assert_eq!(
1692 body, "[1,2,3]",
1693 "PHP autoResponse + isJson=true 时数组必须被 json_encode"
1694 );
1695 }
1696
1697 #[tokio::test]
1698 async fn test_r5_php_autoresponse_html_type_with_array_returns_array_literal() {
1699 let mut headers = HeaderMap::new();
1703 headers.insert("accept", "text/html".parse().unwrap());
1704 let data = serde_json::json!([1, 2, 3]);
1705 let resp = auto_respond(&data, &headers);
1706
1707 assert_eq!(
1709 resp.headers().get("content-type").unwrap(),
1710 "text/html; charset=utf-8",
1711 "PHP autoResponse + isJson=false 时必须返回 HTML 类型"
1712 );
1713
1714 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1716 let body = String::from_utf8(bytes.to_vec()).unwrap();
1717 assert_eq!(
1718 body, "Array",
1719 "PHP autoResponse + isJson=false 时数组必须输出字面量 'Array'(PHP bug 复刻)"
1720 );
1721 }
1722
1723 #[tokio::test]
1724 async fn test_r5_php_autoresponse_html_type_with_object_returns_array_literal() {
1725 let mut headers = HeaderMap::new();
1728 headers.insert("accept", "text/html".parse().unwrap());
1729 let data = serde_json::json!({"name": "alice", "age": 30});
1730 let resp = auto_respond(&data, &headers);
1731
1732 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1733 let body = String::from_utf8(bytes.to_vec()).unwrap();
1734 assert_eq!(
1735 body, "Array",
1736 "PHP autoResponse + isJson=false 时关联数组也输出字面量 'Array'(PHP bug 复刻)"
1737 );
1738 }
1739
1740 #[tokio::test]
1741 async fn test_r5_php_autoresponse_html_type_with_string_returns_string() {
1742 let mut headers = HeaderMap::new();
1745 headers.insert("accept", "text/html".parse().unwrap());
1746 let data = Value::String("Hello World".to_string());
1747 let resp = auto_respond(&data, &headers);
1748
1749 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1750 let body = String::from_utf8(bytes.to_vec()).unwrap();
1751 assert_eq!(
1752 body, "Hello World",
1753 "PHP autoResponse + isJson=false + 字符串时必须原样输出字符串内容"
1754 );
1755 }
1756
1757 #[tokio::test]
1758 async fn test_r5_php_autoresponse_no_accept_header_returns_html() {
1759 let headers = HeaderMap::new();
1761 let data = serde_json::json!({"id": 1});
1762 let resp = auto_respond(&data, &headers);
1763
1764 assert_eq!(
1765 resp.headers().get("content-type").unwrap(),
1766 "text/html; charset=utf-8",
1767 "无 Accept 头时 PHP isJson() 返回 false,必须返回 HTML 类型"
1768 );
1769 }
1770
1771 #[tokio::test]
1772 async fn test_r5_php_autoresponse_wildcard_accept_returns_html() {
1773 let mut headers = HeaderMap::new();
1775 headers.insert("accept", "*/*".parse().unwrap());
1776 let data = serde_json::json!({"id": 1});
1777 let resp = auto_respond(&data, &headers);
1778
1779 assert_eq!(
1780 resp.headers().get("content-type").unwrap(),
1781 "text/html; charset=utf-8",
1782 "Accept: */* 时 PHP isJson() 返回 false,必须返回 HTML 类型"
1783 );
1784 }
1785
1786 #[tokio::test]
1787 async fn test_r5_php_autoresponse_mixed_accept_with_json() {
1788 let mut headers = HeaderMap::new();
1791 headers.insert(
1792 "accept",
1793 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1794 .parse()
1795 .unwrap(),
1796 );
1797 let data = serde_json::json!({"id": 1});
1798 let resp = auto_respond(&data, &headers);
1799
1800 assert_eq!(
1801 resp.headers().get("content-type").unwrap(),
1802 "application/json; charset=utf-8",
1803 "Accept 头含 json MIME 时 PHP isJson() 返回 true,必须返回 JSON 类型"
1804 );
1805 }
1806
1807 #[test]
1808 fn test_r5_php_isjson_case_insensitive_alignment() {
1809 let mut headers_upper = HeaderMap::new();
1812 headers_upper.insert("accept", "APPLICATION/JSON".parse().unwrap());
1813 assert!(
1814 is_json_request(&headers_upper),
1815 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1816 );
1817
1818 let mut headers_mixed = HeaderMap::new();
1819 headers_mixed.insert("accept", "Application/Json".parse().unwrap());
1820 assert!(
1821 is_json_request(&headers_mixed),
1822 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1823 );
1824 }
1825
1826 #[tokio::test]
1827 async fn test_r5_php_default_response_type_json_is_project_main_strategy() {
1828 let headers = HeaderMap::new(); let data = serde_json::json!({"id": 1, "name": "alice"});
1833
1834 let resp = DefaultResponseType::Json.respond(&data, &headers);
1836 assert_eq!(
1837 resp.headers().get("content-type").unwrap(),
1838 "application/json; charset=utf-8",
1839 "项目主策略:默认返回 JSON,不受 Accept 头影响"
1840 );
1841
1842 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1843 let body = String::from_utf8(bytes.to_vec()).unwrap();
1844 assert_eq!(
1845 body, r#"{"id":1,"name":"alice"}"#,
1846 "项目主策略:默认返回 JSON 编码的内容"
1847 );
1848 }
1849
1850 #[tokio::test]
1851 async fn test_r5_php_json_response_default_json_strategy() {
1852 async fn handler() -> JsonResponse {
1856 JsonResponse(serde_json::json!({"code": 1, "msg": "ok", "data": {"id": 1}}))
1857 }
1858
1859 let router = axum::Router::new().route("/", axum::routing::get(handler));
1860
1861 let req = Request::builder()
1863 .method(Method::GET)
1864 .uri("/")
1865 .header("accept", "text/html")
1866 .body(Body::empty())
1867 .unwrap();
1868 let resp = router.oneshot(req).await.unwrap();
1869
1870 assert_eq!(
1871 resp.headers().get("content-type").unwrap(),
1872 "application/json; charset=utf-8",
1873 "项目主策略:JsonResponse IntoResponse 始终返回 JSON,不受 Accept 头影响"
1874 );
1875
1876 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1877 let body = String::from_utf8(bytes.to_vec()).unwrap();
1878 assert_eq!(body, r#"{"code":1,"msg":"ok","data":{"id":1}}"#);
1879 }
1880
1881 #[test]
1893 fn test_is_valid_jsonp_callback_simple_name() {
1894 assert!(is_valid_jsonp_callback("handleResponse"));
1895 assert!(is_valid_jsonp_callback("cb"));
1896 assert!(is_valid_jsonp_callback("a"));
1897 }
1898
1899 #[test]
1900 fn test_is_valid_jsonp_callback_with_underscore() {
1901 assert!(is_valid_jsonp_callback("handle_response"));
1902 assert!(is_valid_jsonp_callback("_cb"));
1903 }
1904
1905 #[test]
1906 fn test_is_valid_jsonp_callback_with_dot() {
1907 assert!(is_valid_jsonp_callback("module.callback"));
1908 assert!(is_valid_jsonp_callback("app.module.handle"));
1909 }
1910
1911 #[test]
1912 fn test_is_valid_jsonp_callback_with_digits() {
1913 assert!(is_valid_jsonp_callback("cb1"));
1914 assert!(is_valid_jsonp_callback("handle123"));
1915 }
1916
1917 #[test]
1918 fn test_is_valid_jsonp_callback_empty_is_invalid() {
1919 assert!(!is_valid_jsonp_callback(""));
1920 }
1921
1922 #[test]
1923 fn test_is_valid_jsonp_callback_starting_with_digit_is_invalid() {
1924 assert!(!is_valid_jsonp_callback("1callback"));
1926 assert!(!is_valid_jsonp_callback("9cb"));
1927 }
1928
1929 #[test]
1930 fn test_is_valid_jsonp_callback_with_special_chars_is_invalid() {
1931 assert!(!is_valid_jsonp_callback("alert(1)"));
1933 assert!(!is_valid_jsonp_callback("<script>"));
1934 assert!(!is_valid_jsonp_callback("cb;evil()"));
1935 assert!(!is_valid_jsonp_callback("cb'"));
1936 assert!(!is_valid_jsonp_callback("cb\""));
1937 assert!(!is_valid_jsonp_callback("cb-"));
1938 assert!(!is_valid_jsonp_callback("cb+"));
1939 assert!(!is_valid_jsonp_callback("cb space"));
1940 }
1941
1942 #[test]
1943 fn test_is_valid_jsonp_callback_too_long_is_invalid() {
1944 let long_name = "a".repeat(129);
1946 assert!(!is_valid_jsonp_callback(&long_name));
1947 let max_name = "a".repeat(128);
1949 assert!(is_valid_jsonp_callback(&max_name));
1950 }
1951
1952 #[tokio::test]
1955 async fn test_respond_jsonp_basic_format() {
1956 let data = serde_json::json!({"id": 1, "name": "alice"});
1957 let resp = respond_jsonp("handleResponse", &data);
1958
1959 assert_eq!(resp.status(), StatusCode::OK);
1960 assert_eq!(
1961 resp.headers().get("content-type").unwrap(),
1962 "application/javascript; charset=utf-8"
1963 );
1964
1965 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1966 let body = String::from_utf8(bytes.to_vec()).unwrap();
1967 assert!(body.starts_with("handleResponse("));
1969 assert!(body.ends_with(");"));
1970 let json_str = &body["handleResponse(".len()..body.len() - ");".len()];
1972 let json: Value = serde_json::from_str(json_str).unwrap();
1973 assert_eq!(json["id"], 1);
1974 assert_eq!(json["name"], "alice");
1975 }
1976
1977 #[tokio::test]
1978 async fn test_respond_jsonp_with_array_data() {
1979 let data = serde_json::json!([1, 2, 3]);
1980 let resp = respond_jsonp("cb", &data);
1981
1982 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1983 let body = String::from_utf8(bytes.to_vec()).unwrap();
1984 assert_eq!(body, "cb([1,2,3]);");
1985 }
1986
1987 #[tokio::test]
1988 async fn test_respond_jsonp_with_string_data() {
1989 let data = Value::String("hello".to_string());
1990 let resp = respond_jsonp("cb", &data);
1991
1992 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1993 let body = String::from_utf8(bytes.to_vec()).unwrap();
1994 assert_eq!(body, r#"cb("hello");"#);
1996 }
1997
1998 #[tokio::test]
1999 async fn test_respond_jsonp_with_null_data() {
2000 let resp = respond_jsonp("cb", &Value::Null);
2001
2002 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
2003 let body = String::from_utf8(bytes.to_vec()).unwrap();
2004 assert_eq!(body, "cb(null);");
2005 }
2006
2007 #[tokio::test]
2008 async fn test_respond_jsonp_with_empty_object() {
2009 let data = serde_json::json!({});
2010 let resp = respond_jsonp("cb", &data);
2011
2012 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
2013 let body = String::from_utf8(bytes.to_vec()).unwrap();
2014 assert_eq!(body, "cb({});");
2015 }
2016
2017 #[tokio::test]
2018 async fn test_respond_jsonp_invalid_callback_returns_400() {
2019 let data = serde_json::json!({"id": 1});
2020 let resp = respond_jsonp("alert(1)", &data);
2021
2022 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
2023 assert_eq!(
2024 resp.headers().get("content-type").unwrap(),
2025 "text/plain; charset=utf-8"
2026 );
2027
2028 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
2029 let body = String::from_utf8(bytes.to_vec()).unwrap();
2030 assert_eq!(body, "Invalid JSONP callback name");
2031 }
2032
2033 #[tokio::test]
2034 async fn test_respond_jsonp_empty_callback_returns_400() {
2035 let data = serde_json::json!({"id": 1});
2036 let resp = respond_jsonp("", &data);
2037
2038 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
2039 }
2040
2041 #[tokio::test]
2042 async fn test_respond_jsonp_xss_injection_blocked() {
2043 let data = serde_json::json!({"id": 1});
2045 let malicious_names = vec![
2046 "<script>alert(1)</script>",
2047 "cb;</script><script>alert(1)",
2048 "cb'+alert(1)+'",
2049 "cb\";alert(1);\"",
2050 ];
2051
2052 for name in malicious_names {
2053 let resp = respond_jsonp(name, &data);
2054 assert_eq!(
2055 resp.status(),
2056 StatusCode::BAD_REQUEST,
2057 "恶意回调名必须被拒绝: {name}"
2058 );
2059 }
2060 }
2061
2062 #[tokio::test]
2065 async fn test_jsonp_response_wrapper_basic() {
2066 async fn handler() -> JsonpResponse {
2067 JsonpResponse("handleResponse".to_string(), serde_json::json!({"id": 1}))
2068 }
2069
2070 let router = axum::Router::new().route("/", axum::routing::get(handler));
2071 let req = Request::builder()
2072 .method(Method::GET)
2073 .uri("/")
2074 .body(Body::empty())
2075 .unwrap();
2076 let resp = router.oneshot(req).await.unwrap();
2077
2078 assert_eq!(resp.status(), StatusCode::OK);
2079 assert_eq!(
2080 resp.headers().get("content-type").unwrap(),
2081 "application/javascript; charset=utf-8"
2082 );
2083
2084 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
2085 let body = String::from_utf8(bytes.to_vec()).unwrap();
2086 assert_eq!(body, r#"handleResponse({"id":1});"#);
2087 }
2088
2089 #[tokio::test]
2090 async fn test_jsonp_response_wrapper_invalid_callback() {
2091 async fn handler() -> JsonpResponse {
2092 JsonpResponse("1invalid".to_string(), serde_json::json!({}))
2094 }
2095
2096 let router = axum::Router::new().route("/", axum::routing::get(handler));
2097 let req = Request::builder()
2098 .method(Method::GET)
2099 .uri("/")
2100 .body(Body::empty())
2101 .unwrap();
2102 let resp = router.oneshot(req).await.unwrap();
2103
2104 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
2105 }
2106
2107 #[test]
2108 fn test_jsonp_response_clone_debug() {
2109 let resp = JsonpResponse("cb".to_string(), serde_json::json!({"id": 1}));
2110 let cloned = resp.clone();
2111 assert_eq!(cloned.0, "cb");
2112 assert_eq!(cloned.1["id"], 1);
2113
2114 let debug = format!("{resp:?}");
2115 assert!(debug.contains("JsonpResponse"));
2116 }
2117}