JavaScript 调用 JSON API 实战
JavaScript 调用 JSON API 实战
迷失的小K前言
最近很久都没有练习自己写 JS 脚本的能力了,刚好在我的博客中需要读取用户的IP等信息,本想着网上直接拿一个 API 调用就好,但是很多 API 不是数据收集参数不够多,就是只允许企业和付费用户调用,好不容意找到个很好的 API, 但是其数据返回的全是英文。最后艰难选择了最后一个全是英文的API, 自己写脚本好了。当然考虑的还不止这些,包括异常处理,sessionStorage 减少接口调用等等优化方法,逐步完善了这个脚本。
接下来我就分享一下写整个脚本的经过。
代码实现
获取 API
我选取的是 IPINFO 的 API, 该 API 需要注册,注册后在主页就可以拿到自己的token,如图所示:
该网站的免费用户每月可以获得 50,000 次免费的请求量,对于我这种小博客来说是足够了,需要更大的请求量也可以升级套餐,但是价格过高( $99/month )
调用 API
首先我们初始化 API 地址:
// your_token 的位置填入自己的 token
var apiUrl = 'https://ipinfo.io/json?token=your_token';当我们接受 API 返回的 JSON 数据时用
fetch()
函数接收,正常情况下我们就可以直接使用数据了,但是若遇到特殊情况,比如说 API 调用失败,我们需要抛出异常并进行异常处理。代码实现:
fetch(apiUrl)
.then(response=>{
if(!response.ok){
// 抛出异常
throw new Error('Can\'t Fetch API Url.');
}
return response.json();
})
.then(data=>{
// 这里是使用数据
writeTable(data);
})
.catch(error => {
// 如果出现异常,我们将返回 null 以告知函数没有读取到数据
writeTable(null);
// 并在终端返回错误信息
console.error('There was a problem with your fetch operation:', error);
});这样的代码看似很完美,但是我们简单测试一下就暴露了一个问题,每当我们访问一次这个页面时,该方法就被调用了一次,API 的使用次数就会减一,这让我们本不富裕的请求量更雪上加霜,那我们有什么方法可以优化一下呢?
优化方法大致分为两种:
- 后端存储数据
- 前端 sessionStorage 或 localStorage
由于我们是纯静态页面,故选择用第二种方法,那到底是用 sessionStorage 还是 localStorage?
sessionStorage 的特点是当我们的数据存入 sessionStorage 后,会缓存在当前标签页中,当标签页被关闭时,数据随机销毁。 localStorage 的特点是数据会永久存储在浏览器的 cache 中,即使关闭标签页和窗口,数据也不会丢失。
那么根据我们获取用户 IP 的这个需求来看,IP 的时效性很强,换一个 Wi-Fi 就会出现变化,所以我们应该选取 sessionStorage 临时存储信息。
代码实现:
// 从 sessionStorage 读取数据
const storedFormData = sessionStorage.getItem('fromData');
// 如果 sessionStorage 存在数据则优先使用 sessionStorage 的数据
if (storedFormData) {
const formData = JSON.parse(storedFormData);
writeTable(formData);
} else {
fetch(apiUrl)
.then(response=>{
if(!response.ok){
throw new Error('Can\'t Fetch API Url.');
}
return response.json();
})
.then(data=>{
writeTable(data);
// 当成功读取数据时,将数据存入 sessionStorage
sessionStorage.setItem('fromData', JSON.stringify(data));
})
.catch(error => {
writeTable(null);
console.error('There was a problem with your fetch operation:', error);
});
}我们离完美的代码又近了一步,但是有些朋友可能就会问:我用的 PJAX,无加载的情况下每个页面都会执行这个函数,但是又因为部分变量在别的页面根本不存在就会报错怎么办?
事实上,JS BOM 中有个非常好用的方法
window.location.pathname
, 该方法会返回当前页面的路径,那么我们只需要判断一下该页面是否为需要执行该 JS 脚本的页面就好了。代码实现:
// 封装成一个函数
function loadTableData(){
// 读取当前页路径
const currentPagePath = window.location.pathname;
// 判断是否为当前页,是则执行
if (currentPagePath === '/privacy/') {
const storedFormData = sessionStorage.getItem('fromData');
if (storedFormData) {
const formData = JSON.parse(storedFormData);
writeTable(formData);
} else {
fetch(apiUrl)
.then(response=>{
if(!response.ok){
throw new Error('Can\'t Fetch API Url.');
}
return response.json();
})
.then(data=>{
writeTable(data);
sessionStorage.setItem('fromData', JSON.stringify(data));
})
.catch(error => {
writeTable(null);
console.error('There was a problem with your fetch operation:', error);
});
}
}
};近乎完美!我们只要进行最后一步,在网页加载成功后执行该脚本就成功了!
// 在刷新后执行
$(()=>{loadTableData()});
// 在 pjax 执行完成后执行,防止换页后数据丢失
$(document).on('pjax:complete', ()=>{loadTableData()});
写入数据
在我们拿到数据后,要进行数据利用,如上文所示,我们调用了 writeTable()
方法使用了数据,现在我们就要对 writeTable()
进行实现。
首先我们简单的写入下数据,JSON的结构直接对应代码结构,比如我们初始化一组数据:
const jsonData = {
"ip": "14.198.50.29",
"data": {
"hostname": "014198050029.ctinets.com",
}
};那么取出
hostname
的方法为:console.log(jsonData.data.hostname);
接下来我们简单实现下 API 的读取:
function writeTable(data){
$('#userAgentCountry').html(data.country);
$('#userAgentIp').html(data.ip);
$('#userAgentRegion').html(data.region);
$('#userAgentCity').html(data.city);
$('#userAgentIsp').html(data.org);
// 我们直接用内置的方法获取到用户的 UA 信息
$('#userAgentDevice').html(navigator.userAgent);
}我们把抛出异常时的情况进行处理
function writeTable(data){
if (data === null){
$('#userAgentCountry').html('无法获取信息');
$('#userAgentIp').html('无法获取信息');
$('#userAgentRegion').html('无法获取信息');
$('#userAgentCity').html('无法获取信息');
$('#userAgentIsp').html('无法获取信息');
$('#userAgentDevice').html(navigator.userAgent);
} else {
$('#userAgentCountry').html(data.country);
$('#userAgentIp').html(data.ip);
$('#userAgentRegion').html(data.region);
$('#userAgentCity').html(data.city);
$('#userAgentIsp').html(data.org);
$('#userAgentDevice').html(navigator.userAgent);
}
}但是我们执行了如此多的 DOM 选择器,为了节省内存,我们初始化他们并直接调用初始化好的选择器
function writeTable(data){
var $userAgentIp = $('#userAgentIp'),
$userAgentCountry = $('#userAgentCountry'),
$userAgentRegion = $('#userAgentRegion'),
$userAgentCity = $('#userAgentCity'),
$userAgentIsp = $('#userAgentIsp'),
$userAgentDevice = $('#userAgentDevice');
if (data === null){
$userAgentIp.html('无法获取信息');
$userAgentCountry.html('无法获取信息');
$userAgentRegion.html('无法获取信息');
$userAgentCity.html('无法获取信息');
$userAgentIsp.html('无法获取信息');
$userAgentDevice.html(navigator.userAgent);
} else {
$userAgentIp.html(data.ip);
$userAgentCountry.html(data.country);
$userAgentRegion.html(data.region);
$userAgentCity.html(data.city);
$userAgentIsp.html(data.org);
$userAgentDevice.html(navigator.userAgent);
}
}
格式化 ISO 国家代码
因为默认的国家代码为二位英文码,故我们需要将其转换成中文。
function countryCodeToName(countryCode) { |
然后在 writeTable()
方法中调用就可以啦
function writeTable(data){ |
完整代码展示
var apiUrl = 'https://ipinfo.io/json?token=your_token'; |
附录:ISO 国家代码中文对照函数
var countryNames = { |
Troubleshooting
- 注意拼写错误,我把 form 拼成 from 了(乐
fetch()
方法仅允许 WEB API,无法直接将自己初始化的 JSON 变量传进去- 确定好
sessionStorage
的逻辑顺序,先存再取 token
一定要设置域名白名单
结语
多位大厨正在努力烹饪,真是一场酣畅淋漓的 API 调用啊(不是