oauth2_wechat/extensions/
internal_sns_userinfo_endpoint.rs1use oauth2_client::re_exports::{
2 http::header::ACCEPT, serde_json, thiserror, Body, Deserialize, Endpoint, HttpError, Request,
3 Response, SerdeJsonError, Serialize, Value, MIME_APPLICATION_JSON,
4};
5
6pub const URL: &str = "https://api.weixin.qq.com/sns/userinfo";
7
8#[derive(Debug, Clone)]
10pub struct SnsUserinfoEndpoint {
11 access_token: String,
12 openid: String,
13}
14impl SnsUserinfoEndpoint {
15 pub fn new(access_token: impl AsRef<str>, openid: impl AsRef<str>) -> Self {
16 Self {
17 access_token: access_token.as_ref().to_owned(),
18 openid: openid.as_ref().to_owned(),
19 }
20 }
21}
22
23impl Endpoint for SnsUserinfoEndpoint {
24 type RenderRequestError = SnsUserinfoEndpointError;
25
26 type ParseResponseOutput = SnsUserinfo;
27 type ParseResponseError = SnsUserinfoEndpointError;
28
29 fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
30 let url = format!(
31 "{}?access_token={}&openid={}&lang=zh_CN",
32 URL, self.access_token, self.openid
33 );
34
35 let request = Request::builder()
36 .uri(url)
37 .header(ACCEPT, MIME_APPLICATION_JSON)
38 .body(vec![])
39 .map_err(SnsUserinfoEndpointError::MakeRequestFailed)?;
40
41 Ok(request)
42 }
43
44 fn parse_response(
45 &self,
46 response: Response<Body>,
47 ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
48 let body = serde_json::from_slice::<SnsUserinfo>(response.body())
49 .map_err(SnsUserinfoEndpointError::DeResponseBodyFailed)?;
50
51 Ok(body)
52 }
53}
54
55#[derive(Deserialize, Serialize, Debug, Clone)]
56pub struct SnsUserinfo {
57 pub openid: String,
58 pub nickname: Option<String>,
59 pub sex: Option<usize>,
60 pub province: Option<String>,
61 pub city: Option<String>,
62 pub country: Option<String>,
63 pub headimgurl: Option<String>,
64 pub privilege: Option<Value>,
65 pub unionid: Option<String>,
66}
67
68#[derive(thiserror::Error, Debug)]
69pub enum SnsUserinfoEndpointError {
70 #[error("MakeRequestFailed {0}")]
71 MakeRequestFailed(HttpError),
72 #[error("DeResponseBodyFailed {0}")]
74 DeResponseBodyFailed(SerdeJsonError),
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn de_sns_userinfo() {
83 match serde_json::from_str::<SnsUserinfo>(include_str!(
84 "../../tests/response_body_json_files/sns_userinfo.json"
85 )) {
86 Ok(user) => {
87 assert_eq!(user.openid, "OPENID");
88 }
89 Err(err) => panic!("{err}"),
90 }
91 }
92}