JS 脚本语言第六课,发起第一个 HTTP 网络请求。

大家好!欢迎来到咱们 JS 脚本语言的第六课。前几节课咱们打好了基础,从这节课开始,咱们就要正式“出海”啦!去网络的海洋里逛一逛,看看能不能捞点有用的资源。今天咱们要聊的是“HTTP 网络请求和传参方法”。别被这些英文字母吓到,其实它们就像是我们和服务器之间寄信的规则。


先说说 HTTP。HTTP 全称是“超文本传输协议”,听着高大上,其实它就是互联网上最早期、最基础的“寄信方式”。大家回想一下本世纪初,那时候咱们还在用 2G 网络上网,网速慢不说,安全性也没太当回事。那时候的 HTTP 就像是在寄“明信片”,数据都是明文(也就是大白话)传输的。


这有啥问题呢?问题大了!你在明信片上写的内容,邮递员(黑客)半路上随便一瞥就能看个精光。如果你是个黑客,你不仅能截获别人的 HTTP 请求,还能模仿别人的语气去发请求,甚至直接盗取别人的账号和余额。


后来到了 3G、4G 时代,大家安全意识上来了。为了保护数据,设计者们给 HTTP 穿上了一件“防弹衣”,也就是加了 SSL/TLS 加密技术。这时候,HTTP 就升级成了 HTTPS(多出来的那个 S 就是 Safe 安全的意思)。


有了 HTTPS,寄信就变成了“寄密封信”。在寄信前,你和服务器会先偷偷商量一把“临时钥匙”(TLS 握手)。你用这把钥匙把信加密寄出去,服务器收到后再用同样的钥匙解密。这样一来,黑客就算半路截获了信件,看到的也只是一堆乱码,根本偷不走你的重要数据。


那 HTTP 请求到底怎么发呢?它分很多种“姿势”,今天咱们重点学最常用的两种:GET 和 POST。


先说 POST。POST 就像是你去邮局寄一个“大包裹”。当你需要上传大文件、提交一大堆复杂数据时,就得用它。如果你非要用 GET 去寄大包裹,服务器可能会直接拒收,甩给你一个“405 错误”,意思是“方法不允许,你这姿势不对”。


POST 的传参逻辑是这样的:数据不写在信封表面,而是装在包裹里面(也就是放在请求体 body 里)。同时,你得在包裹外面贴个标签(请求头),告诉服务器里面装的是啥格式。通常有两个关键标签:

第一个是 Content-Type(内容类型),告诉服务器里面是表单数据(x-www-form-urlencoded)还是 JSON 数据(application/json)。

第二个是 Authorization(授权),告诉服务器你是谁,是传 bearer 令牌还是 API 密钥。

搞清楚这两个标签,你就可以在包裹里(body)安心装你的参数了。


再说 GET。GET 和 POST 最大的区别是,GET 就像是“去便利店买瓶水”,主要用于获取数据,而不是提交大量数据。


GET 的传参方式非常直白,参数直接写在 URL(网址)后面。用问号 ? 开头,如果有多个参数,就用 & 连起来。

比如你要查某个用户的订单,网址可能会长这样:

https://api.example.com/getOrders?userId=123&page=1&pageSize=10

服务器一看网址,就知道你要查 123 号用户的第 1 页订单,每页 10 条。


但是!正因为参数全暴露在网址上,GET 绝对不适合传密码、身份证号这种敏感信息。就算用了 HTTPS,网址也可能会被浏览器的历史记录或者服务器的日志记下来,容易泄露。


总结一下:单纯查数据、删数据,优先用 GET;如果要新增、修改、上传大文件,或者参数特别多、特别复杂,那就乖乖用 POST。

(当然,实际开发中有些后端大佬不按套路出牌,GET 也往 body 里塞参数,POST 也往 URL 上挂参数。但咱们新手还是要先看接口文档,文档怎么写,咱们就怎么传,别自己瞎创新。)


很多高级接口是不让随便进的,需要“验明正身”。这时候请求头里就得带上 Authorization 字段。

最常见的有两种:

