Skip to content

Commit

Permalink
Sync doc from yuque
Browse files Browse the repository at this point in the history
  • Loading branch information
SingleMoonlight committed Oct 2, 2024
1 parent 1f7c3cd commit 741f806
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 1 deletion.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
205 changes: 205 additions & 0 deletions docs/编程/使用jsonp请求心知天气数据.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
---
title: 使用jsonp请求心知天气数据
createTime: 2024/09/26 23:53:03
permalink: /article/use-jsonp-request-xinzhi-weather/
outline: [2, 6]
---
没想到一个简单的 API 请求,调通花了我一个小时,只能说官网文档描述的不够清晰,而且不同文档之间还有出入,挺让人迷糊的。

下面完整记录一下流程,其实真的很简单。

## 准备工作
### 获取公钥和私钥
使用 jsonp 请求天气数据,必须要通过公钥签名的方式进行请求,不能通过私钥请求。

当你添加了 API 产品后,即可在`控制台`->`产品管理`中点击某个产品,查看该 API 产品的密钥,这里用的就是免费版。

![](../.vuepress/public/images/0ec1ac493140fcb94fcb64e9a80a7ea4.png)

### 配置域名
心知天气回对发起请求的原站做域名校验,只有绑定的域名才被准许请求,对于本地调试来说,就需要绑定一个 `127.0.0.1`,见上图。

同时本地开发环境也需要修改,vue 默认的预览地址是 localhost,也需要改成 `127.0.0.1`。我这里使用的是 vite,在 defineConfig 中添加 server 成员即可。

```javascript
export default defineConfig({
// 其他配置
server: {
host: '127.0.0.1',
port: 8080,
},
})
```

## jsonp 封装
jsonp 请求比较具有工具性质,可以单独封装一下,后面也可以重复使用,这里直接给出我写的接口,里面有详细注释,后面使用均基于此。

```javascript
export function jsonpRequest(url, callbackName) {
return new Promise((resolve, reject) => {
// 检查 URL 是否已经包含 callback 参数
let urlAlreadyHasCallback = /[?&]callback=/.test(url);
let scriptSrc = url;
let actualCallbackName = callbackName;

if (!urlAlreadyHasCallback) {
// 使用用户指定的 callbackName 或生成一个唯一的回调函数名
actualCallbackName = `${callbackName}_${Date.now()}_${Math.random().toString(16).slice(2)}`;
}

// 定义全局的回调函数
window[actualCallbackName] = function (data) {
// 调用对应的 resolve 函数
resolve(data);
// 移除回调函数
delete window[actualCallbackName];
// 移除 script 标签
let script = document.querySelector(`script[src*="${actualCallbackName}"]`);
if (script) {
document.body.removeChild(script);
}
};

if (!urlAlreadyHasCallback) {
scriptSrc = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + actualCallbackName;
}

// 创建一个 script 标签
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptSrc;
script.onerror = function () {
reject(new Error('JSONP request failed'));
// 移除回调函数
delete window[actualCallbackName];
document.body.removeChild(script);
};

// 添加 script 标签到文档中
document.body.appendChild(script);
});
}
```

## 请求 URL 构建
这里以查询地址为例(因为需要地址 ID 去查询天气,这个是基础)。

### 确定请求 API
心知天气的 API,查询地址的 API 如下:

```javascript
https://api.seniverse.com/v3/location/search.json
```

其中请求所需参数为 `q=查询文本`

### 确认 jsonp API 参数
通过 jsonp 请求,除了基本的 API 参数外,还需要额外的公钥、时间戳、保活时间、回调函数,其中保活时间不是必须的。

```javascript
let params = {
q: searchText,
public_key: import.meta.env.VITE_APP_XINZHI_WEATHER_PUBLIC_KEY,
ts: Date.now(),
ttl: 300,
callback: callbackName,
};
```

### 计算签名
使用上一步的所有参数通过特定算法进行计算,具体步骤如下:

1、将所有参数按照 `key=value` 的格式字典序升序进行排列,中间用`&`进行连接。对于本请求,就是下面这样:

```javascript
callback=callbackName&public_key=your_public_key&q=beijing&ts=1727534325845&ttl=300
```

