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
485const JSONP_CALLBACK_PATTERN: &str = r"^[a-zA-Z_][a-zA-Z0-9_.]*$";
500
501pub fn is_valid_jsonp_callback(callback: &str) -> bool {
514 if callback.is_empty() || callback.len() > 128 {
515 return false;
516 }
517 regex::Regex::new(JSONP_CALLBACK_PATTERN)
518 .map(|re| re.is_match(callback))
519 .unwrap_or(false)
520}
521
522#[tracing::instrument(skip(data))]
540pub fn respond_jsonp(callback: &str, data: &Value) -> Response {
541 if !is_valid_jsonp_callback(callback) {
542 return (
543 StatusCode::BAD_REQUEST,
544 [(header::CONTENT_TYPE, "text/plain; charset=utf-8")],
545 "Invalid JSONP callback name".to_string(),
546 )
547 .into_response();
548 }
549
550 let json_str = data.to_string();
551 let body = format!("{callback}({json_str});");
552
553 (
554 StatusCode::OK,
555 [(
556 header::CONTENT_TYPE,
557 "application/javascript; charset=utf-8",
558 )],
559 body,
560 )
561 .into_response()
562}
563
564#[derive(Debug, Clone)]
580pub struct JsonpResponse(pub String, pub Value);
581
582impl IntoResponse for JsonpResponse {
583 fn into_response(self) -> Response {
584 respond_jsonp(&self.0, &self.1)
585 }
586}
587
588#[cfg(test)]
589mod tests {
590 use super::*;
591 use axum::body::Body;
592 use axum::http::{Method, Request};
593 use http_body_util::BodyExt;
594 use tower::ServiceExt;
595
596 #[test]
601 fn test_api_response_new() {
602 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
603 assert_eq!(resp.code, 1);
604 assert_eq!(resp.msg, "ok");
605 assert!(resp.data.is_object());
606 }
607
608 #[test]
609 fn test_api_response_success() {
610 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
611 assert_eq!(resp.code, 1);
612 assert_eq!(resp.msg, "ok");
613 assert_eq!(resp.data["id"], 1);
614 }
615
616 #[test]
617 fn test_api_response_success_empty() {
618 let resp = ApiResponse::success_empty();
619 assert_eq!(resp.code, 1);
620 assert_eq!(resp.msg, "");
621 assert!(resp.data.is_object());
622 assert!(resp.data.as_object().unwrap().is_empty());
623 }
624
625 #[test]
626 fn test_api_response_error() {
627 let resp = ApiResponse::error("参数错误");
628 assert_eq!(resp.code, 0);
629 assert_eq!(resp.msg, "参数错误");
630 assert!(resp.data.is_object());
631 }
632
633 #[test]
634 fn test_api_response_error_with_data() {
635 let resp = ApiResponse::error_with_data("失败", serde_json::json!({"field": "name"}));
636 assert_eq!(resp.code, 0);
637 assert_eq!(resp.msg, "失败");
638 assert_eq!(resp.data["field"], "name");
639 }
640
641 #[test]
642 fn test_api_response_error_with_code() {
643 let resp = ApiResponse::error_with_code(-1, "未登录", Value::Object(Map::new()));
644 assert_eq!(resp.code, -1);
645 assert_eq!(resp.msg, "未登录");
646 }
647
648 #[test]
649 fn test_api_response_to_value_field_order() {
650 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
651 let value = resp.to_value();
652 let obj = value.as_object().unwrap();
653
654 let keys: Vec<&String> = obj.keys().collect();
656 assert_eq!(keys, vec!["code", "msg", "data"]);
657 }
658
659 #[test]
660 fn test_api_response_to_value_content() {
661 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1, "name": "alice"}));
662 let value = resp.to_value();
663 assert_eq!(value["code"], 1);
664 assert_eq!(value["msg"], "ok");
665 assert_eq!(value["data"]["id"], 1);
666 assert_eq!(value["data"]["name"], "alice");
667 }
668
669 #[test]
670 fn test_api_response_to_json_string() {
671 let resp = ApiResponse::new(1, "ok", serde_json::json!({}));
672 let json_str = resp.to_json_string();
673 let expected = r#"{"code":1,"msg":"ok","data":{}}"#;
675 assert_eq!(json_str, expected);
676 }
677
678 #[test]
679 fn test_api_response_to_json_string_with_data() {
680 let resp = ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok");
681 let json_str = resp.to_json_string();
682 let expected = r#"{"code":1,"msg":"ok","data":{"id":1,"name":"alice"}}"#;
683 assert_eq!(json_str, expected);
684 }
685
686 #[test]
687 fn test_api_response_serialize_via_serde() {
688 let resp = ApiResponse::new(0, "失败", Value::Object(Map::new()));
689 let json_str = serde_json::to_string(&resp).unwrap();
690 assert_eq!(json_str, r#"{"code":0,"msg":"失败","data":{}}"#);
691 }
692
693 #[test]
694 fn test_api_response_clone() {
695 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
696 let cloned = resp.clone();
697 assert_eq!(cloned.code, resp.code);
698 assert_eq!(cloned.msg, resp.msg);
699 assert_eq!(cloned.data, resp.data);
700 }
701
702 #[test]
703 fn test_api_response_debug_format() {
704 let resp = ApiResponse::new(1, "ok", Value::Object(Map::new()));
705 let debug_str = format!("{resp:?}");
706 assert!(debug_str.contains("ApiResponse"));
707 assert!(debug_str.contains("code: 1"));
708 assert!(debug_str.contains("\"ok\""));
709 }
710
711 #[test]
716 fn test_render_json_returns_response() {
717 let resp = render_json(1, "ok", serde_json::json!({}));
718 assert_eq!(resp.status(), StatusCode::OK);
719 assert_eq!(
720 resp.headers().get("content-type").unwrap(),
721 "application/json; charset=utf-8"
722 );
723 }
724
725 #[test]
726 fn test_render_success_returns_response() {
727 let resp = render_success(serde_json::json!({"id": 1}), "ok");
728 assert_eq!(resp.status(), StatusCode::OK);
729 }
730
731 #[test]
732 fn test_render_error_returns_response() {
733 let resp = render_error("参数错误");
734 assert_eq!(resp.status(), StatusCode::OK); }
736
737 #[test]
738 fn test_render_error_with_code_returns_response() {
739 let resp = render_error_with_code(-1, "未登录", serde_json::json!({}));
740 assert_eq!(resp.status(), StatusCode::OK);
741 }
742
743 #[tokio::test]
748 async fn test_api_response_as_handler_return() {
749 async fn handler() -> ApiResponse {
750 ApiResponse::success(serde_json::json!({"id": 1, "name": "alice"}), "ok")
751 }
752
753 let router = axum::Router::new().route("/", axum::routing::get(handler));
754 let req = Request::builder()
755 .method(Method::GET)
756 .uri("/")
757 .body(Body::empty())
758 .unwrap();
759 let resp = router.oneshot(req).await.unwrap();
760
761 assert_eq!(resp.status(), StatusCode::OK);
762 assert_eq!(
763 resp.headers().get("content-type").unwrap(),
764 "application/json; charset=utf-8"
765 );
766
767 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
768 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
769 let json: Value = serde_json::from_str(&body_str).unwrap();
770
771 assert_eq!(json["code"], 1);
772 assert_eq!(json["msg"], "ok");
773 assert_eq!(json["data"]["id"], 1);
774 assert_eq!(json["data"]["name"], "alice");
775 }
776
777 #[tokio::test]
778 async fn test_render_error_handler_return() {
779 async fn handler() -> Response {
780 render_error("参数错误")
781 }
782
783 let router = axum::Router::new().route("/", axum::routing::post(handler));
784 let req = Request::builder()
785 .method(Method::POST)
786 .uri("/")
787 .body(Body::empty())
788 .unwrap();
789 let resp = router.oneshot(req).await.unwrap();
790
791 assert_eq!(resp.status(), StatusCode::OK);
792
793 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
794 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
795 let json: Value = serde_json::from_str(&body_str).unwrap();
796
797 assert_eq!(json["code"], 0);
798 assert_eq!(json["msg"], "参数错误");
799 assert!(json["data"].is_object());
800 }
801
802 #[tokio::test]
803 async fn test_response_body_exact_format() {
804 async fn handler() -> ApiResponse {
806 ApiResponse::success_empty()
807 }
808
809 let router = axum::Router::new().route("/", axum::routing::get(handler));
810 let req = Request::builder()
811 .method(Method::GET)
812 .uri("/")
813 .body(Body::empty())
814 .unwrap();
815 let resp = router.oneshot(req).await.unwrap();
816
817 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
818 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
819 assert_eq!(body_str, r#"{"code":1,"msg":"","data":{}}"#);
820 }
821
822 #[tokio::test]
823 async fn test_response_with_complex_data() {
824 async fn handler() -> ApiResponse {
825 ApiResponse::success(
826 serde_json::json!({
827 "list": [{"id": 1}, {"id": 2}],
828 "total": 2,
829 "page": 1,
830 "size": 10
831 }),
832 "查询成功",
833 )
834 }
835
836 let router = axum::Router::new().route("/", axum::routing::get(handler));
837 let req = Request::builder()
838 .method(Method::GET)
839 .uri("/")
840 .body(Body::empty())
841 .unwrap();
842 let resp = router.oneshot(req).await.unwrap();
843
844 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
845 let body_str = String::from_utf8(bytes.to_vec()).unwrap();
846 let json: Value = serde_json::from_str(&body_str).unwrap();
847
848 assert_eq!(json["code"], 1);
849 assert_eq!(json["msg"], "查询成功");
850 assert_eq!(json["data"]["total"], 2);
851 assert_eq!(json["data"]["list"][0]["id"], 1);
852 assert_eq!(json["data"]["list"][1]["id"], 2);
853 }
854
855 #[tokio::test]
856 async fn test_response_with_various_error_codes() {
857 let test_cases = vec![
859 (0, "业务失败"),
860 (-1, "未登录"),
861 (-2, "用户不存在"),
862 (-3, "用户被禁用"),
863 (403, "禁止访问"),
864 (404, "资源不存在"),
865 (422, "参数校验失败"),
866 (500, "数据库错误"),
867 ];
868
869 for (code, msg) in test_cases {
870 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
871 let json_str = resp.to_json_string();
872 let json: Value = serde_json::from_str(&json_str).unwrap();
873 assert_eq!(json["code"], code);
874 assert_eq!(json["msg"], msg);
875 }
876 }
877
878 #[test]
903 fn test_php_consistency_render_json_compact_field_order() {
904 let resp = ApiResponse::new(1, "ok", serde_json::json!({"id": 1}));
908 let value = resp.to_value();
909 let obj = value.as_object().unwrap();
910 let keys: Vec<&String> = obj.keys().collect();
911 assert_eq!(
912 keys,
913 vec!["code", "msg", "data"],
914 "字段顺序必须为 code → msg → data(对齐 PHP compact())"
915 );
916 assert_eq!(value["code"], 1);
917 assert_eq!(value["msg"], "ok");
918 assert_eq!(value["data"]["id"], 1);
919 }
920
921 #[test]
922 fn test_php_consistency_render_json_default_values() {
923 let resp = ApiResponse::new(1, "", Value::Object(Map::new()));
926 let json_str = resp.to_json_string();
927 assert_eq!(
928 json_str, r#"{"code":1,"msg":"","data":{}}"#,
929 "默认值必须与 PHP renderJson() 一致:code=1, msg='', data={{}}"
930 );
931 }
932
933 #[test]
934 fn test_php_consistency_render_success_calls_render_json_with_code_1() {
935 let resp = ApiResponse::success(serde_json::json!({"id": 1}), "ok");
938 assert_eq!(
939 resp.code, 1,
940 "renderSuccess 必须 code=1(对齐 PHP renderJson(1, ...))"
941 );
942 assert_eq!(resp.msg, "ok");
943 assert_eq!(resp.data["id"], 1);
944
945 let json_str = resp.to_json_string();
947 let expected = r#"{"code":1,"msg":"ok","data":{"id":1}}"#;
948 assert_eq!(json_str, expected);
949 }
950
951 #[test]
952 fn test_php_consistency_render_error_default_code_is_0() {
953 let resp = ApiResponse::error("参数错误");
956 assert_eq!(
957 resp.code, 0,
958 "renderError 默认 code=0(对齐 PHP 默认参数 $code = 0)"
959 );
960 assert_eq!(resp.msg, "参数错误");
961 assert!(
962 resp.data.is_object(),
963 "renderError 默认 data 为空对象(对齐 PHP $data = [])"
964 );
965
966 let response = render_error("参数错误");
968 assert_eq!(response.status(), StatusCode::OK);
969 }
970
971 #[test]
972 fn test_php_consistency_render_error_with_custom_code_aligns_base_exception() {
973 let test_cases = vec![
980 (-1i32, "未登录"),
981 (-2, "用户不存在"),
982 (-3, "用户被禁用"),
983 (0, "业务失败"),
984 ];
985
986 for (code, msg) in test_cases {
987 let resp = ApiResponse::error_with_code(code, msg, Value::Object(Map::new()));
988 let json_str = resp.to_json_string();
989 let json: Value = serde_json::from_str(&json_str).unwrap();
990 assert_eq!(
991 json["code"], code,
992 "自定义错误码必须与 PHP BaseException 约定一致"
993 );
994 assert_eq!(json["msg"], msg);
995 assert!(json.get("data").is_some(), "data 字段必须存在");
997 }
998 }
999
1000 #[test]
1017 fn test_default_response_type_default_is_json() {
1018 let t = DefaultResponseType::default();
1020 assert_eq!(t, DefaultResponseType::Json);
1021 }
1022
1023 #[test]
1024 fn test_default_response_type_variants_eq() {
1025 assert_eq!(DefaultResponseType::Json, DefaultResponseType::Json);
1026 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Html);
1027 assert_ne!(DefaultResponseType::Json, DefaultResponseType::Auto);
1028 assert_ne!(DefaultResponseType::Html, DefaultResponseType::Auto);
1029 }
1030
1031 #[test]
1032 fn test_default_response_type_clone_copy() {
1033 let t = DefaultResponseType::Json;
1034 let t2 = t; assert_eq!(t, t2);
1036 let t3 = t;
1038 assert_eq!(t, t3);
1039 }
1040
1041 #[test]
1042 fn test_default_response_type_debug() {
1043 let debug = format!("{:?}", DefaultResponseType::Json);
1044 assert!(debug.contains("Json"));
1045 let debug = format!("{:?}", DefaultResponseType::Html);
1046 assert!(debug.contains("Html"));
1047 let debug = format!("{:?}", DefaultResponseType::Auto);
1048 assert!(debug.contains("Auto"));
1049 }
1050
1051 #[test]
1052 fn test_default_response_type_respond_json() {
1053 let headers = HeaderMap::new();
1055 let data = serde_json::json!({"id": 1});
1056 let resp = DefaultResponseType::Json.respond(&data, &headers);
1057 assert_eq!(resp.status(), StatusCode::OK);
1058 assert_eq!(
1059 resp.headers().get("content-type").unwrap(),
1060 "application/json; charset=utf-8"
1061 );
1062 }
1063
1064 #[test]
1065 fn test_default_response_type_respond_html() {
1066 let headers = HeaderMap::new();
1068 let data = serde_json::json!({"id": 1});
1069 let resp = DefaultResponseType::Html.respond(&data, &headers);
1070 assert_eq!(resp.status(), StatusCode::OK);
1071 assert_eq!(
1072 resp.headers().get("content-type").unwrap(),
1073 "text/html; charset=utf-8"
1074 );
1075 }
1076
1077 #[tokio::test]
1078 async fn test_default_response_type_respond_html_body() {
1079 let headers = HeaderMap::new();
1080 let data = serde_json::json!({"id": 1});
1081 let resp = DefaultResponseType::Html.respond(&data, &headers);
1082 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1083 let body = String::from_utf8(bytes.to_vec()).unwrap();
1084 assert_eq!(body, r#"{"id":1}"#);
1086 }
1087
1088 #[tokio::test]
1089 async fn test_default_response_type_respond_auto_with_json_accept() {
1090 let mut headers = HeaderMap::new();
1092 headers.insert("accept", "application/json".parse().unwrap());
1093 let data = serde_json::json!({"id": 1});
1094 let resp = DefaultResponseType::Auto.respond(&data, &headers);
1095 assert_eq!(
1096 resp.headers().get("content-type").unwrap(),
1097 "application/json; charset=utf-8"
1098 );
1099 }
1100
1101 #[tokio::test]
1102 async fn test_default_response_type_respond_auto_with_html_accept() {
1103 let mut headers = HeaderMap::new();
1105 headers.insert("accept", "text/html".parse().unwrap());
1106 let data = serde_json::json!({"id": 1});
1107 let resp = DefaultResponseType::Auto.respond(&data, &headers);
1108 assert_eq!(
1109 resp.headers().get("content-type").unwrap(),
1110 "text/html; charset=utf-8"
1111 );
1112 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1113 let body = String::from_utf8(bytes.to_vec()).unwrap();
1114 assert_eq!(body, "Array");
1116 }
1117
1118 #[test]
1121 fn test_is_json_request_with_application_json() {
1122 let mut headers = HeaderMap::new();
1123 headers.insert("accept", "application/json".parse().unwrap());
1124 assert!(is_json_request(&headers));
1125 }
1126
1127 #[test]
1128 fn test_is_json_request_with_text_json() {
1129 let mut headers = HeaderMap::new();
1130 headers.insert("accept", "text/json".parse().unwrap());
1131 assert!(is_json_request(&headers));
1132 }
1133
1134 #[test]
1135 fn test_is_json_request_with_vnd_api_json() {
1136 let mut headers = HeaderMap::new();
1137 headers.insert("accept", "application/vnd.api+json".parse().unwrap());
1138 assert!(is_json_request(&headers));
1139 }
1140
1141 #[test]
1142 fn test_is_json_request_with_wildcard() {
1143 let mut headers = HeaderMap::new();
1145 headers.insert("accept", "*/*".parse().unwrap());
1146 assert!(!is_json_request(&headers));
1147 }
1148
1149 #[test]
1150 fn test_is_json_request_with_text_html() {
1151 let mut headers = HeaderMap::new();
1152 headers.insert("accept", "text/html".parse().unwrap());
1153 assert!(!is_json_request(&headers));
1154 }
1155
1156 #[test]
1157 fn test_is_json_request_no_accept_header() {
1158 let headers = HeaderMap::new();
1159 assert!(!is_json_request(&headers));
1160 }
1161
1162 #[test]
1163 fn test_is_json_request_case_insensitive() {
1164 let mut headers = HeaderMap::new();
1166 headers.insert("accept", "APPLICATION/JSON".parse().unwrap());
1167 assert!(is_json_request(&headers));
1168 }
1169
1170 #[test]
1171 fn test_is_json_request_mixed_accept() {
1172 let mut headers = HeaderMap::new();
1174 headers.insert(
1175 "accept",
1176 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1177 .parse()
1178 .unwrap(),
1179 );
1180 assert!(is_json_request(&headers));
1181 }
1182
1183 #[test]
1186 fn test_respond_returns_json_content_type() {
1187 let data = serde_json::json!({"id": 1});
1188 let resp = respond(&data);
1189 assert_eq!(resp.status(), StatusCode::OK);
1190 assert_eq!(
1191 resp.headers().get("content-type").unwrap(),
1192 "application/json; charset=utf-8"
1193 );
1194 }
1195
1196 #[tokio::test]
1197 async fn test_respond_object_body() {
1198 let data = serde_json::json!({"id": 1, "name": "alice"});
1199 let resp = respond(&data);
1200 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1201 let body = String::from_utf8(bytes.to_vec()).unwrap();
1202 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1203 }
1204
1205 #[tokio::test]
1206 async fn test_respond_array_body() {
1207 let data = serde_json::json!([1, 2, 3]);
1208 let resp = respond(&data);
1209 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1210 let body = String::from_utf8(bytes.to_vec()).unwrap();
1211 assert_eq!(body, r#"[1,2,3]"#);
1212 }
1213
1214 #[tokio::test]
1215 async fn test_respond_string_value() {
1216 let data = Value::String("hello".to_string());
1217 let resp = respond(&data);
1218 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1219 let body = String::from_utf8(bytes.to_vec()).unwrap();
1220 assert_eq!(body, r#""hello""#);
1222 }
1223
1224 #[tokio::test]
1225 async fn test_respond_null_value() {
1226 let resp = respond(&Value::Null);
1227 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1228 let body = String::from_utf8(bytes.to_vec()).unwrap();
1229 assert_eq!(body, "null");
1230 }
1231
1232 #[tokio::test]
1233 async fn test_respond_number_value() {
1234 let resp = respond(&serde_json::json!(42));
1235 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1236 let body = String::from_utf8(bytes.to_vec()).unwrap();
1237 assert_eq!(body, "42");
1238 }
1239
1240 #[tokio::test]
1241 async fn test_respond_bool_value() {
1242 let resp = respond(&serde_json::json!(true));
1243 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1244 let body = String::from_utf8(bytes.to_vec()).unwrap();
1245 assert_eq!(body, "true");
1246 }
1247
1248 #[test]
1251 fn test_respond_html_content_type() {
1252 let resp = respond_html("<h1>Hello</h1>");
1253 assert_eq!(resp.status(), StatusCode::OK);
1254 assert_eq!(
1255 resp.headers().get("content-type").unwrap(),
1256 "text/html; charset=utf-8"
1257 );
1258 }
1259
1260 #[tokio::test]
1261 async fn test_respond_html_body() {
1262 let resp = respond_html("<p>test</p>");
1263 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1264 let body = String::from_utf8(bytes.to_vec()).unwrap();
1265 assert_eq!(body, "<p>test</p>");
1266 }
1267
1268 #[tokio::test]
1269 async fn test_respond_html_empty() {
1270 let resp = respond_html("");
1271 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1272 let body = String::from_utf8(bytes.to_vec()).unwrap();
1273 assert_eq!(body, "");
1274 }
1275
1276 #[tokio::test]
1277 async fn test_respond_html_with_unicode() {
1278 let resp = respond_html("<p>你好世界</p>");
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, "<p>你好世界</p>");
1282 }
1283
1284 #[test]
1287 fn test_respond_text_content_type() {
1288 let resp = respond_text("plain text");
1289 assert_eq!(resp.status(), StatusCode::OK);
1290 assert_eq!(
1291 resp.headers().get("content-type").unwrap(),
1292 "text/plain; charset=utf-8"
1293 );
1294 }
1295
1296 #[tokio::test]
1297 async fn test_respond_text_body() {
1298 let resp = respond_text("OK");
1299 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1300 let body = String::from_utf8(bytes.to_vec()).unwrap();
1301 assert_eq!(body, "OK");
1302 }
1303
1304 #[tokio::test]
1307 async fn test_auto_respond_json_request_with_object() {
1308 let mut headers = HeaderMap::new();
1310 headers.insert("accept", "application/json".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 "application/json; charset=utf-8"
1316 );
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, r#"{"id":1}"#);
1320 }
1321
1322 #[tokio::test]
1323 async fn test_auto_respond_json_request_with_array() {
1324 let mut headers = HeaderMap::new();
1325 headers.insert("accept", "application/json".parse().unwrap());
1326 let data = serde_json::json!([1, 2, 3]);
1327 let resp = auto_respond(&data, &headers);
1328 assert_eq!(
1329 resp.headers().get("content-type").unwrap(),
1330 "application/json; charset=utf-8"
1331 );
1332 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1333 let body = String::from_utf8(bytes.to_vec()).unwrap();
1334 assert_eq!(body, r#"[1,2,3]"#);
1335 }
1336
1337 #[tokio::test]
1338 async fn test_auto_respond_html_request_with_object_returns_array_literal() {
1339 let mut headers = HeaderMap::new();
1341 headers.insert("accept", "text/html".parse().unwrap());
1342 let data = serde_json::json!({"id": 1});
1343 let resp = auto_respond(&data, &headers);
1344 assert_eq!(
1345 resp.headers().get("content-type").unwrap(),
1346 "text/html; charset=utf-8"
1347 );
1348 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1349 let body = String::from_utf8(bytes.to_vec()).unwrap();
1350 assert_eq!(body, "Array");
1351 }
1352
1353 #[tokio::test]
1354 async fn test_auto_respond_html_request_with_array_returns_array_literal() {
1355 let mut headers = HeaderMap::new();
1357 headers.insert("accept", "text/html".parse().unwrap());
1358 let data = serde_json::json!([1, 2, 3]);
1359 let resp = auto_respond(&data, &headers);
1360 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1361 let body = String::from_utf8(bytes.to_vec()).unwrap();
1362 assert_eq!(body, "Array");
1363 }
1364
1365 #[tokio::test]
1366 async fn test_auto_respond_html_request_with_string_returns_string() {
1367 let mut headers = HeaderMap::new();
1369 headers.insert("accept", "text/html".parse().unwrap());
1370 let data = Value::String("hello".to_string());
1371 let resp = auto_respond(&data, &headers);
1372 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1373 let body = String::from_utf8(bytes.to_vec()).unwrap();
1374 assert_eq!(body, "hello");
1375 }
1376
1377 #[tokio::test]
1378 async fn test_auto_respond_html_request_with_null_returns_empty() {
1379 let mut headers = HeaderMap::new();
1380 headers.insert("accept", "text/html".parse().unwrap());
1381 let resp = auto_respond(&Value::Null, &headers);
1382 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1383 let body = String::from_utf8(bytes.to_vec()).unwrap();
1384 assert_eq!(body, "");
1385 }
1386
1387 #[tokio::test]
1388 async fn test_auto_respond_html_request_with_number_returns_number_string() {
1389 let mut headers = HeaderMap::new();
1390 headers.insert("accept", "text/html".parse().unwrap());
1391 let resp = auto_respond(&serde_json::json!(42), &headers);
1392 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1393 let body = String::from_utf8(bytes.to_vec()).unwrap();
1394 assert_eq!(body, "42");
1395 }
1396
1397 #[tokio::test]
1398 async fn test_auto_respond_no_accept_header_returns_html() {
1399 let headers = HeaderMap::new();
1401 let data = serde_json::json!({"id": 1});
1402 let resp = auto_respond(&data, &headers);
1403 assert_eq!(
1404 resp.headers().get("content-type").unwrap(),
1405 "text/html; charset=utf-8"
1406 );
1407 }
1408
1409 #[tokio::test]
1410 async fn test_auto_respond_wildcard_accept_returns_html() {
1411 let mut headers = HeaderMap::new();
1413 headers.insert("accept", "*/*".parse().unwrap());
1414 let data = serde_json::json!({"id": 1});
1415 let resp = auto_respond(&data, &headers);
1416 assert_eq!(
1417 resp.headers().get("content-type").unwrap(),
1418 "text/html; charset=utf-8"
1419 );
1420 }
1421
1422 #[tokio::test]
1425 async fn test_json_response_into_response_object() {
1426 async fn handler() -> JsonResponse {
1427 JsonResponse(serde_json::json!({"id": 1, "name": "alice"}))
1428 }
1429
1430 let router = axum::Router::new().route("/", axum::routing::get(handler));
1431 let req = Request::builder()
1432 .method(Method::GET)
1433 .uri("/")
1434 .body(Body::empty())
1435 .unwrap();
1436 let resp = router.oneshot(req).await.unwrap();
1437
1438 assert_eq!(resp.status(), StatusCode::OK);
1439 assert_eq!(
1440 resp.headers().get("content-type").unwrap(),
1441 "application/json; charset=utf-8"
1442 );
1443 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1444 let body = String::from_utf8(bytes.to_vec()).unwrap();
1445 assert_eq!(body, r#"{"id":1,"name":"alice"}"#);
1446 }
1447
1448 #[tokio::test]
1449 async fn test_json_response_into_response_array() {
1450 async fn handler() -> JsonResponse {
1451 JsonResponse(serde_json::json!([1, 2, 3]))
1452 }
1453
1454 let router = axum::Router::new().route("/", axum::routing::get(handler));
1455 let req = Request::builder()
1456 .method(Method::GET)
1457 .uri("/")
1458 .body(Body::empty())
1459 .unwrap();
1460 let resp = router.oneshot(req).await.unwrap();
1461
1462 assert_eq!(
1463 resp.headers().get("content-type").unwrap(),
1464 "application/json; charset=utf-8"
1465 );
1466 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1467 let body = String::from_utf8(bytes.to_vec()).unwrap();
1468 assert_eq!(body, r#"[1,2,3]"#);
1469 }
1470
1471 #[tokio::test]
1472 async fn test_json_response_into_response_string() {
1473 async fn handler() -> JsonResponse {
1474 JsonResponse(Value::String("hello".to_string()))
1475 }
1476
1477 let router = axum::Router::new().route("/", axum::routing::get(handler));
1478 let req = Request::builder()
1479 .method(Method::GET)
1480 .uri("/")
1481 .body(Body::empty())
1482 .unwrap();
1483 let resp = router.oneshot(req).await.unwrap();
1484
1485 assert_eq!(
1487 resp.headers().get("content-type").unwrap(),
1488 "application/json; charset=utf-8"
1489 );
1490 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1491 let body = String::from_utf8(bytes.to_vec()).unwrap();
1492 assert_eq!(body, r#""hello""#);
1493 }
1494
1495 #[tokio::test]
1496 async fn test_json_response_into_response_null() {
1497 async fn handler() -> JsonResponse {
1498 JsonResponse(Value::Null)
1499 }
1500
1501 let router = axum::Router::new().route("/", axum::routing::get(handler));
1502 let req = Request::builder()
1503 .method(Method::GET)
1504 .uri("/")
1505 .body(Body::empty())
1506 .unwrap();
1507 let resp = router.oneshot(req).await.unwrap();
1508
1509 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1510 let body = String::from_utf8(bytes.to_vec()).unwrap();
1511 assert_eq!(body, "null");
1512 }
1513
1514 #[tokio::test]
1515 async fn test_json_response_into_response_post_handler() {
1516 async fn handler() -> JsonResponse {
1518 JsonResponse(serde_json::json!({
1519 "code": 1,
1520 "msg": "success",
1521 "data": {"id": 12345, "status": "paid"}
1522 }))
1523 }
1524
1525 let router = axum::Router::new().route("/api/order", axum::routing::post(handler));
1526 let req = Request::builder()
1527 .method(Method::POST)
1528 .uri("/api/order")
1529 .body(Body::empty())
1530 .unwrap();
1531 let resp = router.oneshot(req).await.unwrap();
1532
1533 assert_eq!(resp.status(), StatusCode::OK);
1534 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1535 let body = String::from_utf8(bytes.to_vec()).unwrap();
1536 let json: Value = serde_json::from_str(&body).unwrap();
1537 assert_eq!(json["code"], 1);
1538 assert_eq!(json["msg"], "success");
1539 assert_eq!(json["data"]["id"], 12345);
1540 assert_eq!(json["data"]["status"], "paid");
1541 }
1542
1543 #[test]
1544 fn test_json_response_from_value() {
1545 let value = serde_json::json!({"id": 1});
1547 let json_resp: JsonResponse = value.clone().into();
1548 assert_eq!(json_resp.0, value);
1549 }
1550
1551 #[test]
1552 fn test_json_response_clone_debug() {
1553 let resp = JsonResponse(serde_json::json!({"id": 1}));
1554 let cloned = resp.clone();
1555 assert_eq!(resp.0, cloned.0);
1556
1557 let debug = format!("{resp:?}");
1558 assert!(debug.contains("JsonResponse"));
1559 }
1560
1561 #[test]
1584 fn test_r5_php_isjson_accept_application_json() {
1585 let mut headers = HeaderMap::new();
1587 headers.insert("accept", "application/json".parse().unwrap());
1588 assert!(
1589 is_json_request(&headers),
1590 "Accept: application/json 时 isJson() 必须返回 true(对齐 PHP)"
1591 );
1592 }
1593
1594 #[test]
1595 fn test_r5_php_isjson_accept_text_html() {
1596 let mut headers = HeaderMap::new();
1598 headers.insert("accept", "text/html".parse().unwrap());
1599 assert!(
1600 !is_json_request(&headers),
1601 "Accept: text/html 时 isJson() 必须返回 false(对齐 PHP)"
1602 );
1603 }
1604
1605 #[test]
1606 fn test_r5_php_isjson_accept_wildcard() {
1607 let mut headers = HeaderMap::new();
1609 headers.insert("accept", "*/*".parse().unwrap());
1610 assert!(
1611 !is_json_request(&headers),
1612 "Accept: */* 时 isJson() 必须返回 false(对齐 PHP type() 无匹配 MIME)"
1613 );
1614 }
1615
1616 #[test]
1617 fn test_r5_php_isjson_no_accept_header() {
1618 let headers = HeaderMap::new();
1620 assert!(
1621 !is_json_request(&headers),
1622 "无 Accept 头时 isJson() 必须返回 false(对齐 PHP)"
1623 );
1624 }
1625
1626 #[tokio::test]
1627 async fn test_r5_php_autoresponse_json_type_with_array() {
1628 let mut headers = HeaderMap::new();
1631 headers.insert("accept", "application/json".parse().unwrap());
1632 let data = serde_json::json!([1, 2, 3]);
1633 let resp = auto_respond(&data, &headers);
1634
1635 assert_eq!(
1637 resp.headers().get("content-type").unwrap(),
1638 "application/json; charset=utf-8",
1639 "PHP autoResponse + isJson=true 时必须返回 JSON 类型"
1640 );
1641
1642 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1644 let body = String::from_utf8(bytes.to_vec()).unwrap();
1645 assert_eq!(
1646 body, "[1,2,3]",
1647 "PHP autoResponse + isJson=true 时数组必须被 json_encode"
1648 );
1649 }
1650
1651 #[tokio::test]
1652 async fn test_r5_php_autoresponse_html_type_with_array_returns_array_literal() {
1653 let mut headers = HeaderMap::new();
1657 headers.insert("accept", "text/html".parse().unwrap());
1658 let data = serde_json::json!([1, 2, 3]);
1659 let resp = auto_respond(&data, &headers);
1660
1661 assert_eq!(
1663 resp.headers().get("content-type").unwrap(),
1664 "text/html; charset=utf-8",
1665 "PHP autoResponse + isJson=false 时必须返回 HTML 类型"
1666 );
1667
1668 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1670 let body = String::from_utf8(bytes.to_vec()).unwrap();
1671 assert_eq!(
1672 body, "Array",
1673 "PHP autoResponse + isJson=false 时数组必须输出字面量 'Array'(PHP bug 复刻)"
1674 );
1675 }
1676
1677 #[tokio::test]
1678 async fn test_r5_php_autoresponse_html_type_with_object_returns_array_literal() {
1679 let mut headers = HeaderMap::new();
1682 headers.insert("accept", "text/html".parse().unwrap());
1683 let data = serde_json::json!({"name": "alice", "age": 30});
1684 let resp = auto_respond(&data, &headers);
1685
1686 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1687 let body = String::from_utf8(bytes.to_vec()).unwrap();
1688 assert_eq!(
1689 body, "Array",
1690 "PHP autoResponse + isJson=false 时关联数组也输出字面量 'Array'(PHP bug 复刻)"
1691 );
1692 }
1693
1694 #[tokio::test]
1695 async fn test_r5_php_autoresponse_html_type_with_string_returns_string() {
1696 let mut headers = HeaderMap::new();
1699 headers.insert("accept", "text/html".parse().unwrap());
1700 let data = Value::String("Hello World".to_string());
1701 let resp = auto_respond(&data, &headers);
1702
1703 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1704 let body = String::from_utf8(bytes.to_vec()).unwrap();
1705 assert_eq!(
1706 body, "Hello World",
1707 "PHP autoResponse + isJson=false + 字符串时必须原样输出字符串内容"
1708 );
1709 }
1710
1711 #[tokio::test]
1712 async fn test_r5_php_autoresponse_no_accept_header_returns_html() {
1713 let headers = HeaderMap::new();
1715 let data = serde_json::json!({"id": 1});
1716 let resp = auto_respond(&data, &headers);
1717
1718 assert_eq!(
1719 resp.headers().get("content-type").unwrap(),
1720 "text/html; charset=utf-8",
1721 "无 Accept 头时 PHP isJson() 返回 false,必须返回 HTML 类型"
1722 );
1723 }
1724
1725 #[tokio::test]
1726 async fn test_r5_php_autoresponse_wildcard_accept_returns_html() {
1727 let mut headers = HeaderMap::new();
1729 headers.insert("accept", "*/*".parse().unwrap());
1730 let data = serde_json::json!({"id": 1});
1731 let resp = auto_respond(&data, &headers);
1732
1733 assert_eq!(
1734 resp.headers().get("content-type").unwrap(),
1735 "text/html; charset=utf-8",
1736 "Accept: */* 时 PHP isJson() 返回 false,必须返回 HTML 类型"
1737 );
1738 }
1739
1740 #[tokio::test]
1741 async fn test_r5_php_autoresponse_mixed_accept_with_json() {
1742 let mut headers = HeaderMap::new();
1745 headers.insert(
1746 "accept",
1747 "text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8"
1748 .parse()
1749 .unwrap(),
1750 );
1751 let data = serde_json::json!({"id": 1});
1752 let resp = auto_respond(&data, &headers);
1753
1754 assert_eq!(
1755 resp.headers().get("content-type").unwrap(),
1756 "application/json; charset=utf-8",
1757 "Accept 头含 json MIME 时 PHP isJson() 返回 true,必须返回 JSON 类型"
1758 );
1759 }
1760
1761 #[test]
1762 fn test_r5_php_isjson_case_insensitive_alignment() {
1763 let mut headers_upper = HeaderMap::new();
1766 headers_upper.insert("accept", "APPLICATION/JSON".parse().unwrap());
1767 assert!(
1768 is_json_request(&headers_upper),
1769 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1770 );
1771
1772 let mut headers_mixed = HeaderMap::new();
1773 headers_mixed.insert("accept", "Application/Json".parse().unwrap());
1774 assert!(
1775 is_json_request(&headers_mixed),
1776 "PHP isJson() 大小写不敏感(stristr),Rust 必须对齐"
1777 );
1778 }
1779
1780 #[tokio::test]
1781 async fn test_r5_php_default_response_type_json_is_project_main_strategy() {
1782 let headers = HeaderMap::new(); let data = serde_json::json!({"id": 1, "name": "alice"});
1787
1788 let resp = DefaultResponseType::Json.respond(&data, &headers);
1790 assert_eq!(
1791 resp.headers().get("content-type").unwrap(),
1792 "application/json; charset=utf-8",
1793 "项目主策略:默认返回 JSON,不受 Accept 头影响"
1794 );
1795
1796 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1797 let body = String::from_utf8(bytes.to_vec()).unwrap();
1798 assert_eq!(
1799 body, r#"{"id":1,"name":"alice"}"#,
1800 "项目主策略:默认返回 JSON 编码的内容"
1801 );
1802 }
1803
1804 #[tokio::test]
1805 async fn test_r5_php_json_response_default_json_strategy() {
1806 async fn handler() -> JsonResponse {
1810 JsonResponse(serde_json::json!({"code": 1, "msg": "ok", "data": {"id": 1}}))
1811 }
1812
1813 let router = axum::Router::new().route("/", axum::routing::get(handler));
1814
1815 let req = Request::builder()
1817 .method(Method::GET)
1818 .uri("/")
1819 .header("accept", "text/html")
1820 .body(Body::empty())
1821 .unwrap();
1822 let resp = router.oneshot(req).await.unwrap();
1823
1824 assert_eq!(
1825 resp.headers().get("content-type").unwrap(),
1826 "application/json; charset=utf-8",
1827 "项目主策略:JsonResponse IntoResponse 始终返回 JSON,不受 Accept 头影响"
1828 );
1829
1830 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1831 let body = String::from_utf8(bytes.to_vec()).unwrap();
1832 assert_eq!(body, r#"{"code":1,"msg":"ok","data":{"id":1}}"#);
1833 }
1834
1835 #[test]
1847 fn test_is_valid_jsonp_callback_simple_name() {
1848 assert!(is_valid_jsonp_callback("handleResponse"));
1849 assert!(is_valid_jsonp_callback("cb"));
1850 assert!(is_valid_jsonp_callback("a"));
1851 }
1852
1853 #[test]
1854 fn test_is_valid_jsonp_callback_with_underscore() {
1855 assert!(is_valid_jsonp_callback("handle_response"));
1856 assert!(is_valid_jsonp_callback("_cb"));
1857 }
1858
1859 #[test]
1860 fn test_is_valid_jsonp_callback_with_dot() {
1861 assert!(is_valid_jsonp_callback("module.callback"));
1862 assert!(is_valid_jsonp_callback("app.module.handle"));
1863 }
1864
1865 #[test]
1866 fn test_is_valid_jsonp_callback_with_digits() {
1867 assert!(is_valid_jsonp_callback("cb1"));
1868 assert!(is_valid_jsonp_callback("handle123"));
1869 }
1870
1871 #[test]
1872 fn test_is_valid_jsonp_callback_empty_is_invalid() {
1873 assert!(!is_valid_jsonp_callback(""));
1874 }
1875
1876 #[test]
1877 fn test_is_valid_jsonp_callback_starting_with_digit_is_invalid() {
1878 assert!(!is_valid_jsonp_callback("1callback"));
1880 assert!(!is_valid_jsonp_callback("9cb"));
1881 }
1882
1883 #[test]
1884 fn test_is_valid_jsonp_callback_with_special_chars_is_invalid() {
1885 assert!(!is_valid_jsonp_callback("alert(1)"));
1887 assert!(!is_valid_jsonp_callback("<script>"));
1888 assert!(!is_valid_jsonp_callback("cb;evil()"));
1889 assert!(!is_valid_jsonp_callback("cb'"));
1890 assert!(!is_valid_jsonp_callback("cb\""));
1891 assert!(!is_valid_jsonp_callback("cb-"));
1892 assert!(!is_valid_jsonp_callback("cb+"));
1893 assert!(!is_valid_jsonp_callback("cb space"));
1894 }
1895
1896 #[test]
1897 fn test_is_valid_jsonp_callback_too_long_is_invalid() {
1898 let long_name = "a".repeat(129);
1900 assert!(!is_valid_jsonp_callback(&long_name));
1901 let max_name = "a".repeat(128);
1903 assert!(is_valid_jsonp_callback(&max_name));
1904 }
1905
1906 #[tokio::test]
1909 async fn test_respond_jsonp_basic_format() {
1910 let data = serde_json::json!({"id": 1, "name": "alice"});
1911 let resp = respond_jsonp("handleResponse", &data);
1912
1913 assert_eq!(resp.status(), StatusCode::OK);
1914 assert_eq!(
1915 resp.headers().get("content-type").unwrap(),
1916 "application/javascript; charset=utf-8"
1917 );
1918
1919 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1920 let body = String::from_utf8(bytes.to_vec()).unwrap();
1921 assert!(body.starts_with("handleResponse("));
1923 assert!(body.ends_with(");"));
1924 let json_str = &body["handleResponse(".len()..body.len() - ");".len()];
1926 let json: Value = serde_json::from_str(json_str).unwrap();
1927 assert_eq!(json["id"], 1);
1928 assert_eq!(json["name"], "alice");
1929 }
1930
1931 #[tokio::test]
1932 async fn test_respond_jsonp_with_array_data() {
1933 let data = serde_json::json!([1, 2, 3]);
1934 let resp = respond_jsonp("cb", &data);
1935
1936 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1937 let body = String::from_utf8(bytes.to_vec()).unwrap();
1938 assert_eq!(body, "cb([1,2,3]);");
1939 }
1940
1941 #[tokio::test]
1942 async fn test_respond_jsonp_with_string_data() {
1943 let data = Value::String("hello".to_string());
1944 let resp = respond_jsonp("cb", &data);
1945
1946 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1947 let body = String::from_utf8(bytes.to_vec()).unwrap();
1948 assert_eq!(body, r#"cb("hello");"#);
1950 }
1951
1952 #[tokio::test]
1953 async fn test_respond_jsonp_with_null_data() {
1954 let resp = respond_jsonp("cb", &Value::Null);
1955
1956 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1957 let body = String::from_utf8(bytes.to_vec()).unwrap();
1958 assert_eq!(body, "cb(null);");
1959 }
1960
1961 #[tokio::test]
1962 async fn test_respond_jsonp_with_empty_object() {
1963 let data = serde_json::json!({});
1964 let resp = respond_jsonp("cb", &data);
1965
1966 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
1967 let body = String::from_utf8(bytes.to_vec()).unwrap();
1968 assert_eq!(body, "cb({});");
1969 }
1970
1971 #[tokio::test]
1972 async fn test_respond_jsonp_invalid_callback_returns_400() {
1973 let data = serde_json::json!({"id": 1});
1974 let resp = respond_jsonp("alert(1)", &data);
1975
1976 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1977 assert_eq!(
1978 resp.headers().get("content-type").unwrap(),
1979 "text/plain; charset=utf-8"
1980 );
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, "Invalid JSONP callback name");
1985 }
1986
1987 #[tokio::test]
1988 async fn test_respond_jsonp_empty_callback_returns_400() {
1989 let data = serde_json::json!({"id": 1});
1990 let resp = respond_jsonp("", &data);
1991
1992 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
1993 }
1994
1995 #[tokio::test]
1996 async fn test_respond_jsonp_xss_injection_blocked() {
1997 let data = serde_json::json!({"id": 1});
1999 let malicious_names = vec![
2000 "<script>alert(1)</script>",
2001 "cb;</script><script>alert(1)",
2002 "cb'+alert(1)+'",
2003 "cb\";alert(1);\"",
2004 ];
2005
2006 for name in malicious_names {
2007 let resp = respond_jsonp(name, &data);
2008 assert_eq!(
2009 resp.status(),
2010 StatusCode::BAD_REQUEST,
2011 "恶意回调名必须被拒绝: {name}"
2012 );
2013 }
2014 }
2015
2016 #[tokio::test]
2019 async fn test_jsonp_response_wrapper_basic() {
2020 async fn handler() -> JsonpResponse {
2021 JsonpResponse("handleResponse".to_string(), serde_json::json!({"id": 1}))
2022 }
2023
2024 let router = axum::Router::new().route("/", axum::routing::get(handler));
2025 let req = Request::builder()
2026 .method(Method::GET)
2027 .uri("/")
2028 .body(Body::empty())
2029 .unwrap();
2030 let resp = router.oneshot(req).await.unwrap();
2031
2032 assert_eq!(resp.status(), StatusCode::OK);
2033 assert_eq!(
2034 resp.headers().get("content-type").unwrap(),
2035 "application/javascript; charset=utf-8"
2036 );
2037
2038 let bytes = resp.into_body().collect().await.unwrap().to_bytes();
2039 let body = String::from_utf8(bytes.to_vec()).unwrap();
2040 assert_eq!(body, r#"handleResponse({"id":1});"#);
2041 }
2042
2043 #[tokio::test]
2044 async fn test_jsonp_response_wrapper_invalid_callback() {
2045 async fn handler() -> JsonpResponse {
2046 JsonpResponse("1invalid".to_string(), serde_json::json!({}))
2048 }
2049
2050 let router = axum::Router::new().route("/", axum::routing::get(handler));
2051 let req = Request::builder()
2052 .method(Method::GET)
2053 .uri("/")
2054 .body(Body::empty())
2055 .unwrap();
2056 let resp = router.oneshot(req).await.unwrap();
2057
2058 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
2059 }
2060
2061 #[test]
2062 fn test_jsonp_response_clone_debug() {
2063 let resp = JsonpResponse("cb".to_string(), serde_json::json!({"id": 1}));
2064 let cloned = resp.clone();
2065 assert_eq!(cloned.0, "cb");
2066 assert_eq!(cloned.1["id"], 1);
2067
2068 let debug = format!("{resp:?}");
2069 assert!(debug.contains("JsonpResponse"));
2070 }
2071}