一种是 Basic Auth(基础认证),就是把“用户名:密码”拼起来,做个简单的 Base64 编码放进去。但这招太老了,相当于把密码写在明处只是稍微扭曲了一下,现在除了内网,基本不推荐用。

另一种是 Bearer Token(令牌认证),这也是现在最主流的。你先登录,服务器给你一个 token(令牌)。这 token 就像是你在这家店的“临时 VIP 卡”。以后你每次去请求需要登录的接口,都得在请求头里带上:

Authorization: Bearer <你的token>

只要带上它,服务器就认你。如果不带,或者 token 过期了,服务器就会返回 401(未授权)或 403(禁止访问),直接把你拒之门外。


还有一种是 API 密钥,通常是一长串字符,用来证明你是哪个第三方开发者。服务器靠它来算你的调用次数。这个密钥可能放在请求头里,也可能直接挂在 URL 参数里,具体看文档。


为了让你彻底弄懂,咱们把 HTTP 请求拆开看看。一个完整的请求其实分四部分:请求行、请求头、空行、请求体。

请求行:说明你要干嘛(GET/POST)、去哪(URL)、用啥协议(HTTP/1.1)。

请求头:一堆键值对标签,比如刚才说的 Content-Type 和 Authorization。

空行:一个固定的回车换行,用来分隔标签和正文。

请求体:真正装大数据的地方。


比如一个 POST 请求的“裸报文”(也就是没经过任何包装的原始文本)大概长这样:


POST /api/login HTTP/1.1

Host: example.com

Content-Type: application/json

Authorization: Bearer xxxx


{"username":"admin","password":"123456"}


你看,是不是一目了然?抓包工具里看到的也就是这些东西。弄懂了这个,网络请求对你来说就不再是个黑盒了。


接下来咱们看一段真实的 JS 代码,这是一个调用百度语音合成获取音频的函数。


getAudio: function(text, locale, voice, speed, volume, pitch) {

let spd = Math.round(speed * 15 / 100)

let vol = Math.round(volume * 15 / 100)

let pit = Math.round(pitch * 15 / 100)

let per = voice


let encText = encodeURIComponent(text)

let reqUrl = 'http://tsn.baidu.com/text2audio?tex=' + encText +

'&spd=' + spd + '&vol=' + vol + '&pit=' + pit +

'&per=' + per +

'&cuid=baidu_speech_demo&idx=1&cod=2&lan=zh&ctp=1&pdt=220&aue=1&res_tag=audio'


let resp = ttsrv.httpGet(reqUrl, {

'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'

})


// ========== 关键修复:状态码与内容检测 ==========

let code = resp.code()

let bodyBytes = resp.body().bytes()

let bodyStr = ttsrv.bytesToStr(bodyBytes)


// 1. 检测 HTTP 状态码

if (code !== 200) {

let snippet = bodyStr.length > 300 ? bodyStr.substring(0, 300) + '...' : bodyStr

throw new Error('❌ 百度TTS状态码错误 (HTTP ' + code + '): ' + snippet)

}


// 2. 检测 Content-Type(如果服务器返回了该头)

let contentType = resp.header('Content-Type') || ''

if (contentType.length > 0 &&

contentType.indexOf('audio') === -1 &&

contentType.indexOf('octet-stream') === -1) {

let snippet = bodyStr.length > 300 ? bodyStr.substring(0, 300) + '...' : bodyStr

throw new Error('❌ 百度TTS返回非音频格式 (Content-Type: ' + contentType + '): ' + snippet)

}


// 3. 检测内容魔数(如果以 { 或 < 开头,说明是 JSON 或 HTML 错误信息)

if (bodyBytes.length > 0 && (bodyBytes[0] === 0x7B || bodyBytes[0] === 0x3C)) {

let snippet = bodyStr.length > 200 ? bodyStr.substring(0, 200) + '...' : bodyStr

throw new Error('❌ 百度TTS返回文本错误 (疑似JSON/HTML): ' + snippet)

}


// 检测通过,返回音频流

return new java.io.ByteArrayInputStream(bodyBytes)

}