2、使用私钥,对上一步产生的字符串进行 HMAC-SHA1 的方式做哈希运算得到二进制结果,并用 Base64 的方式编码,得到一串哈希字符串。

3、再将上一步的哈希字符串做 URLEncode 编码,即可得到最终的签名。

看起来很复杂,实际很简单,这里直接贴出计算代码,需要注意的是,首先要安装 `crypto-js` 库:

```javascript
npm install crypto-js
```

然后就可以计算签名了:

```javascript
import CryptoJS from "crypto-js"

function generateSig(params, privateKey) {
// 先按照键的字典序升序排列所有的键值对
let entries = Object.entries(params).sort((a, b) => a[0].localeCompare(b[0]));
// 将键值对转换为 "key=value" 格式
let keyValuePairs = entries.map(([key, value]) => `${key}=${value}`);
// 将键值对连接成字符串
let paramsString = keyValuePairs.join('&');
// 生成签名
let sig = encodeURIComponent(
CryptoJS.HmacSHA1(paramsString, privateKey)
.toString(CryptoJS.enc.Base64));

return sig;
}
```

### 拼接最终 URL
计算完签名后,需要把签名在拼接到上一步排列好的字符串后面,即在后面加上 `&sig=sigString`

其实可以在上一步一并完成,这里合并为一个新的接口:

```javascript
function (params) {
// 先按照键的字典序升序排列所有的键值对
let entries = Object.entries(params).sort((a, b) => a[0].localeCompare(b[0]));
// 将键值对转换为 "key=value" 格式
let keyValuePairs = entries.map(([key, value]) => `${key}=${value}`);
// 将键值对连接成字符串
let paramsString = keyValuePairs.join('&');
// 生成签名
let sig = encodeURIComponent(
CryptoJS.HmacSHA1(paramsString, import.meta.env.VITE_APP_XINZHI_WEATHER_PRIVATE_KEY)
.toString(CryptoJS.enc.Base64));
paramsString += '&sig=' + sig;

return paramsString;
}
```
## 测试请求
结合封装好的 jsonp,就可以进行请求测试了:
```javascript
function searchLocation(searchText) {
return new Promise((resolve, reject) => {
let searchLocationUrl = 'https://api.seniverse.com/v3/location/search.json';
let callbackName = 'jsonpCallback_searchLocation';
let params = {
q: searchText,
public_key: import.meta.env.VITE_APP_XINZHI_WEATHER_PUBLIC_KEY,
ts: Date.now(),
ttl: 300,
callback: callbackName,
};
let paramsString = generateRequestParamsString(params);
searchLocationUrl += '?' + paramsString;
jsonpRequest(searchLocationUrl, callbackName)
.then(data => {
if (data.results.length > 0) {
resolve(data.results);
} else {
reject(data);
}
})
.catch(error => {
reject(error);
});
})
}
```
上述代码除了公钥和私钥需要自行替换外,可以直接使用,一共三个接口 `jsonpRequest``generateRequestParamsString``searchLocation`。搜索地址的接口也可以根据实际情况修改为其他业务,比如查询当前的天气,获取未来的天气等等。
52 changes: 52 additions & 0 deletions docs/编程/在GitHub Action中使用密钥.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: 在GitHub Action中使用密钥
createTime: 2024/09/28 23:24:49
permalink: /article/use-secret-in-github-action/
outline: [2, 6]
---
## 写在前面
我们在开发引用第三方 API 时,往往需要用到 API 平台要求的 KEY,这些密钥在本地可以通过文件的形式引入到代码中,然后版本维护的时候不上传到 GitHub,这样可以一定程度上避免密钥泄露的风险,当我们使用 GitHub 进行打包编译的时候,又需要用到密钥,这时候就可以用 GitHub 提供的密钥管理工具管理我们的密钥。

## 本地密钥
这里本地的密钥是通过 vite 环境变量的方式引用,即将密钥写在单独的环境变量文件 .env 中,然后在 .gitignore 中忽略 .env 文件。

```javascript
VITE_MY_PRAVITE_KEY=xxxxxxxxxxxx
```

