全部的操做都是在獲取access_token的基礎上進行操做,access_token是根據appid和addsecret進行獲取。(這兩個參數對於我的公衆號是沒有的)html
文中的代碼只是取關鍵代碼,完整的代碼會在文章最後代表git地址。java
在公衆號後臺的開發者工具點擊公衆平臺測試帳號能夠獲取一個測試帳號,該測試帳號能夠測試公衆號提供的高級接口。git
接下來以進入配置便可進行測試獲取access_token:github
公衆平臺的API調用所需的access_token的使用及生成方式說明:web
一、建議公衆號開發者使用中控服務器統一獲取和刷新access_token,其餘業務邏輯服務器所使用的access_token均來自於該中控服務器,不該該各自去刷新,不然容易形成衝突,致使access_token覆蓋而影響業務;面試
二、目前access_token的有效期經過返回的expire_in來傳達,目前是7200秒以內的值。中控服務器須要根據這個有效時間提早去刷新新access_token。在刷新過程當中,中控服務器可對外繼續輸出的老access_token,此時公衆平臺後臺會保證在5分鐘內,新老access_token均可用,這保證了第三方業務的平滑過渡;spring
三、access_token的有效時間可能會在將來有調整,因此中控服務器不只須要內部定時主動刷新,還須要提供被動刷新access_token的接口,這樣便於業務服務器在API調用獲知access_token已超時的狀況下,能夠觸發access_token的刷新流程。數據庫
接口地址:apache
https請求方式: GETjson
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數 | 是否必須 | 說明 |
---|---|---|
grant_type | 是 | 獲取access_token填寫client_credential |
appid | 是 | 第三方用戶惟一憑證 |
secret | 是 | 第三方用戶惟一憑證密鑰,即appsecret |
代碼以下:
// 用於管理token /** * 獲取到的accessToken */ private static String accessToken; /** * 最後一次獲取Access_Token的時間 */ private static Date lastGetAccessTokenTime; public static String getAccessToken() { if (StringUtils.isBlank(accessToken) || isExpiredAccessToken()) { accessToken = null; lastGetAccessTokenTime = null; Map<String, Object> param = new HashMap<>(); param.put("grant_type", "client_credential"); param.put("appid", "appid"); param.put("secret", "appsecret"); String responseStr = HttpUtils.doGetWithParams(ACCESS_TOKEN_URL, param); if (StringUtils.isNotBlank(responseStr)) { JSONObject parseObject = JSONObject.parseObject(responseStr); if (parseObject != null && parseObject.containsKey("access_token")) { accessToken = parseObject.getString("access_token"); lastGetAccessTokenTime = new Date(); LOGGER.debug("調用接口獲取accessToken,獲取到的信息爲: {}", parseObject.toString()); } } } else { LOGGER.debug("使用未過期的accessToken: {}", accessToken); } return accessToken; } private static boolean isExpiredAccessToken() { if (lastGetAccessTokenTime == null) { return true; } // 1.5小時之後的就算失效 long existTime = 5400000L; long now = System.currentTimeMillis(); if (now - lastGetAccessTokenTime.getTime() > existTime) { return true; } return false; }
注意:獲取access_token的接口每日調用次數上限爲2000次,因此要妥善管理該token。 上面是若是1.5小時就從新獲取token,目前公衆號的持續時長是7200s,也就是2小時。
微信官方提供的素材管理的接口以下:
下面研究幾個素材接口的使用。
package cn.qlq.utils; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * http工具類的使用 * * @author Administrator * */ public class HttpUtils { private static Logger logger = LoggerFactory.getLogger(HttpUtils.class); /** * get請求 * * @return */ public static String doGet(String url, Map params) { try { HttpClient client = new DefaultHttpClient(); // 發送get請求 HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); /** 請求發送成功,並獲得響應 **/ if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { /** 讀取服務器返回過來的json字符串數據 **/ String strResult = EntityUtils.toString(response.getEntity(), "utf-8"); return strResult; } } catch (IOException e) { logger.debug("get data error"); } return null; } /** * get請求攜帶參數(注意只能是英文參數) * * @return */ public static String doGetWithParams(String url, Map params) { try { HttpClient client = new DefaultHttpClient(); // 若是攜帶參數,從新拼接get參數 if (params != null && params.size() > 0) { StringBuffer sb = new StringBuffer(url); sb.append("?"); for (Object key : params.keySet()) { sb.append(key + "=" + params.get(key) + "&"); } url = sb.toString().substring(0, sb.length() - 1); } // 發送get請求 HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); /** 請求發送成功,並獲得響應 **/ if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { /** 讀取服務器返回過來的json字符串數據 **/ String strResult = EntityUtils.toString(response.getEntity(), "utf-8"); return strResult; } } catch (IOException e) { logger.error("get data error"); } return null; } /** * post請求(用於key-value格式的參數) * * @param url * @param params * @return */ public static String doPost(String url, Map params) { BufferedReader in = null; try { // 定義HttpClient HttpClient client = new DefaultHttpClient(); // 實例化HTTP方法 HttpPost request = new HttpPost(); request.setURI(new URI(url)); // 設置參數 List<NameValuePair> nvps = new ArrayList<NameValuePair>(); for (Iterator iter = params.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String value = String.valueOf(params.get(name)); nvps.add(new BasicNameValuePair(name, value)); } request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); HttpResponse response = client.execute(request); int code = response.getStatusLine().getStatusCode(); if (code == 200) { // 請求成功 in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "utf-8")); StringBuffer sb = new StringBuffer(""); String line = ""; String NL = System.getProperty("line.separator"); while ((line = in.readLine()) != null) { sb.append(line + NL); } in.close(); return sb.toString(); } else { // System.out.println("狀態碼:" + code); return null; } } catch (Exception e) { e.printStackTrace(); return null; } } /** * post請求(用於請求json格式的參數) * * @param url * @param params * @return */ public static String doPost(String url, String params) throws Exception { CloseableHttpClient httpclient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(url);// 建立httpPost httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-Type", "application/json"); String charSet = "UTF-8"; StringEntity entity = new StringEntity(params, charSet); httpPost.setEntity(entity); CloseableHttpResponse response = null; try { response = httpclient.execute(httpPost); StatusLine status = response.getStatusLine(); int state = status.getStatusCode(); if (state == HttpStatus.SC_OK) { HttpEntity responseEntity = response.getEntity(); String jsonString = EntityUtils.toString(responseEntity); return jsonString; } else { logger.error("請求返回:" + state + "(" + url + ")"); } } finally { if (response != null) { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } try { httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** * * @param url * 上傳的URL * @param filePath * 本地路徑 * @param fileName * 上傳的name(至關於input框的name屬性) * @return */ public static String uploadFile(String url, String filePath, String fileName) { CloseableHttpClient httpclient = HttpClientBuilder.create().build(); CloseableHttpResponse response = null; String result = ""; try { HttpPost httppost = new HttpPost(url); // 能夠選擇文件,也能夠選擇附加的參數 HttpEntity req = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE) .addPart(fileName, new FileBody(new File(filePath)))// 上傳文件,若是不須要上傳文件注掉此行 .build(); httppost.setEntity(req); response = httpclient.execute(httppost); HttpEntity re = response.getEntity(); if (re != null) { result = new BufferedReader(new InputStreamReader(re.getContent())).readLine(); } EntityUtils.consume(re); } catch (Exception e) { } finally { IOUtils.closeQuietly(response); } return result; } }
公衆號常常有須要用到一些臨時性的多媒體素材的場景,例如在使用接口特別是發送消息時,對多媒體文件、多媒體消息的獲取和調用等操做,是經過media_id來進行的。素材管理接口對全部認證的訂閱號和服務號開放。
注意點:
一、臨時素材media_id是可複用的。
二、媒體文件在微信後臺保存時間爲3天,即3天后media_id失效。
三、上傳臨時素材的格式、大小限制與公衆平臺官網一致。
圖片(image): 2M,支持PNG\JPEG\JPG\GIF格式
語音(voice):2M,播放長度不超過60s,支持AMR\MP3格式
視頻(video):10MB,支持MP4格式
縮略圖(thumb):64KB,支持JPG格式
四、需使用https調用本接口。
代碼以下:
/** * 上傳臨時素材文件 * * @param filePath * 本地資源路徑 * @param type * 類型:有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb) * @return {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789} */ public static JSONObject uploadTemporaryMaterial(String filePath, String type) { String replacedUrl = URL_UPLOAD__TEMPEORARY_MATERIAL.replace("ACCESS_TOKEN", getAccessToken()).replace("TYPE", type); String uploadFileResult = HttpUtils.uploadFile(replacedUrl, filePath, "media"); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
測試代碼:
private static void uploadTemporaryMaterialTest() { JSONObject uploadPermanentMaterial = uploadTemporaryMaterial("G:/yzm.png", "image"); System.out.println(uploadPermanentMaterial); }
結果:
{"item":[],"media_id":"4PJ0BWSquFXwV6p9abthc8V4-CWhDYJxEOH-LaGvI9cfcwmCwWP9wBH-3iojYNYY","created_at":1572357820,"type":"image"}
對於經常使用的素材,開發者可經過本接口上傳到微信服務器,永久使用。新增的永久素材也能夠在公衆平臺官網素材管理模塊中查詢管理。
請注意:
一、永久圖片素材新增後,將帶有URL返回給開發者,開發者能夠在騰訊系域名內使用(騰訊系域名外使用,圖片將被屏蔽)。
二、公衆號的素材庫保存總數量有上限:圖文消息素材、圖片素材上限爲100000,其餘類型爲1000。
三、素材的格式大小等要求與公衆平臺官網一致:
圖片(image): 2M,支持bmp/png/jpeg/jpg/gif格式
語音(voice):2M,播放長度不超過60s,mp3/wma/wav/amr格式
視頻(video):10MB,支持MP4格式
縮略圖(thumb):64KB,支持JPG格式
四、圖文消息的具體內容中,微信後臺將過濾外部的圖片連接,圖片url需經過"上傳圖文消息內的圖片獲取URL"接口上傳圖片獲取。
五、"上傳圖文消息內的圖片獲取URL"接口所上傳的圖片,不佔用公衆號的素材庫中圖片數量的100000個的限制,圖片僅支持jpg/png格式,大小必須在1MB如下。
六、圖文消息支持正文中插入本身賬號和其餘公衆號已羣發文章連接的能力。
代碼:
/** * 上傳永久素材文件 * * @param filePath * 本地資源路徑 * @param type * 類型:有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb) * @return {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789} */ public static JSONObject uploadPermanentMaterial(String filePath, String type) { String replacedUrl = URL_UPLOAD__PERMANENT_MATERIAL.replace("ACCESS_TOKEN", getAccessToken()).replace("TYPE", type); String uploadFileResult = HttpUtils.uploadFile(replacedUrl, filePath, "media"); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
測試代碼:
private static void uploadPermanentMaterialTest() { JSONObject uploadPermanentMaterial = uploadPermanentMaterial("G:/yzm.png", "image"); System.out.println(uploadPermanentMaterial); }
結果:
{"item":[],"media_id":"56gT8viUy_wLQ8Q2s5H6L2ozaMo33Iojusr8Xfmyy_I","url":"http://mmbiz.qpic.cn/mmbiz_png/2bviaegGtHCF6iaCgWciblPGzBUtBZCKiaiaG4OXsnf4KWrzteHK5ZLibE9AwicUO7qu3hhgkZGvowN4u6z6PxU80aPoQ/0?wx_fmt=png"}
回傳的URL也能夠直接從瀏覽器解析
也支持新增永久圖文素材
接口描述:
http請求方式: POST,https協議
https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=ACCESS_TOKE
傳遞參數實例:
{ "articles": [{ "title": TITLE, "thumb_media_id": THUMB_MEDIA_ID, "author": AUTHOR, "digest": DIGEST, "show_cover_pic": SHOW_COVER_PIC(0 / 1), "content": CONTENT, "content_source_url": CONTENT_SOURCE_URL, "need_open_comment":1, "only_fans_can_comment":1 }, //若新增的是多圖文素材,則此處應還有幾段articles結構 ] }
代碼以下:
/** * * @param title * 標題 * @param thumb_media_id * 圖文消息的封面圖片素材id(必須是永久mediaID) * @param author * 做者 * @param digest * 圖文消息的摘要,僅有單圖文消息纔有摘要,多圖文此處爲空。若是本字段爲沒有填寫,則默認抓取正文前64個字。 * @param show_cover_pic * 是否顯示封面,0爲false,即不顯示,1爲true,即顯示 * @param content * 圖文消息的具體內容,支持HTML標籤,必須少於2萬字符,小於1M,且此處會去除JS,涉及圖片url必須來源 * "上傳圖文消息內的圖片獲取URL"接口獲取。外部圖片url將被過濾。 * @param ontent_source_url * 圖文消息的原文地址,即點擊「閱讀原文」後的URL * @param need_open_comment * Uint32 是否打開評論,0不打開,1打開 * @param only_fans_can_comment * Uint32 是否粉絲纔可評論,0全部人可評論,1粉絲纔可評論 * @return */ public static JSONObject uploadPermanentNews(String title, String thumb_media_id, String author, String digest, String show_cover_pic, String content, String ontent_source_url, String need_open_comment, String only_fans_can_comment) { Map<String, Object> params = new HashMap<>(); params.put("title", title); params.put("thumb_media_id", thumb_media_id); params.put("author", author); params.put("digest", digest); params.put("title", title); params.put("show_cover_pic", show_cover_pic); params.put("content", content); params.put("ontent_source_url", ontent_source_url); params.put("need_open_comment", need_open_comment); params.put("only_fans_can_comment", only_fans_can_comment); List<Map<String, Object>> articles = new ArrayList<>(); articles.add(params); HashMap<Object, Object> param = new HashMap<>(); param.put("articles", articles); String jsonString = JSONObject.toJSONString(param); System.out.println(jsonString); String replacedUrl = URL_UPLOAD__PERMANENT_NEWS.replace("ACCESS_TOKEN", getAccessToken()); String doPost = HttpUtils.doPost(replacedUrl, jsonString); if (StringUtils.isNotBlank(doPost)) { return JSONObject.parseObject(doPost); } return null; }
測試代碼:
private static void uploadPermanentNewsTest() { JSONObject uploadFile = uploadPermanentNews("18年寫的面試心得", "56gT8viUy_wLQ8Q2s5H6LxrbLx9I-ystQZ1Dv7IkNRo", "qiaozhi", "摘要", "1", "<html><body>111222</body><html>", "https://www.cnblogs.com/qlqwjy/p/9194434.html", "1", "1"); System.out.println(uploadFile); }
結果:
{"item":[],"media_id":"56gT8viUy_wLQ8Q2s5H6L2DeqyqigQj3EdOsec4pRdw"}
(1)獲取素材列表
接口描述以下:
http請求方式: POST
https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN
參數以下:
{ "type":TYPE, "offset":OFFSET, "count":COUNT }
參數 | 是否必須 | 說明 |
---|---|---|
type | 是 | 素材的類型,圖片(image)、視頻(video)、語音 (voice)、圖文(news) |
offset | 是 | 從所有素材的該偏移位置開始返回,0表示從第一個素材 返回 |
count | 是 | 返回素材的數量,取值在1到20之間 |
代碼以下:
/** * 獲取素材列表 * * @param type * 素材的類型,圖片(image)、視頻(video)、語音 (voice)、圖文(news) * @param offset * 從所有素材的該偏移位置開始返回,0表示從第一個素材 返回 * @param count * 返回素材的數量,取值在1到20之間 * @return */ public static JSONObject listPermanentMaterial(String type, int offset, int count) { String replacedUrl = URL_GET__PERMANENT_MATERIAL_LIST.replace("ACCESS_TOKEN", getAccessToken()); Map<String, Object> params = new LinkedHashMap<>(); params.put("type", type); params.put("offset", offset); params.put("count", count); String uploadFileResult = HttpUtils.doPost(replacedUrl, JSONObject.toJSONString(params)); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
測試代碼以下:
private static void listPermanentMaterialTest() { JSONObject listPermanentMaterial = listPermanentMaterial("news", 0, 20); System.out.println(listPermanentMaterial); }
結果:
{ "item": [{ "content": { "news_item": [{ "content": "<html>111222<html></html></html>", "author": "qiaozhi", "title": "18å¹´å\u0086\u0099ç\u009A\u0084é\u009D¢è¯\u0095å¿\u0083å¾\u0097", "thumb_media_id": "56gT8viUy_wLQ8Q2s5H6LxrbLx9I-ystQZ1Dv7IkNRo", "need_open_comment": 1, "thumb_url": "http://mmbiz.qpic.cn/mmbiz_jpg/2bviaegGtHCGCKPWT3SDPic5I3O7qHeMmKVyP8HdfOYB6mcCBx9Laib5VhkcSqvscYlu9YksLKvFSLVyJU99wLz5A/0?wx_fmt=jpeg", "show_cover_pic": 1, "content_source_url": "", "digest": "æ\u0091\u0098è¦\u0081", "only_fans_can_comment": 1, "url": "http://mp.weixin.qq.com/s?__biz=MzAxNTQxODYyMA==&mid=100000007&idx=1&sn=19cb106633ee6fd8cf5035ccc7c97bc2&chksm=1b8514942cf29d82b21954855b21ffb3dd47b110306e67481b4a98dc50a78fb247b47b4bdf61#rd" }], "update_time": 1572401773, "create_time": 1572401773 }, "update_time": 1572401773, "media_id": "56gT8viUy_wLQ8Q2s5H6L2DeqyqigQj3EdOsec4pRdw" }], "item_count": 1, "total_count": 1 }
(2)獲取永久素材信息:
代碼以下:
/** * 獲取永久素材信息 * * @param MEDIA_ID * mediaId * @return */ public static JSONObject getPermanentMaterial(String mediaId) { String replacedUrl = URL_GET__PERMANENT_MATERIAL.replace("ACCESS_TOKEN", getAccessToken()); Map<String, Object> params = new LinkedHashMap<>(); params.put("media_id", mediaId); String uploadFileResult = HttpUtils.doPost(replacedUrl, JSONObject.toJSONString(params)); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
測試代碼:
private static void getPermanentMaterialTest() { JSONObject permanentMaterial = getPermanentMaterial("56gT8viUy_wLQ8Q2s5H6L2DeqyqigQj3EdOsec4pRdw"); System.out.println(permanentMaterial); }
結果:
{ "news_item": [{ "content": "<html>111222<html></html></html>", "author": "qiaozhi", "title": "18å¹´å\u0086\u0099ç\u009A\u0084é\u009D¢è¯\u0095å¿\u0083å¾\u0097", "thumb_media_id": "56gT8viUy_wLQ8Q2s5H6LxrbLx9I-ystQZ1Dv7IkNRo", "need_open_comment": 1, "thumb_url": "http://mmbiz.qpic.cn/mmbiz_jpg/2bviaegGtHCGCKPWT3SDPic5I3O7qHeMmKVyP8HdfOYB6mcCBx9Laib5VhkcSqvscYlu9YksLKvFSLVyJU99wLz5A/0?wx_fmt=jpeg", "show_cover_pic": 1, "content_source_url": "", "digest": "æ\u0091\u0098è¦\u0081", "only_fans_can_comment": 1, "url": "http://mp.weixin.qq.com/s?__biz=MzAxNTQxODYyMA==&mid=100000007&idx=1&sn=19cb106633ee6fd8cf5035ccc7c97bc2&chksm=1b8514942cf29d82b21954855b21ffb3dd47b110306e67481b4a98dc50a78fb247b47b4bdf61#rd" }], "update_time": 1572401773, "create_time": 1572401773 }
返回參數說明:
參數 | 描述 |
---|---|
title | 圖文消息的標題 |
thumb_media_id | 圖文消息的封面圖片素材id(必須是永久mediaID) |
show_cover_pic | 是否顯示封面,0爲false,即不顯示,1爲true,即顯示 |
author | 做者 |
digest | 圖文消息的摘要,僅有單圖文消息纔有摘要,多圖文此處爲空 |
content | 圖文消息的具體內容,支持HTML標籤,必須少於2萬字符,小於1M,且此處會去除JS |
url | 圖文頁的URL |
content_source_url | 圖文消息的原文地址,即點擊「閱讀原文」後的URL |
補充:素材也能夠做爲消息進行回傳,好比回傳圖片消息的時候使用素材
例如修改咱們上篇回傳圖片消息的mediaId爲上面的永久素材的ID:
/** * 處理圖片消息(回覆一條圖片消息) * * @param message * @return */ private static AbstractResponseMessage handleImageMessage(Map<String, Object> message) { ImageMessage imageMessage = BeanUtils.map2Bean(message, ImageMessage.class, true); String url = imageMessage.getPicUrl(); // 能夠用圖片路徑作其餘操做 if (StringUtils.isNotBlank(url)) { System.out.println("您接收到的圖片消息url爲: " + url); } // 回傳一條圖片消息 ImageResponseMessage responseMessage = new ImageResponseMessage(); responseMessage.setCreateTime(System.currentTimeMillis()); responseMessage.setFromUserName(imageMessage.getToUserName()); responseMessage.setToUserName(imageMessage.getFromUserName()); responseMessage.setMediaId("56gT8viUy_wLQ8Q2s5H6L2ozaMo33Iojusr8Xfmyy_I"); responseMessage.setMsgType(MESSAGE_IMAGE); return responseMessage; }
測試結果:
用戶管理提供的接口以下:
在這裏測試獲取用戶基本信息:
在關注者與公衆號產生消息交互後,公衆號可得到關注者的OpenID(加密後的微信號,每一個用戶對每一個公衆號的OpenID是惟一的。對於不一樣公衆號,同一用戶的openid不一樣)。公衆號可經過本接口來根據OpenID獲取用戶基本信息,包括暱稱、頭像、性別、所在城市、語言和關注時間。
接口以下:
http請求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
參數 | 是否必須 | 說明 |
---|---|---|
access_token | 是 | 調用接口憑證 |
openid | 是 | 普通用戶的標識,對當前公衆號惟一 |
lang | 否 | 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語 |
openId能夠在用戶關注的時候獲取到,或者在接收消息的時候也能夠獲取到。
代碼以下:
/** * 獲取用戶信息 * * @param openId * 普通用戶的標識,對當前公衆號惟一 * @return JSON數據格式的用戶信息 */ public static JSONObject userInfo(String openId) { String replacedUrl = URL_GET__USER_INFO.replace("ACCESS_TOKEN", getAccessToken()).replace("OPENID", openId); String uploadFileResult = HttpUtils.doGet(replacedUrl, null); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
測試代碼:
private static void userInfoTest() { JSONObject userInfo = userInfo("openId"); System.out.println(userInfo); }
結果:
{"sex":1,"qr_scene":0,"nickname":"空城、舊夢","remark":"","qr_scene_str":"","city":"西城","country":"中國","subscribe_time":1572242301,"tagid_list":[],"subscribe_scene":"ADD_SCENE_QR_CODE","subscribe":1,"province":"北京","openid":"openId","language":"zh_CN","groupid":0,"headimgurl":"http://thirdwx.qlogo.cn/mmopen/qiaZvpkPgmiaNGLEXrgb7C9RQf8JYxrHFgPiaZHx8AoAdxwx78m3v9Vwhkaa4NrZxGwVniatWEWjU0xl3FxICnpjVZs7IkuyrO2t/132"}
接口以下:
http請求方式: POST
https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
POST數據示例:
{
"user_list": [
{
"openid": "otvxTs4dckWG7imySrJd6jSi0CWE",
"lang": "zh_CN"
},
{
"openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg",
"lang": "zh_CN"
}
]
}
代碼以下:
/** * 批量獲取用戶信息的 * * @return JSON數據格式的用戶信息 */ public static JSONObject batchGetUserInfo(String... openIds) { List<Map<String, Object>> userOpenIds = new LinkedList<>(); Map<String, Object> tmpMap = null; for (String openId : openIds) { tmpMap = new HashMap<>(); tmpMap.put("openid", openId); // 默認就是zh_CN,能夠不傳 tmpMap.put("lang", "zh_CN"); userOpenIds.add(tmpMap); } Map<String, Object> params = new HashMap<>(); params.put("user_list", userOpenIds); String param = JSONObject.toJSONString(params); System.out.println(param); String replacedUrl = URL_GET__USER_INFO_BATCH.replace("ACCESS_TOKEN", getAccessToken()); String uploadFileResult = HttpUtils.doPost(replacedUrl, param); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
微信提供的自定義菜單的接口以下:
1.自定義菜單最多包括3個一級菜單,每一個一級菜單最多包含5個二級菜單。
2.一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以「...」代替。
3.建立自定義菜單後,菜單的刷新策略是,在用戶進入公衆號會話頁或公衆號profile頁時,若是發現上一次拉取菜單的請求在5分鐘之前,就會拉取一下菜單,若是菜單有更新,就會刷新客戶端的菜單。測試時能夠嘗試取消關注公衆帳號後再次關注,則能夠看到建立後的效果。
自定義菜單接口可實現多種類型按鈕,以下:(最經常使用的就是Click和View類型的按鈕)
1.click:點擊推事件用戶點擊click類型按鈕後,微信服務器會經過消息接口推送消息類型爲event的結構給開發者(參考消息接口指南),而且帶上按鈕中開發者填寫的key值,開發者能夠經過自定義的key值與用戶進行交互;
2.view:跳轉URL用戶點擊view類型按鈕後,微信客戶端將會打開開發者在按鈕中填寫的網頁URL,可與網頁受權獲取用戶基本信息接口結合,得到用戶基本信息。
3.scancode_push:掃碼推事件用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後顯示掃描結果(若是是URL,將進入URL),且會將掃碼的結果傳給開發者,開發者能夠下發消息。
4.scancode_waitmsg:掃碼推事件且彈出「消息接收中」提示框用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後,將掃碼的結果傳給開發者,同時收起掃一掃工具,而後彈出「消息接收中」提示框,隨後可能會收到開發者下發的消息。
5.pic_sysphoto:彈出系統拍照發圖用戶點擊按鈕後,微信客戶端將調起系統相機,完成拍照操做後,會將拍攝的相片發送給開發者,並推送事件給開發者,同時收起系統相機,隨後可能會收到開發者下發的消息。
6.pic_photo_or_album:彈出拍照或者相冊發圖用戶點擊按鈕後,微信客戶端將彈出選擇器供用戶選擇「拍照」或者「從手機相冊選擇」。用戶選擇後即走其餘兩種流程。
7.pic_weixin:彈出微信相冊發圖器用戶點擊按鈕後,微信客戶端將調起微信相冊,完成選擇操做後,將選擇的相片發送給開發者的服務器,並推送事件給開發者,同時收起相冊,隨後可能會收到開發者下發的消息。
8.location_select:彈出地理位置選擇器用戶點擊按鈕後,微信客戶端將調起地理位置選擇工具,完成選擇操做後,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨後可能會收到開發者下發的消息。(有用)
9.media_id:下發消息(除文本消息)用戶點擊media_id類型按鈕後,微信服務器會將開發者填寫的永久素材id對應的素材下發給用戶,永久素材類型能夠是圖片、音頻、視頻、圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。
10.view_limited:跳轉圖文消息URL用戶點擊view_limited類型按鈕後,微信客戶端將打開開發者在按鈕中填寫的永久素材id對應的圖文消息URL,永久素材類型只支持圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。
請注意,3到8的全部事件,僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊後將沒有迴應,開發者也不能正常接收到事件推送。9和10,是專門給第三方平臺旗下未微信認證(具體而言,是資質認證未經過)的訂閱號準備的事件類型,它們是沒有事件推送的,能力相對受限,其餘類型的公衆號沒必要使用。
接口說明:
http請求方式:POST(請使用https協議)
https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
click和view的請求示例:
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"name":"菜單",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
},
{
"type":"click",
"name":"贊一下咱們",
"key":"V1001_GOOD"
}]
}]
}
其餘新增按鈕類型的請求示例:
{
"button": [
{
"name": "掃碼",
"sub_button": [
{
"type": "scancode_waitmsg",
"name": "掃碼帶提示",
"key": "rselfmenu_0_0",
"sub_button": [ ]
},
{
"type": "scancode_push",
"name": "掃碼推事件",
"key": "rselfmenu_0_1",
"sub_button": [ ]
}
]
},
{
"name": "發圖",
"sub_button": [
{
"type": "pic_sysphoto",
"name": "系統拍照發圖",
"key": "rselfmenu_1_0",
"sub_button": [ ]
},
{
"type": "pic_photo_or_album",
"name": "拍照或者相冊發圖",
"key": "rselfmenu_1_1",
"sub_button": [ ]
},
{
"type": "pic_weixin",
"name": "微信相冊發圖",
"key": "rselfmenu_1_2",
"sub_button": [ ]
}
]
},
{
"name": "發送位置",
"type": "location_select",
"key": "rselfmenu_2_0"
},
{
"type": "media_id",
"name": "圖片",
"media_id": "MEDIA_ID1"
},
{
"type": "view_limited",
"name": "圖文消息",
"media_id": "MEDIA_ID2"
}
]
}
代碼以下:
菜單實體類:
package cn.qlq.bean.weixin.menu; public class Menu { private Button[] button; public Button[] getButton() { return button; } public void setButton(Button[] button) { this.button = button; } }
package cn.qlq.bean.weixin.menu; public class Button { private String type; private String name; private Button[] sub_button; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Button[] getSub_button() { return sub_button; } public void setSub_button(Button[] sub_button) { this.sub_button = sub_button; } }
package cn.qlq.bean.weixin.menu; public class ClickButton extends Button { private String key; public String getKey() { return key; } public void setKey(String key) { this.key = key; } }
package cn.qlq.bean.weixin.menu; public class ViewButton extends Button { private String url; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
建立菜單工具類:
/** * 建立菜單 * * @param menu * JSON格式的菜單數據 * @return */ public static JSONObject createMenu(String menu) { String replacedUrl = URL_CREATE_MENU.replace("ACCESS_TOKEN", getAccessToken()); String uploadFileResult = HttpUtils.doPost(replacedUrl, menu); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; } /** * 組裝菜單 * * @return */ private static Menu initMenu() { Menu menu = new Menu(); ClickButton button11 = new ClickButton(); button11.setName("click菜單"); button11.setType("click"); button11.setKey("11"); ViewButton button21 = new ViewButton(); button21.setName("view菜單"); button21.setType("view"); button21.setUrl("http://b4a819d0.ngrok.io/index.html"); ClickButton button31 = new ClickButton(); button31.setName("掃碼事件"); button31.setType("scancode_push"); button31.setKey("31"); ClickButton button32 = new ClickButton(); button32.setName("地理位置"); button32.setType("location_select"); button32.setKey("32"); Button button = new Button(); button.setName("菜單"); button.setSub_button(new Button[] { button31, button32 }); menu.setButton(new Button[] { button11, button21, button }); return menu; }
修改接收事件的代碼(在前面入門寫的處理消息),處理CLICK事件和VIEW事件。(CLICK和VIEW事件會多傳一個EventKey參數,對於CLICK事件是按鈕的key,對於VIEW事件是跳轉的URL)
/** * 處理事件消息(訂閱和取消訂閱) * * @param messageMap * @return */ private static AbstractResponseMessage handleEventMessage(Map<String, Object> messageMap) { EventMessage message = BeanUtils.map2Bean(messageMap, EventMessage.class, true); String event = message.getEvent(); if (StringUtils.isNotBlank(event)) { System.out.println("您接收到事件消息, 事件類型爲: " + event); } // 關注的時候 if (MESSAGE_SUBSCRIBE.equals(event)) { System.out.println("這裏能夠向數據庫插入數據"); String responseMsg = MessageUtils.subscribeWelcomeText(); return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } // 取消關注(不用回傳消息.須要將用戶產生的數據刪除) if (MESSAGE_SUBSCRIBE.equals(event)) { System.out.println("這時須要從數據刪除 " + message.getFromUserName() + " 用戶產生的相關數據"); return null; } ClickViewEventMessage map2Bean = BeanUtils.map2Bean(messageMap, ClickViewEventMessage.class, true); // 點擊自定義的點擊菜單事件 if (MESSAGE_CLICK.equals(event)) { String eventKey = map2Bean.getEventKey(); String content = "您點擊的按鈕的key爲: " + eventKey; return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content); } // VIEW菜單的事件 if (MESSAGE_VIEW.equals(event)) { String eventKey = map2Bean.getEventKey(); String content = "您點擊的按鈕跳轉的URL爲: " + eventKey; return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content); } return null; }
測試代碼:
private static void createMenuTest() { Menu initMenu = initMenu(); JSONObject createMenu = createMenu(JSONObject.toJSONString(initMenu)); System.out.println(createMenu); }
結果:
{"errmsg":"ok","errcode":0}
手機公衆號查看效果以下:
使用接口建立自定義菜單後,開發者還可以使用接口查詢自定義菜單的結構。另外請注意,在設置了個性化菜單後,使用本自定義菜單查詢接口能夠獲取默認菜單和所有個性化菜單信息。
接口說明:
http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
代碼:
/** * 獲取自定義菜單 * * @return */ public static JSONObject getMenu() { String replacedUrl = URL_GET_MENU.replace("ACCESS_TOKEN", getAccessToken()); String uploadFileResult = HttpUtils.doGet(replacedUrl, null); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
結果:
{"menu":{"button":[{"name":"click菜單","sub_button":[],"type":"click","key":"11"},{"name":"view菜單","sub_button":[],"type":"view","url":"http://b4a819d0.ngrok.io/index.html"},{"name":"菜單","sub_button":[{"name":"掃碼事件","sub_button":[],"type":"scancode_push","key":"31"},{"name":"地理位置","sub_button":[],"type":"location_select","key":"32"}]}]}}
使用接口建立自定義菜單後,開發者還可以使用接口刪除當前使用的自定義菜單。該接口會刪除全部菜單。
代碼:
/** * 刪除自定義菜單 * * @return */ public static JSONObject deleteMenu() { String replacedUrl = URL_DELETE_MENU.replace("ACCESS_TOKEN", getAccessToken()); String uploadFileResult = HttpUtils.doGet(replacedUrl, null); if (StringUtils.isNotBlank(uploadFileResult)) { return JSONObject.parseObject(uploadFileResult); } return null; }
完整的代碼接收消息與回傳消息代碼參考:https://github.com/qiao-zhi/springboot-ssm.git