这篇文章记录一下自己写小程序后台时,如何通过https接口获取到用户手机号。大概流程如下:
1、获取通过认证的appId和secret;
2、利用appId和secret获取accessToken;
3、前端获取到用户的code;
4、通过code和accessToken获取手机号。
appId和secret是在微信公众平台上各种验证之后得到的。
参考来源: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
使用说明:
需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到动态令牌code,然后把code传到开发者后台,并在开发者后台调用微信后台提供的 phonenumber.getPhoneNumber 接口,消费code来换取用户手机号。每个code有效期为5分钟,且只能消费一次。
注: getPhoneNumber 返回的 code 与 wx.login 返回的 code 作用是不一样的,不能混用。
代码示例:
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
Page({
getPhoneNumber (e) {
console.log(e.detail.code)
}
})
参考:
https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/phonenumber/phonenumber.getPhoneNumber.html
https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
我这里直接上代码:
/**
* HTTP/HTTPS 请求封装: GET / POST
* 默认失败重试3次
* @author admin
*/
@Slf4j
public class HttpClientSslUtils {
/**
* 默认的字符编码格式
*/
private static final String DEFAULT_CHAR_SET = "UTF-8";
/**
* 默认连接超时时间 (毫秒)
*/
private static final Integer DEFAULT_CONNECTION_TIME_OUT = 2000;
/**
* 默认socket超时时间 (毫秒)
*/
private static final Integer DEFAULT_SOCKET_TIME_OUT = 3000;
/** socketTimeOut上限 */
private static final Integer SOCKET_TIME_OUT_UPPER_LIMIT = 10000;
/** socketTimeOut下限 */
private static final Integer SOCKET_TIME_OUT_LOWER_LIMIT = 1000;
private static CloseableHttpClient getHttpClient() {
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_SOCKET_TIME_OUT)
.setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT).build();
return HttpClients.custom().setDefaultRequestConfig(requestConfig)
.setRetryHandler(new DefaultHttpRequestRetryHandler()).build();
}
private static CloseableHttpClient getHttpClient(Integer socketTimeOut) {
RequestConfig requestConfig =
RequestConfig.custom().setSocketTimeout(socketTimeOut).setConnectTimeout(DEFAULT_CONNECTION_TIME_OUT)
.build();
return HttpClients.custom().setDefaultRequestConfig(requestConfig)
.setRetryHandler(new DefaultHttpRequestRetryHandler()).build();
}
public static String doPost(String url, String requestBody) throws Exception {
return doPost(url, requestBody, ContentType.APPLICATION_JSON);
}
public static String doPost(String url, String requestBody, Integer socketTimeOut) throws Exception {
return doPost(url, requestBody, ContentType.APPLICATION_JSON, null, socketTimeOut);
}
public static String doPost(String url, String requestBody, ContentType contentType) throws Exception {
return doPost(url, requestBody, contentType, null);
}
public static String doPost(String url, String requestBody, List<BasicHeader> headers) throws Exception {
return doPost(url, requestBody, ContentType.APPLICATION_JSON, headers);
}
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers)
throws Exception {
return doPost(url, requestBody, contentType, headers, getHttpClient());
}
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers,
Integer socketTimeOut) throws Exception {
if (socketTimeOut < SOCKET_TIME_OUT_LOWER_LIMIT || socketTimeOut > SOCKET_TIME_OUT_UPPER_LIMIT) {
log.error("socketTimeOut非法");
throw new Exception();
}
return doPost(url, requestBody, contentType, headers, getHttpClient(socketTimeOut));
}
/**
* 通用Post远程服务请求
* @param url
* 请求url地址
* @param requestBody
* 请求体body
* @param contentType
* 内容类型
* @param headers
* 请求头
* @return String 业务自行解析
* @throws Exception
*/
public static String doPost(String url, String requestBody, ContentType contentType, List<BasicHeader> headers,
CloseableHttpClient client) throws Exception {
// 构造http方法,设置请求和传输超时时间,重试3次
CloseableHttpResponse response = null;
long startTime = System.currentTimeMillis();
try {
HttpPost post = new HttpPost(url);
if (!CollectionUtils.isEmpty(headers)) {
for (BasicHeader header : headers) {
post.setHeader(header);
}
}
StringEntity entity =
new StringEntity(requestBody, ContentType.create(contentType.getMimeType(), DEFAULT_CHAR_SET));
post.setEntity(entity);
response = client.execute(post);
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity()));
throw new Exception();
}
String result = EntityUtils.toString(response.getEntity());
return result;
} finally {
releaseResourceAndLog(url, requestBody, response, startTime);
}
}
/**
* 暂时用于智慧园区业务联调方式
* @param url 业务请求url
* @param param 业务参数
* @return
* @throws Exception
*/
public static String doPostWithUrlEncoded(String url,
Map<String, String> param) throws Exception {
// 创建Httpclient对象
CloseableHttpClient httpClient = getHttpClient();
CloseableHttpResponse response = null;
long startTime = System.currentTimeMillis();
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<org.apache.http.NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, DEFAULT_CHAR_SET);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
log.error("业务请求返回失败:{}" , EntityUtils.toString(response.getEntity()));
throw new Exception();
}
String resultString = EntityUtils.toString(response.getEntity(), DEFAULT_CHAR_SET);
return resultString;
} finally {
releaseResourceAndLog(url, param == null ? null : param.toString(), response, startTime);
}
}
private static void releaseResourceAndLog(String url, String request, CloseableHttpResponse response, long startTime) {
if (null != response) {
try {
response.close();
recordInterfaceLog(startTime, url, request);
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
public static String doGet(String url) throws Exception {
return doGet(url, ContentType.DEFAULT_TEXT);
}
public static String doGet(String url, ContentType contentType) throws Exception {
return doGet(url, contentType, null);
}
public static String doGet(String url, List<BasicHeader> headers) throws Exception {
return doGet(url, ContentType.DEFAULT_TEXT, headers);
}
/**
* 通用Get远程服务请求
* @param url
* 请求参数
* @param contentType
* 请求参数类型
* @param headers
* 请求头可以填充
* @return String 业务自行解析数据
* @throws Exception
*/
public static String doGet(String url, ContentType contentType, List<BasicHeader> headers) throws Exception {
CloseableHttpResponse response = null;
long startTime = System.currentTimeMillis();
try {
CloseableHttpClient client = getHttpClient();
HttpGet httpGet = new HttpGet(url);
if (!CollectionUtils.isEmpty(headers)) {
for (BasicHeader header : headers) {
httpGet.setHeader(header);
}
}
if(contentType != null){
httpGet.setHeader("Content-Type", contentType.getMimeType());
}
response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.OK.value()) {
log.error("业务请求返回失败:{}", EntityUtils.toString(response.getEntity()));
throw new Exception();
}
String result = EntityUtils.toString(response.getEntity());
return result;
} finally {
releaseResourceAndLog(url, null, response, startTime);
}
}
private static void recordInterfaceLog(long startTime, String url, String request) {
long endTime = System.currentTimeMillis();
long timeCost = endTime - startTime;
MDC.put("totalTime", String.valueOf(timeCost));
MDC.put("url", url);
MDC.put("logType", "third-platform-service");
log.info("HttpClientSslUtils 远程请求:{} 参数:{} 耗时:{}ms", url, request, timeCost);
}
}
@Slf4j
public class JsonUtil {
/**
* 定义映射对象
*/
public static ObjectMapper objectMapper = new ObjectMapper();
/**
* 日期格式化
*/
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
static {
//对象的所有字段全部列入
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//取消默认转换timestamps形式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//忽略空Bean转json的错误
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//所有的日期格式都统一为以下的样式,即yyyy-MM-dd HH:mm:ss
objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
//忽略 在json字符串中存在,但是在java对象中不存在对应属性的情况。防止错误
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* string转JsonNode
*
* @param jsonString
* @return com.fasterxml.jackson.databind.JsonNode
*/
public static JsonNode stringToJsonNode(String jsonString) throws JsonProcessingException {
return objectMapper.readTree(jsonString);
}
/**
* 对象转json字符串
*
* @param obj
* @param <T>
*/
public static <T> String objToString(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.warn("Parse Object to String error : {}", e.getMessage());
return null;
}
}
/**
* 对象转格式化的字符串字符串
*
* @param obj
* @param <T>
* @return
*/
public static <T> String objToPrettyString(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.warn("Parse Object to String error : {}", e.getMessage());
return null;
}
}
/**
* json字符串转对象
*
* @param jsonString
* @param cls
* @param <T>
*/
public static <T> T stringToObj(String jsonString, Class<T> cls) {
if (StringUtils.isEmpty(jsonString) || cls == null) {
return null;
}
try {
return cls.equals(String.class) ? (T) jsonString : objectMapper.readValue(jsonString, cls);
} catch (JsonProcessingException e) {
log.warn("Parse String to Object error : {}", e.getMessage());
return null;
}
}
/**
* json字符串转对象(复杂泛型类型)
*
* @param jsonString
* @param typeReference
* @param <T>
* @return
*/
public static <T> T stringToObj(String jsonString, TypeReference<T> typeReference) {
if (StringUtils.isEmpty(jsonString) || typeReference == null) {
return null;
}
try {
return typeReference.getType().equals(String.class) ? (T) jsonString : objectMapper.readValue(jsonString, typeReference);
} catch (JsonProcessingException e) {
log.warn("Parse String to Object error : {}", e.getMessage());
return null;
}
}
}
public JSONObject getPhoneNumber(String code) {
JSONObject phone;
// 获取token
String token_url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", APPID, SECRET);
try {
JSONObject token = JSON.parseObject(HttpClientSslUtils.doGet(token_url));
if (token == null) {
log.info("获取token失败");
return null;
}
String accessToken = token.getString("access_token");
if (StringUtils.isEmpty(accessToken)) {
log.info("获取token失败");
return null;
}
log.info("token : {}", accessToken);
//获取phone
String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"
+ "?access_token=" + accessToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", code);
String reqJsonStr = JsonUtil.objToString(jsonObject);
phone = JSON.parseObject(HttpClientSslUtils.doPost(url, reqJsonStr));
if (phone == null) {
log.info("获取手机号失败");
return null;
}
return phone;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
记得导入jar包:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
到此搞定!
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我