代码里就可以通过如下方式直接使用:

```javascript
import.meta.env.VITE_MY_PRAVITE_KEY
```

## GitHub 密钥
### 创建密钥
在代码仓库 `Setting`->`Actions secrets and variables`->`Actions`->`Repository secrets`中新建一个密钥。

![](../.vuepress/public/images/5412c6455a3a6a003755d37ae3864ee2.png)

输入密钥名称和密钥,点击添加即可:

![](../.vuepress/public/images/a70be4b8f603226eb28257a603e74693.png)

### 使用密钥
然后在 GitHub Aciton 的部署文件 deploy.xml 中就可以通过如下方式进行引用:

```javascript
# 其他构建步骤

- name: Set environment secrets
env:
VITE_MY_PRAVITE_KEY: ${{ secrets.VITE_MY_PRAVITE_KEY }}
run: |
touch .env
echo "VITE_MY_PRAVITE_KEY = VITE_MY_PRAVITE_KEY" >> .env

# 其他构建步骤
```

这里就是在原本的部署流程中添加了一个创建环境变量文件的步骤,将环境变量写入 .env 文件中,然后再进行构建,这样就达到了和本地打包构建一样的效果。

在构建完成后,构建环境会直接被清除,因此上述步骤中创建的文件也并不会被保存。

60 changes: 59 additions & 1 deletion elog-coding.cache.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,73 @@
"realName": "语雀 + Elog + vuepress + Theme Plume + GitHub Pages + GitHub Action一站式博客解决方案",
"relativePath": "\\语雀 + Elog + vuepress + Theme Plume + GitHub Pages + GitHub Action一站式博客解决方案.md",
"docPath": "docs\\编程"
},
{
"id": 188211790,
"doc_id": "use-jsonp-request-xinzhi-weather",
"updated": 1727537084000,
"properties": {
"title": "使用jsonp请求心知天气数据",
"urlname": "use-jsonp-request-xinzhi-weather",
"date": "2024/09/26 23:53:03"
},
"catalog": [],
"realName": "使用jsonp请求心知天气数据",
"relativePath": "\\使用jsonp请求心知天气数据.md",
"docPath": "docs\\编程"
},
{
"id": 188436818,
"doc_id": "use-secret-in-github-action",
"updated": 1727884664000,
"properties": {
"title": "在GitHub Action中使用密钥",
"urlname": "use-secret-in-github-action",
"date": "2024/09/28 23:24:49"
},
"catalog": [],
"realName": "在GitHub Action中使用密钥",
"relativePath": "\\在GitHub Action中使用密钥.md",
"docPath": "docs\\编程"
}
],
"catalog": [
{
"type": "DOC",
"title": "在GitHub Action中使用密钥",
"uuid": "XR18jZz1Dh58T-2x",
"url": "use-secret-in-github-action",
"prev_uuid": "",
"sibling_uuid": "_i-p631UbPZ53HRw",
"child_uuid": "",
"parent_uuid": "",
"doc_id": 188436818,
"level": 0,
"id": 188436818,
"open_window": 1,
"visible": 1
},
{
"type": "DOC",
"title": "使用jsonp请求心知天气数据",
"uuid": "_i-p631UbPZ53HRw",
"url": "use-jsonp-request-xinzhi-weather",
"prev_uuid": "XR18jZz1Dh58T-2x",
"sibling_uuid": "7l1vH8I7Y2r5OwkO",
"child_uuid": "",
"parent_uuid": "",
"doc_id": 188211790,
"level": 0,
"id": 188211790,
"open_window": 1,
"visible": 1
},
{
"type": "DOC",
"title": "语雀 + Elog + vuepress + Theme Plume + GitHub Pages + GitHub Action一站式博客解决方案",
"uuid": "7l1vH8I7Y2r5OwkO",
"url": "yuque-elog-vuepress-plume-github-pages-action-one-stop-blog-solution",
"prev_uuid": "",
"prev_uuid": "_i-p631UbPZ53HRw",
"sibling_uuid": "86f48FlwoR4GN3TK",
"child_uuid": "",
"parent_uuid": "",
Expand Down

0 comments on commit 741f806

Please sign in to comment.