├── python ├── utils │ ├── __init__.py │ ├── const_value.py │ └── helper.py ├── demo-requests.py ├── demo-urllib.py ├── demo-jsonp.py └── README.md ├── .gitignore ├── oc ├── ViewController.h ├── HmacSha1Tool.h ├── README.md ├── HmacSha1Tool.m └── ViewController.m ├── nodejs ├── package.json ├── index.js ├── README.md └── lib │ └── api.js ├── java ├── README.MD └── DemoJava.java ├── common-lisp ├── seniverse-demo.asd ├── README.md └── seniverse-demo.lisp ├── php ├── demo-jsonp.php ├── README.md ├── demo.php └── curl_example.php ├── javascript ├── index.html ├── jsonp.html ├── README.md └── scripts │ └── hmac-sha1.js └── README.md /python/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.pyc 3 | -------------------------------------------------------------------------------- /oc/ViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ViewController : UIViewController 4 | 5 | 6 | @end 7 | 8 | -------------------------------------------------------------------------------- /nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "crypto": "0.0.3", 4 | "optimist": "^0.6.1", 5 | "request-promise": "^2.0.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /java/README.MD: -------------------------------------------------------------------------------- 1 | # JAVA 生成 API URL 2 | 3 | 使用Java(no other dependences)生成签名,直接运行文件将会输出最终应该调用的 URL 4 | 5 | 之后将该 URL 传递至前端通过 JSONP 进行调用,或在后端 server 内调用 6 | 7 | URL 参数等参见官网API。 -------------------------------------------------------------------------------- /oc/HmacSha1Tool.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HmacSha1Tool : NSObject 4 | 5 | + (NSString *)HmacSha1Key:(NSString *)key data:(NSString *)data; 6 | @end 7 | -------------------------------------------------------------------------------- /python/utils/const_value.py: -------------------------------------------------------------------------------- 1 | KEY = '4r9bergjetiv1tsd' # API key 2 | UID = "U785B76FC9" # 用户ID 3 | 4 | LOCATION = 'beijing' # 所查询的位置,可以使用城市拼音、v3 ID、经纬度等 5 | API = 'https://api.seniverse.com/v3/weather/now.json' # API URL,可替换为其他 URL 6 | UNIT = 'c' # 单位 7 | LANGUAGE = 'zh-Hans' # 查询结果的返回语言 8 | -------------------------------------------------------------------------------- /python/utils/helper.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from .const_value import LOCATION 3 | 4 | 5 | def getLocation(): 6 | """get location from user input 7 | default beijing 8 | """ 9 | argvs = sys.argv 10 | location = argvs[1] if len(argvs) >= 2 else LOCATION 11 | return location 12 | -------------------------------------------------------------------------------- /common-lisp/seniverse-demo.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem :seniverse-demo 2 | :version "0.0.1" 3 | :description "seniverse weather API demo." 4 | :author "Muyinliu Xing " 5 | :depends-on (:flexi-streams 6 | :ironclad 7 | :cl-base64 8 | :drakma) 9 | :components ((:static-file "seniverse-demo.asd") 10 | (:file "seniverse-demo"))) 11 | -------------------------------------------------------------------------------- /oc/README.md: -------------------------------------------------------------------------------- 1 | # 心知天气 API 调用示例 Objective-C 2 | 3 | ## Demo 运行方法 4 | 5 | Xcode 新建一个项目,将 `HmacSha1Tool.h`, `HmacSha1Tool.m`, `ViewController.h`, `ViewController.m` 四个文件拷贝到项目中(`ViewController.h`, `ViewController.m` 直接替换),直接运行即可查看天气实况,如果想查看心知天气其他 API,可在 `ViewController.m` 文件中把 `viewDidLoad` 方法里面的 `TIANQI_NOW_WEATHER_URL` 替换为你想查看的URL即可。 6 | 7 | ## 注意 8 | 9 | - 本项目未做默认值为空校验,实际开发需做好校验。 10 | - 仅做开发参考使用,不建议在生产环境下暴露 key 11 | -------------------------------------------------------------------------------- /nodejs/index.js: -------------------------------------------------------------------------------- 1 | 2 | const UID = "U785B76FC9"; // 测试用 用户ID,请更换成您自己的用户ID 3 | const KEY = "4r9bergjetiv1tsd"; // 测试用 key,请更换成您自己的 Key 4 | var LOCATION = "Beijing"; // 除拼音外,还可以使用 v3 id、汉语等形式 5 | 6 | var Api = require('./lib/api.js') 7 | var argv = require('optimist').default('l', LOCATION).argv; 8 | 9 | 10 | var api = new Api(UID, KEY); 11 | api.getWeatherNow(argv.l).then(function(data) { 12 | console.log(JSON.stringify(data, null, 4)); 13 | }).catch(function(err) { 14 | console.log(err.error.status); 15 | }); 16 | -------------------------------------------------------------------------------- /python/demo-requests.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from utils.const_value import API, KEY, UNIT, LANGUAGE 3 | from utils.helper import getLocation 4 | 5 | 6 | def fetchWeather(location): 7 | result = requests.get(API, params={ 8 | 'key': KEY, 9 | 'location': location, 10 | 'language': LANGUAGE, 11 | 'unit': UNIT 12 | }, timeout=1) 13 | return result.text 14 | 15 | 16 | if __name__ == '__main__': 17 | location = getLocation() 18 | result = fetchWeather(location) 19 | print(result) 20 | -------------------------------------------------------------------------------- /php/demo-jsonp.php: -------------------------------------------------------------------------------- 1 | 但需注意,**调用时使用的域名或IP需与当前账号在官网上绑定的域名一致!**域名绑定可见:[我的服务->我的账号](http://www.seniverse.com/account) 19 | 20 | ## 参数说明 21 | 22 | ### 所查询的位置 23 | 24 | 查询的城市支持如下参数形式: 25 | 26 | - 城市ID 例如:`location=WX4FBXXFKE4F` 27 | - 城市中文名 例如:`location=北京` 28 | - 省市名称组合 例如:`location=辽宁朝阳`、`location=北京朝阳` 29 | - 城市拼音/英文名 例如:`location=beijing`(如拼音相同城市,可在之前加省份和空格,例:shanxi yulin) 30 | - 经纬度 例如:`location=39.93:116.40`(纬度前经度在后,冒号分隔) 31 | - IP地址 例如:`location=220.181.111.86`(某些IP地址可能无法定位到城市) 32 | - "ip"两个字母 自动识别请求IP地址,例如:`location=ip` 33 | -------------------------------------------------------------------------------- /nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Node.js 调用 API 实例 2 | 3 | ## 使用更安全的签名验证方式(荐) 4 | 5 | 进行签名验证的过程可见代码: [lib/api.js](./lib/api.js) 6 | 7 | > 但需注意,**调用时使用的域名或IP需与当前账号在官网上绑定的域名一致!**域名绑定可见:[我的服务->我的账号](http://www.seniverse.com/account) 8 | 9 | ## 本地运行 10 | 11 | ```bash 12 | $ npm install 13 | # 修改 index.js 文件中的 UID 和 KEY 14 | 15 | # 获取北京实况天气 16 | $ node index.js 17 | 18 | # 获取厦门实况天气 19 | $ node index.js -l xiamen 20 | $ node index.js -l 厦门 21 | ``` 22 | 23 | ## 参数说明 24 | 25 | ### 所查询的位置 26 | 27 | 查询的城市支持如下参数形式: 28 | 29 | - 城市ID 例如:`location=WX4FBXXFKE4F` 30 | - 城市中文名 例如:`location=北京` 31 | - 省市名称组合 例如:`location=辽宁朝阳`、`location=北京朝阳` 32 | - 城市拼音/英文名 例如:`location=beijing`(如拼音相同城市,可在之前加省份和空格,例:shanxi yulin) 33 | - 经纬度 例如:`location=39.93:116.40`(纬度前经度在后,冒号分隔) 34 | - IP地址 例如:`location=220.181.111.86`(某些IP地址可能无法定位到城市) 35 | - "ip"两个字母 自动识别请求IP地址,例如:`location=ip` 36 | -------------------------------------------------------------------------------- /php/demo.php: -------------------------------------------------------------------------------- 1 | time(), 12 | 'ttl' => 300, 13 | 'uid' => $uid, 14 | ]; 15 | $sig_data = http_build_query($param); // http_build_query 会自动进行 url 编码 16 | // 使用 HMAC-SHA1 方式,以 API 密钥(key)对上一步生成的参数字符串(raw)进行加密,然后 base64 编码 17 | $sig = base64_encode(hash_hmac('sha1', $sig_data, $key, TRUE)); 18 | 19 | // 拼接 url 中的 get 参数。文档:https://www.seniverse.com/doc#daily 20 | $param['sig'] = $sig; // 签名 21 | $param['location'] = $location; 22 | $param['start'] = 0; // 开始日期。0 = 今天天气 23 | $param['days'] = 1; // 查询天数,1 = 只查一天 24 | 25 | // 构造url 26 | $url = $api . '?' . http_build_query($param); 27 | echo $url; -------------------------------------------------------------------------------- /common-lisp/README.md: -------------------------------------------------------------------------------- 1 | # Common Lisp 调用 API 示例 2 | 3 | 使用库 `drakma` 发送 HTTP 请求进行 API 的调用 4 | 5 | ## 使用更安全的签名验证方式(荐) 6 | 7 | 使用以下库进行签名: 8 | 9 | - cl-base64 10 | - ironclad 11 | - flexi-streams 12 | 13 | ### Requirements 14 | 15 | - CL implementation installed(eg. SBCL, CCL or LispWorks) 16 | - Quicklisp installed 17 | 18 | ### Prepare 19 | 20 | ```shell 21 | $ git clone https://github.com/seniverse/seniverse-api-demos.git 22 | $ ln -s api-demos/common-lisp/ ~/quicklisp/local-projects/seniverse-demo 23 | ``` 24 | 25 | ### Run 26 | 27 | ```shell 28 | $ sbcl 29 | ``` 30 | 31 | 注:这里使用 [SBCL](http://sbcl.org) 作为运行环境 32 | 33 | ```lisp 34 | (ql:quickload 'seniverse-demo) 35 | ``` 36 | 37 | 示例输出结果: 38 | 39 | ``` 40 | {"results":[{"location":{"id":"WX4FBXXFKE4F","name":"北京","country":"CN","path":"北京,北京,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"多云","code":"4","temperature":"4","feels_like":"3","pressure":"1024","humidity":"53","visibility":"2.0","wind_direction":"东南","wind_direction_degree":"117","wind_speed":"3.6","wind_scale":"1","clouds":"","dew_point":""},"last_update":"2017-02-14T15:45:00+08:00"}]} 41 | 200 42 | ``` 43 | -------------------------------------------------------------------------------- /nodejs/lib/api.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Module denpendencies 5 | */ 6 | var crypto = require('crypto'); 7 | var querystring = require('querystring'); 8 | var request = require('request-promise'); 9 | 10 | var URL = 'https://api.seniverse.com/v3/'; 11 | 12 | function Api(uid, secretKey) { 13 | this.uid = uid; 14 | this.secretKey = secretKey; 15 | } 16 | 17 | Api.prototype.getSignatureParams = function() { 18 | 19 | var params = {} 20 | params.ts = Math.floor((new Date()).getTime() /1000); // 当前时间戳 21 | params.ttl = 300; // 过期时间 22 | params.uid = this.uid; // 用户ID 23 | 24 | var str = querystring.encode(params); // 构造请求字符串 25 | 26 | // 使用 HMAC-SHA1 方式,以API密钥(key)对上一步生成的参数字符串进行加密 27 | params.sig = crypto.createHmac('sha1', this.secretKey) 28 | .update(str) 29 | .digest('base64'); // 将加密结果用 base64 编码,并做一个 urlencode,得到签名 sig 30 | 31 | return params; 32 | } 33 | 34 | Api.prototype.getWeatherNow = function(location) { 35 | var params = this.getSignatureParams(); 36 | params.location = location; 37 | 38 | // 将构造的 URL 直接在后端 server 内调用 39 | return request({ 40 | url: URL + 'weather/now.json', 41 | qs : params, 42 | json : true 43 | }); 44 | } 45 | 46 | 47 | module.exports = Api; 48 | -------------------------------------------------------------------------------- /python/demo-jsonp.py: -------------------------------------------------------------------------------- 1 | """ 2 | 本例举例通过更安全的签名验证方式构造请求 URL 3 | 最终构造的 URL 可传递至前端,通过 JSONP 的方式进行调用, 4 | 也可在后端 sever 内发送请求 5 | """ 6 | 7 | import time 8 | import hashlib 9 | import hmac 10 | import base64 11 | from urllib import parse 12 | from utils.const_value import UID, KEY, API 13 | from utils.helper import getLocation 14 | 15 | 16 | def getJsonpUrl(location): 17 | """通过 HMAC-SHA1 进行签名验证 18 | 19 | 需注意,调用最终的 URL 时使用的域名或IP需与当前账号在官网上绑定的域名一致! 20 | 域名绑定可见:http://www.seniverse.com/account 21 | """ 22 | ts = int(time.time()) # 当前时间戳 23 | params = "ts={ts}&uid={uid}".format(ts=ts, uid=UID) # 构造验证参数字符串 24 | 25 | key = bytes(KEY, 'UTF-8') 26 | raw = bytes(params, 'UTF-8') 27 | 28 | # 使用 HMAC-SHA1 方式,以 API 密钥(key)对上一步生成的参数字符串(raw)进行加密 29 | digester = hmac.new(key, raw, hashlib.sha1).digest() 30 | 31 | # 将上一步生成的加密结果用 base64 编码,并做一个 urlencode,得到签名sig 32 | signature = base64.encodestring(digester).rstrip() 33 | sig = parse.quote(signature.decode('utf8')) 34 | 35 | # 构造最终请求的 url 36 | url = API + "?location={}&".format(location) + \ 37 | params + '&sig=' + sig + "&callback=?" 38 | return url 39 | 40 | 41 | if __name__ == '__main__': 42 | location = getLocation() 43 | url = getJsonpUrl(location) 44 | print(url) 45 | -------------------------------------------------------------------------------- /oc/HmacSha1Tool.m: -------------------------------------------------------------------------------- 1 | #import "HmacSha1Tool.h" 2 | #import 3 | @implementation HmacSha1Tool 4 | 5 | /** 6 | 通过 HMAC-SHA1,对请求参数字符串进行加密,得到一个签名字符串。 7 | 8 | @param key 你的 API 密钥 9 | @param data 请求参数字符串 10 | @return 签名字符串 sig 11 | */ 12 | + (NSString *)HmacSha1Key:(NSString *)key data:(NSString *)data { 13 | const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding]; 14 | const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding]; 15 | 16 | // HmacSHA1 加密 17 | unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH]; 18 | CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC); 19 | NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC 20 | length:sizeof(cHMAC)]; 21 | 22 | //将加密结果进行一次 BASE64 编码。 23 | NSString *hash = [HMAC base64EncodedStringWithOptions:0]; 24 | 25 | //将 BASE64 编码结果做一个 urlencode 26 | NSString *charactersToEscape = @"?!@#$^&%*+,:;='\"`<>()[]{}/\\| "; 27 | NSCharacterSet *allowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:charactersToEscape] invertedSet]; 28 | NSString *encodedUrl = [hash stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters]; 29 | 30 | //得到签名 sig 31 | return encodedUrl; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /php/curl_example.php: -------------------------------------------------------------------------------- 1 | time(), 28 | 'ttl' => 300, 29 | 'uid' => $uid, 30 | ]; 31 | $sig_data = http_build_query($param); // http_build_query 会自动进行 url 编码 32 | // 使用 HMAC-SHA1 方式,以 API 密钥(key)对上一步生成的参数字符串(raw)进行加密,然后 base64 编码 33 | $sig = base64_encode(hash_hmac('sha1', $sig_data, $key, TRUE)); 34 | 35 | // 拼接 url 中的 get 参数。文档:https://www.seniverse.com/doc#daily 36 | $param['sig'] = $sig; // 签名 37 | $param['location'] = $location; 38 | $param['start'] = 0; // 开始日期。0 = 今天天气 39 | $param['days'] = 1; // 查询天数,1 = 只查一天 40 | 41 | // 构造 url 42 | $url = $api . '?' . http_build_query($param); 43 | 44 | echo $url; 45 | echo httpGet($url); 46 | -------------------------------------------------------------------------------- /javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Seniverse API Test Page 7 | 8 | 9 | 10 | 11 |
12 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /javascript/jsonp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Seniverse API Jsonp Test Page 7 | 8 | 9 | 10 | 11 |
12 |
13 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # Python3 调用 API 示例 2 | 3 | Python3 通过发送网络请求进行 API 的调用 4 | 5 | ## 直接调用 6 | 7 | ### 使用 [requests](https://github.com/kennethreitz/requests/) 8 | 9 | 通过第三方 Http 请求库进行接口调用。 10 | 11 | ```bash 12 | $ pip3 install requests 13 | ``` 14 | 15 | ```python 16 | # 发送 get 请求 17 | result = requests.get(url, params, timeout) 18 | ``` 19 | 20 | ### 使用 urllib 21 | 22 | 通过官方标准库`urllib`进行接口的调用。 23 | 24 | 使用 https 的 API 时,直接调用 urllib 进行请求会报错:`urllib.error.URLError: `,需要我们引用`ssl`库进行配置 25 | 26 | 更多详细信息可查看:[Stackoverflow - "SSL: CERTIFICATE_VERIFY_FAILED" Error](http://stackoverflow.com/questions/27835619/ssl-certificate-verify-failed-error) 27 | 28 | ```python 29 | from urllib import request, parse 30 | 31 | # 通过 parse 将请求参数转为字符串 32 | params = parse.urlencode({ 33 | 'key': KEY, 34 | 'location': LOCATION, 35 | 'language': LANGUAGE, 36 | 'unit': UNIT 37 | }) 38 | 39 | # 构建请求 40 | req = request.Request(url) 41 | # 发送请求,读取返回值并进行 UTF-8 编码 42 | response = request.urlopen(req, context=gcontext).read().decode('UTF-8') 43 | ``` 44 | 45 | ## 使用更安全的签名验证方式(荐) 46 | 47 | 使用官方标准库进行签名: 48 | 49 | - time 50 | - hashlib 51 | - hmac 52 | - base64 53 | - urllib 54 | 55 | 具体签名方式可以参考:[demo-jsonp.py](./demo-jsonp.py),直接运行文件将会输出最终应该调用的 URL,该 URL 可由传递至前端进行调用,也可以在后端 server 内调用 56 | 57 | > 但需注意,**调用时使用的域名或IP需与当前账号在官网上绑定的域名一致!**域名绑定可见:[我的服务->我的账号](http://www.seniverse.com/account) 58 | 59 | ## 本地测试 60 | 61 | ```bash 62 | $ cd python 63 | $ ls # should has demo-requests.py, demo-urllib.py... 64 | 65 | # 获取默认地址实况天气(北京) 66 | $ python3 demo-requests.py 67 | $ python3 demo-urllib.py 68 | # 获取厦门实况天气 69 | $ python3 demo-requests.py xiamen 70 | $ python3 demo-requests.py 厦门 71 | $ python3 demo-urllib.py xiamen 72 | $ python3 demo-urllib.py 厦门 73 | 74 | # 获取 jsonp url 75 | $ python3 demo-jsonp.py # 北京 76 | $ python3 demo-requests.py xiamen # 厦门 77 | ``` 78 | 79 | ## 参数说明 80 | 81 | ### 所查询的位置 82 | 83 | 查询的城市支持如下参数形式: 84 | 85 | - 城市ID 例如:`location=WX4FBXXFKE4F` 86 | - 城市中文名 例如:`location=北京` 87 | - 省市名称组合 例如:`location=辽宁朝阳`、`location=北京朝阳` 88 | - 城市拼音/英文名 例如:`location=beijing`(如拼音相同城市,可在之前加省份和空格,例:shanxi yulin) 89 | - 经纬度 例如:`location=39.93:116.40`(纬度前经度在后,冒号分隔) 90 | - IP地址 例如:`location=220.181.111.86`(某些IP地址可能无法定位到城市) 91 | - "ip"两个字母 自动识别请求IP地址,例如:`location=ip` 92 | -------------------------------------------------------------------------------- /common-lisp/seniverse-demo.lisp: -------------------------------------------------------------------------------- 1 | (defpackage seniverse-demo 2 | (:use :cl)) 3 | 4 | (in-package :seniverse-demo) 5 | 6 | (defvar *api-key* "4r9bergjetiv1tsd") ;; Please use your own API key 7 | (defvar *user-id* "U785B76FC9") ;; Please use your own User ID 8 | (defvar *auth-ttl* 1800) ;; second 9 | (defvar *api-url* "https://api.seniverse.com/v3/weather/now.json") 10 | (defvar *universal-time-diff-unix-time* 2208988800) 11 | 12 | (defun universal-time->unix-time (universal-time) 13 | (- universal-time *universal-time-diff-unix-time*)) 14 | 15 | (defun unix-time->universal-time (unix-time) 16 | (+ unix-time *universal-time-diff-unix-time*)) 17 | 18 | (defun test () 19 | (let* ((unix-time (universal-time->unix-time (get-universal-time))) 20 | (string (format nil "ts=~A&ttl=~A&uid=~A" 21 | unix-time 22 | *auth-ttl* 23 | *user-id*)) 24 | (string-vector (flex:string-to-octets string :external-format :utf-8)) 25 | (key-vector (flex:string-to-octets *api-key* :external-format :utf-8)) 26 | (hmac (ironclad:make-hmac key-vector 'ironclad:sha1)) 27 | (signature (progn 28 | (ironclad:update-hmac hmac string-vector) 29 | (drakma:url-encode 30 | (base64:usb8-array-to-base64-string 31 | (ironclad:hmac-digest hmac)) 32 | :utf-8))) 33 | (request-url (format nil "~A?location=~A&ts=~A&ttl=~A&uid=~A&sig=~A" 34 | *api-url* 35 | "beijing" 36 | unix-time 37 | *auth-ttl* 38 | *user-id* 39 | signature))) 40 | (setf request-url (ppcre:regex-replace-all "%3D" request-url "%3d")) 41 | (format *terminal-io* "request-url: ~A~%~%" request-url) 42 | (multiple-value-bind (data status-code) 43 | (drakma:http-request request-url 44 | :preserve-uri t 45 | :additional-headers '(("Accept-Encoding" . ""))) 46 | (format *terminal-io* "~A~%~A~%" 47 | (flex:octets-to-string data :external-format :utf-8) 48 | status-code)))) 49 | 50 | (test) 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 心知天气 API 调用示例 2 | 3 | > 举例说明心知天气 API 的基本调用方法 4 | 5 | **注: 心知天气官网正式启用新域名:[www.seniverse.com](//www.seniverse.com) ,因此本项目由 thinkpage-api-demos 改名为 seniverse-api-demos** 6 | 7 | ## 目录 8 | 9 | - [Javascript](./javascript) **本示例仅做开发参考使用,不建议在生产环境下暴露 key!生产环境下请通过后端进行签名验证** 10 | 11 | - [直接调用](./javascript/index.html) 12 | - [JSONP](./javascript/jsonp.html) 13 | 14 | - [Node.js](./nodejs) 15 | 16 | - [使用更安全的签名验证方式(荐)](./nodejs/lib/api.js) 17 | 18 | - [Python](./python) 19 | 20 | - 直接调用 21 | 22 | - [urllib](./python/demo-urllib.py) 23 | - [requests](./python/demo-requests.py) 24 | 25 | - [使用更安全的签名验证方式(荐)](./python/demo-jsonp.py) 26 | 27 | - [PHP](./php) 28 | 29 | - [使用更安全的签名验证方式(荐)](./php/demo-jsonp.php) 30 | - [PHP demo, contributed by open source community](./php/demo.php) 31 | 32 | - Android 33 | 34 | - ~~可直接使用已封装好的 [Android SDK](https://github.com/seniverse/ThinkPageSDK_Android)~~ **注:该 SDK 已停止维护,不建议使用** 35 | - 或自主发起 http 请求,进行 API 调用 36 | 37 | - Ruby 38 | 39 | - [Java](./java) 40 | 41 | - Swift 42 | 43 | - [Object-C](./oc) 44 | 45 | - [Common Lisp](./common-lisp) 46 | 47 | - [使用更安全的签名验证方式(荐)](./common-lisp/seniverse-demo.lisp) 48 | - [cl-seniverse-api ( Lisp SDK )](https://github.com/muyinliu/cl-seniverse-api) 49 | 50 | ## 常见问题 51 | 52 | 1. 我在本地调试的时候,如何进行域名绑定? 53 | 54 | 本地调试时,请在 [我的账号 -> 我的服务](https://www.seniverse.com/account) 页面填写域名为:`127.0.0.1`。若 `127.0.0.1` 在本机无法访问,请注意是否开启了全局代理。 55 | 56 | 2. 为什么我绑定了 `127.0.0.1` 但访问时报错(没有找到域名,或者报跨域请求的错误)?(常见于纯前端 API 调用时) 57 | 58 | **请通过 `127.0.0.1` 的链接,通过 jsonp 的形式访问,而不是 `localhost`,`localhost` 访问会跨域!!!** 59 | 60 | **请通过 `127.0.0.1` 的链接,通过 jsonp 的形式访问,而不是 `localhost`,`localhost` 访问会跨域!!!** 61 | 62 | **请通过 `127.0.0.1` 的链接,通过 jsonp 的形式访问,而不是 `localhost`,`localhost` 访问会跨域!!!** 63 | 64 | 3. 关于 API 调用问题 65 | 66 | 为了保证您账号的安全,不建议纯前端进行 API 调用!仅仅通过前端调用会造成您的 UID 和 Key 暴露,可能会带来不必要的麻烦。推荐的调用方式有: 67 | 68 | - 后端进行 API 调用获取数据后交给前端渲染 69 | - 或者后端构造 JSONP 形式的请求链接,交给前端调用 70 | 71 | 4. 关于 API 调用失败的问题 72 | 73 | 可能的原因有: 74 | 75 | - ts 时间戳过短 76 | - Key 加密后的结果没有通过 Base64 编码 77 | 78 | ## Third party modules 79 | 80 | - Javascript 81 | - [seniverse-jsonp](https://github.com/seanhuai/seniverse-jsonp) 82 | 83 | ## Contributors 84 | 85 | - [hewiez](https://github.com/hewiez) 86 | - [wuqingzheng](https://github.com/wuqingzheng) 87 | - [flyingant](https://github.com/flyingant) 88 | - [muyinliu](https://github.com/muyinliu) 89 | - [mokeyjay](https://github.com/mokeyjay) 90 | - [ecmadao](https://github.com/ecmadao) 91 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | # Javascript 调用 API 示例 2 | 3 | > [查看自己的用户ID 和 API key](http://www.seniverse.com/doc#info) 4 | 5 | > 注:本示例仅做开发参考使用,不建议在生产环境下暴露 key! 6 | 7 | ## 基本步骤 8 | 9 | ### 构造验证参数字符串 10 | 11 | #### 参数说明 12 | 13 | - UNIX 时间戳(秒) 14 | - uid 用户ID 15 | - ttl 签名失效时间(可选) 16 | 17 | 使用上述参数构造参数字符串,例如: 18 | 19 | ```javascript 20 | var UID = "UEBACC6EE1"; 21 | var ts = Math.floor((new Date()).getTime() / 1000); 22 | var str = "ts="+ts+"&uid=" + UID; // 参数字符串 23 | ``` 24 | 25 | ### 加密 26 | 27 | 使用 HMAC-SHA1 方式,以API密钥(key)对上一步生成的参数字符串进行加密 28 | 29 | ```javascript 30 | // ... 31 | var str = "ts="+ts+"&uid=" + UID; 32 | 33 | var KEY = "qvbt7jgw87gbvseo"; 34 | var result = CryptoJS.HmacSHA1(str, KEY); 35 | ``` 36 | 37 | ### 编码 38 | 39 | 将上一步生成的加密结果用 base64 编码,并做一个 urlencode,得到签名 sig 40 | 41 | ```javascript 42 | var sig = result.toString(CryptoJS.enc.Base64); 43 | str = str + "&sig=" + sig; // 最终构造的已加密的参数字符串 44 | ``` 45 | 46 | ### 发送请求 47 | 48 | #### 直接发送 GET 请求 49 | 50 | > 详细代码可见:[Seniverse API Test](./index.html) 51 | 52 | ```javascript 53 | // 该 Api url 可获取天气实况。用户可替换为其他 url 进行测试 54 | var API = "http://api.seniverse.com/v3/weather/now.json?location=beijing&"; 55 | var url = API + str + "&callback=?"; 56 | $.getJSON(url) 57 | ``` 58 | 59 | #### 通过 JSONP 的形式调用 60 | 61 | > 详细代码可见:[Seniverse API JSONP Test](./jsonp.html) 62 | 63 | ```javascript 64 | // 该 Api url 可获取天气实况。用户可替换为其他 url 进行测试 65 | var API = "http://api.seniverse.com/v3/weather/now.json?location=beijing&"; 66 | var jsonpCallback = function(result) { 67 | // result 为回调函数 68 | // 用户在这里处理返回的结果 69 | } 70 | 71 | var url = API + str + "&callback=jsonpCallback"; 72 | 73 | // 向 HTML 中动态插入 script 标签,通过 JSONP 的方式进行调用 74 | var newScript = document.createElement('script'); 75 | newScript.type = 'text/javascript'; 76 | newScript.src = url; 77 | $('body').append(newScript); 78 | ``` 79 | 80 | ## 本地启动本示例 81 | 82 | 需要起本地 server, 测试中使用的 key 和 uid 绑定的域名是127.0.0.1. 83 | 84 | ```bash 85 | $ npm install http-server -g 86 | $ cd seniverse-api-demos/javascript 87 | $ http-server -p 3333 88 | ``` 89 | 90 | 打开浏览器访问 91 | 92 | - `http://127.0.0.1:3333/jsonp` 93 | 94 | *注:请通过 `127.0.0.1` 的链接访问 `/jsonp`,而不是 `localhost`,`localhost` 访问会跨域!!!* 95 | 96 | *注:请通过 `127.0.0.1` 的链接访问 `/jsonp`,而不是 `localhost`,`localhost` 访问会跨域!!!* 97 | 98 | *注:请通过 `127.0.0.1` 的链接访问 `/jsonp`,而不是 `localhost`,`localhost` 访问会跨域!!!* 99 | 100 | ## 参数说明 101 | 102 | ### 所查询的位置 103 | 104 | 查询的城市支持如下参数形式: 105 | 106 | - 城市ID 例如:`location=WX4FBXXFKE4F` 107 | - 城市中文名 例如:`location=北京` 108 | - 省市名称组合 例如:`location=辽宁朝阳`、`location=北京朝阳` 109 | - 城市拼音/英文名 例如:`location=beijing`(如拼音相同城市,可在之前加省份和空格,例:shanxi yulin) 110 | - 经纬度 例如:`location=39.93:116.40`(纬度前经度在后,冒号分隔) 111 | - IP地址 例如:`location=220.181.111.86`(某些IP地址可能无法定位到城市) 112 | - "ip"两个字母 自动识别请求IP地址,例如:`location=ip` 113 | -------------------------------------------------------------------------------- /java/DemoJava.java: -------------------------------------------------------------------------------- 1 | import javax.crypto.Mac; 2 | import javax.crypto.spec.SecretKeySpec; 3 | import java.security.SignatureException; 4 | import java.util.Date; 5 | import java.net.URLEncoder; 6 | import java.io.UnsupportedEncodingException; 7 | 8 | class DemoJava { 9 | 10 | private String TIANQI_DAILY_WEATHER_URL = "https://api.seniverse.com/v3/weather/daily.json"; 11 | 12 | private String TIANQI_API_SECRET_KEY = "YOUR API KEY"; // 13 | 14 | private String TIANQI_API_USER_ID = "YOUR USER ID"; // 15 | 16 | /** 17 | * Generate HmacSHA1 signature with given data string and key 18 | * @param data 19 | * @param key 20 | * @return 21 | * @throws SignatureException 22 | */ 23 | private String generateSignature(String data, String key) throws SignatureException { 24 | String result; 25 | try { 26 | // get an hmac_sha1 key from the raw key bytes 27 | SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1"); 28 | // get an hmac_sha1 Mac instance and initialize with the signing key 29 | Mac mac = Mac.getInstance("HmacSHA1"); 30 | mac.init(signingKey); 31 | // compute the hmac on input data bytes 32 | byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8")); 33 | result = new sun.misc.BASE64Encoder().encode(rawHmac); 34 | } 35 | catch (Exception e) { 36 | throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); 37 | } 38 | return result; 39 | } 40 | 41 | /** 42 | * Generate the URL to get diary weather 43 | * @param location 44 | * @param language 45 | * @param unit 46 | * @param start 47 | * @param days 48 | * @return 49 | */ 50 | public String generateGetDiaryWeatherURL( 51 | String location, 52 | String language, 53 | String unit, 54 | String start, 55 | String days 56 | ) throws SignatureException, UnsupportedEncodingException { 57 | String timestamp = String.valueOf(new Date().getTime()); 58 | String params = "ts=" + timestamp + "&ttl=30&uid=" + TIANQI_API_USER_ID; 59 | String signature = URLEncoder.encode(generateSignature(params, TIANQI_API_SECRET_KEY), "UTF-8"); 60 | return TIANQI_DAILY_WEATHER_URL + "?" + params + "&sig=" + signature + "&location=" + location + "&language=" + language + "&unit=" + unit + "&start=" + start + "&days=" + days; 61 | } 62 | 63 | public static void main(String args[]){ 64 | DemoJava demo = new DemoJava(); 65 | try { 66 | String url = demo.generateGetDiaryWeatherURL( 67 | "shanghai", 68 | "zh-Hans", 69 | "c", 70 | "1", 71 | "1" 72 | ); 73 | System.out.println("URL:" + url); 74 | } catch (Exception e) { 75 | System.out.println("Exception:" + e); 76 | } 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /oc/ViewController.m: -------------------------------------------------------------------------------- 1 | #import "ViewController.h" 2 | #import "HmacSha1Tool.h" 3 | 4 | @interface ViewController () 5 | @end 6 | 7 | @implementation ViewController 8 | 9 | #pragma mark - API KEY / USER ID 10 | 11 | static NSString *TIANQI_API_SECRET_KEY = @"xkojmyi74gj0kjgi"; // YOUR API KEY 12 | static NSString *TIANQI_API_USER_ID = @"U0E4A68FBD"; // YOUR USER ID 13 | 14 | #pragma mark - 天气、生活、位置接口(这里只测试了免费接口,付费接口未做测试) 15 | 16 | //---------------------- 天气 ---------------------- 17 | static NSString *TIANQI_DAILY_WEATHER_URL = @"https://api.seniverse.com/v3/weather/daily.json"; //逐日天气预报和昨日天气 URL 18 | static NSString *TIANQI_NOW_WEATHER_URL = @"https://api.seniverse.com/v3/weather/now.json"; //天气实况 URL 19 | 20 | //---------------------- 生活 ---------------------- 21 | static NSString *TIANQI_LIFE_SUGGESTION_URL = @"https://api.seniverse.com/v3/life/suggestion.json"; //生活指数 URL 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | // 创建 session 对象 27 | NSURLSession *session = [NSURLSession sharedSession]; 28 | NSString *urlStr = [self fetchWeatherWithURL:TIANQI_LIFE_SUGGESTION_URL ttl:@30 Location:@"shanghai" language:@"zh-Hans" unit:@"c" start:@"1" days:@"1"]; 29 | NSURL *url = [NSURL URLWithString:urlStr]; 30 | 31 | // 通过 URL 初始化 task,在 block 内部可以直接对返回的数据进行处理 32 | NSURLSessionTask *task = [session dataTaskWithURL:url 33 | completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 34 | NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); 35 | }]; 36 | 37 | // 启动任务 38 | [task resume]; 39 | } 40 | 41 | /** 42 | 配置带请求参数的 url 地址。 43 | 44 | @param url 需要请求的 url 地址。 例如:获取指定城市的天气实况 url 为 TIANQI_NOW_WEATHER_URL 45 | @param ttl 签名失效时间(可选),默认有效期为 1800 秒(30分钟) 46 | @param location 所查询的位置 47 | @param language 语言(可选)。 默认值:zh-Hans 48 | @param unit 单位 (可选)。默认值:c 49 | @param start 起始时间 (可选)。默认值:0 50 | @param days 天数 (可选)。 默认为你的权限允许的最多天数 51 | @return return 带请求参数的 url 地址 52 | */ 53 | - (NSString *)fetchWeatherWithURL:(NSString *)url ttl:(NSNumber *)ttl Location:(NSString *)location 54 | language:(NSString *)language unit:(NSString *)unit 55 | start:(NSString *)start days:(NSString *)days{ 56 | NSString *timestamp = [NSString stringWithFormat:@"%.0ld",time(NULL)]; 57 | NSString *params = [NSString stringWithFormat:@"ts=%@&ttl=%@&uid=%@", timestamp, ttl, TIANQI_API_USER_ID]; 58 | NSString *signature = [self getSigntureWithParams:params]; 59 | 60 | NSString *urlStr = [NSString stringWithFormat:@"%@?%@&sig=%@&location=%@&language=%@&unit=%@&start=%@&days=%@", 61 | url, params, signature, location, language, unit, start, days]; 62 | return urlStr; 63 | } 64 | 65 | /** 66 | 获得签名字符串,关于如何使用签名验证方式,详情见 https://www.seniverse.com/doc#sign 67 | 68 | @param params 验证参数字符串 69 | @return signature HMAC-SHA1 加密后得到的签名字符串 70 | */ 71 | - (NSString *)getSigntureWithParams:(NSString *)params{ 72 | NSString *signature = [HmacSha1Tool HmacSha1Key:TIANQI_API_SECRET_KEY data:params]; 73 | return signature; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /javascript/scripts/hmac-sha1.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 8 | p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>3]|=parseInt(a.substr(f, 10 | 2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}}, 11 | r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;ka;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^ 15 | g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})(); 16 | (function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d< 26 | e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); 27 | --------------------------------------------------------------------------------