大家注意看,这个网址是 http 开头的,不是 https。为啥呢?因为这是个老接口,为了追求速度,而且它用了个特殊的“免死金牌”:URL 里有个 cuid=baidu_speech_demo。这相当于告诉服务器:“我是来演示的,别查我身份了”。但在正规的企业开发里,你可千万别这么干,正规流程得先拿 API 密钥去换个 access_token,再带着 token 去请求。


再看代码里,明明参数都拼在 URL 上了(GET 请求),为啥还要在请求头里写 Content-Type 呢?其实标准 HTTP 协议里 GET 是不需要的。但这里是为了“防乱码”。因为咱们传了中文文本,用了 encodeURIComponent 进行编码,加上这个请求头声明字符集,能确保服务器正确识别中文,不会把中文变成乱码。


最精彩的是代码里的“三层防御”(响应处理):

第一层:看状态码是不是 200(成功)。

第二层:看服务器返回的标签(Content-Type)里有没有 audio(音频)字样,防止它返回个错误文本。

第三层:看返回数据的“魔数”(第一个字节)。如果第一个字节是 {(JSON的开头)或者 <(HTML的开头),说明服务器其实报错了,返回的是错误提示,而不是音频。

这种“防御性编程”思维特别棒!因为网络世界很险恶,服务器可能宕机、可能限流,如果不做这些检查,直接把错误文本当音频播放,程序直接就崩溃了。


最后,咱们得聊聊“响应码”。你给服务器寄信,服务器回信时会附带一个三位数的状态码,告诉你结果咋样。

1xx 和 2xx:好消息,请求成功了。比如 200 就是最标准的“OK”。

3xx:重定向。意思是“你找错地方了,去隔壁看看”,服务器给你指了条新路。

4xx:客户端错误。也就是你的锅。比如你参数写错了、没带 token、或者网址写错了。

5xx:服务端错误。也就是服务器的锅。服务器自己崩了或者出 bug 了。


这里有个好玩的彩蛋:418 状态码。它的意思是“I'm a teapot(我是个茶壶)”。这其实是早年程序员的一个愚人节玩笑,意思是“你让我泡咖啡,但我只是个茶壶,我干不了”。现在有些服务器会用 418 来故意拦截那些不守规矩的爬虫,意思是“我看你就是个外行,不伺候了”。


常见的 4xx 错误咱们得记住:

400:请求格式不对(比如 JSON 写错了)。

401:未授权(没带 token 或 token 错了)。

403:禁止访问(带了 token,但你的级别不够)。

404:地址不存在(网址写错了,或者接口被隐藏了)。

405:方法不允许(比如人家只收 POST,你非发 GET)。

429:请求太频繁(被限流了,服务器让你“慢点,歇会儿再来”)。


记住,状态码只是第一道关卡。有时候服务器明明返回了 200(成功),但 body 里装的却是一段 JSON 错误信息(比如参数传错了)。所以咱们前面代码里的“三层防御”才那么重要,不能光看状态码,还得看里面到底装了啥。


好了,到这里,网络请求的骨架你已经完全掌握了:请求方法、请求头、请求体、参数位置、状态码、内容校验、鉴权方式。


下一步,就是真正动手在 JS 里写代码发请求了。当你学会把参数拼进 URL、把 JSON 塞进 body、带上 Authorization 头,你会发现,写网络请求就像是在写一封格式标准的信。只要知道收件人是谁、内容写哪、落款怎么签,剩下的就水到渠成了。


不过,发信前得先看“说明书”(API 文档)。如果调用的不是正规 API,那就得学一招“抓包”,去半路拦截别人的信,看看人家是怎么传的。等咱们把 HTTP 和 WebSocket 都讲透,我再手把手教你们怎么抓包分析。


哦对了,顺便提一嘴,HTTP 有个叫“长连接”的功能(参数里设置 Keep-Alive 为 true),就像是不挂断电话一直聊,能提高效率,如果服务器支持的话可以试试。


今天的课就到这里,大家消化一下,咱们下节课见,下课!

发表于:2026-09-03 12:04
0个回复
  • 消灭零回复
您还没有登录,登录后才可回复。 登录 注册