14.HTTP請求(原文在掘金更新,同名“我的GIS”)
HTTP請求
一、原生XHR(xhr請求使用瀏覽器中遵守同源策略的Ajax引擎實現(xiàn))??
AJAX
AJAX全稱為Asynchronous Javascript And XML,就是異步的JS和XML
通過AJAX可以在瀏覽器中向服務器發(fā)送異步請求,最大的優(yōu)勢:頁面無刷新獲取數(shù)據(jù)
AJAX不是新的編程語言,而是將現(xiàn)有的標準組合在一起使用的新方式
AJAX的優(yōu)點
可以無需刷新頁面而與服務器端進行通信
允許你根據(jù)用戶事件來更新部分頁面內容
AJAX的缺點
沒有瀏覽歷史,不能回退
存在跨域問題
SEO(搜索引擎優(yōu)化)不友好
AJAX核心對象
"XMLHttpRequest"??AJAX的所有操作都是通過該對象進行的
流程
<button id="btn">點我發(fā)送請求(原生js-ajax-get)</button> <div id="content"></div> <script type="text/javascript"> ? ? const btn = document.getElementById('btn'); ? ? const content = document.getElementById('content'); ? ? let isLoading ? ? ? ? ?//給按鈕綁定監(jiān)聽 ? ? btn.onclick = ()=>{ ? ? ? ? // if(isLoading) xhr.abort() // 避免多次請求 ? ? ? ? // 1. 創(chuàng)建XHR實例對象 ? ? ? ? const xhr = new XMLHttpRequest(); ? ? ? ? ? ? ? ? ?// xhr內部有5種狀態(tài),值分別為: 0、1、2、3、4 ? ? ? ? // 綁定監(jiān)聽:xhr實例對象在實例出來狀態(tài)是0, 收到返回數(shù)據(jù)狀態(tài)為4 ? ? ? ? xhr.onreadystatechange = ()=>{ ? ? ? ? ? ? if(xhr.readyState === 4){ ? ? ? ? ? ? ? ? if(xhr.status >= 200 && xhr.status < 300){ ? ? ? ? ? ? ? ? ? ? console.log(xhr.response); ? ? ? ? ? ? ? ? ? ? content.innerHTML = `<h3>${xhr.response}</h3>`; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? ? ? ? ?// 2. GET方式:指定method、url、攜帶params(服務端路徑占位符/:name)或query參數(shù) ? ? ? ? xhr.open('GET', 'http://127.0.0.1:8080/test_get?name="張三"&age=18'); ? ? ? ? ? ? ? ? ?// 3. 發(fā)送請求 ? ? ? ? xhr.send(); ? ? ? ? // is isLoading = true ? ? } </script> 復制代碼
// POST方式:指定method、url、攜帶params、query或body(主要)參數(shù) xhr.open('POST', 'http://127.0.0.1:8080/test_post') // 務必要帶請求體類型(編碼形式):urlencoded或json(application/json) xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') // 請求體攜帶: xhr.send('name=張三&age=18') // const p = {name:"張三", age:18}; xhr.send(JSON.stringify(p)); 復制代碼
xhr其他配置(API)
// responseType 用于指定返回數(shù)據(jù)的格式(解析方式) xhr.responseType = 'json' // 配置出錯回調 xhr.onerror = ()=>{ ? ? console.log("請求出錯了!") } // 設定超時時間 xhr.timeout = 2000 // 配置超時回調 xhr.ontimeout = ()=>{ ? ? console.log("請求超時了!") } 復制代碼
缺點:代碼繁瑣
二、跨域
JSONP(JSON with Padding)解決跨域??
利用不受同源策略CORS限制的script標簽訪問跨域鏈接(僅支持get方式),繞過有跨域限制的xhr 后端:
app.get('/test_jsonp', (request, response)=>{ ? ? const {callback} = request.query ? ? const person = [{name:'tom', age:18}, {name:'張三', age:18}] ? ? response.send(`${callback}(${JSON.stringify(person)})`) // js對象轉換成json字符串 }) 復制代碼
前端:
<button id="btn">點我獲取數(shù)據(jù)</button> <script> ? ? const btn = document.getElementById('btn') ? ? btn.onclick = ()=>{ ? ? ? ? // 1.創(chuàng)建script節(jié)點 ? ? ? ? const scriptNode = document.createElement('script') ? ? ? ? // 2.給節(jié)點指定src屬性(請求地址)(可使用query參數(shù)傳遞函數(shù)名) ? ? ? ? scriptNode.src = 'http://localhost:8080/test_jsonp?callback=demo' ? ? ? ? // 3.將節(jié)點放入頁面 ? ? ? ? document.body.appendChild(scriptNode) ? ? ? ? window.demo = (a)=>{ ? ? ? ? ? ? console.log(a); ? ? ? ? } ? ? ? ? // 4.請求完畢,移除節(jié)點 ? ? ? ? document.body.removeChild(scriptNode) ? ? } </script> 復制代碼
cors中間件后端express解決跨域
后端配置setHeader()跨域響應頭方法:
簡單請求(get; post):
app.get('/test_get ',(request, response)=>{ ? ? response.setHeader('Access-Control-Allow-Origin','*') ? ? response.setHeader('Access-Control-Expose-Headers' ,'*') ? ? response.send('hello_test_get') }) 復制代碼
復雜請求(put; delete 這兩種請求會有預請求options/嗅探請求):
// 預請求處理 app.options('/test_put', (request, response)=>{ ? ? // *通配符,可替換為指定前端請求源地址 ? ? response.setHeader('Access-Control-Allow-Origin','*') ? ? response.setHeader('Access-Cpntrol-Expose-Headers','*') ? ? response.setHeader('Access-Control-Allow-Methods','*') ? ? response.send() }) app.put('/test_put', (request, response)=>{ ? ? response.setHeader('Access-Control-Allow-Origin','*') ? ? response.setHeader('Access-Control-Expose-Headers','*') ? ? response.send('hello_test_put') }) 復制代碼
后端使用cors處理跨域:
const cors = require('cors') const app = express() app.use(cors()) // 使用中間件處理跨域 復制代碼
三、jQuery(封裝Ajax/依賴于XHR)(一般http請求:非Ajax請求)
完整版
<script> ? ? const btn1 = $('#btn1') ? ? const btn2 = $('#btn2') ? ? const content = $('#content') ? ? btn1.click(()=>{ ? ? ? ? // 使用jQuery發(fā)送ajax-get請求(完整版) ? ? ? ? $.ajax({ ? ? ? ? url: 'http://127.0.0.1:8080/jquery_get', // 請求地址 ? ? ? ? method: 'GET', // 請求方式 ? ? ? ? data: {school: 'atguigu'}, // 攜帶的數(shù)據(jù) ? ? ? ? dataType: 'json', // 配置響應數(shù)據(jù)格式 ? ? ? ? timeout: 200, // 超時時間 ? ? ? ? success: (result, reponseText, xhr)=>{ ? ? ? ? ? ? content.append(<div>姓名: ${result.name}, 年齡: ${result.age}</div>) ? ? ? ? }, // 成功的回調 ? ? ? ? error: ()=>{console.log("請求出錯了!")} // 失敗的回調 ? ? ? ? }) ? ? }) </script> 復制代碼
精簡版
$.get('http://127.0.0.1:8080/jquery_get', {school: 'atguigu'}, (data)=>{ ? ? console.log(data) // 成功的回調; },'json') 復制代碼
缺點:回調地獄問題(需借助Promise解決)
$.get('http://127.0.0.1:8080/jquery_get_1', {school_1: 'atguigu1'}, (data)=>{ ? ? console.log(data) // 成功的回調; ? ? $.get('http://127.0.0.1:8080/jquery_get_2', {school_2: 'atguigu2'}, (data)=>{ ? ? ? ? console.log(data) // 成功的回調; ? ? ? ? $.get('http://127.0.0.1:8080/jquery_get_3', {school_3: 'atguigu3'}, (data)=>{ ? ? ? ? ? ? console.log(data) // 成功的回調; ? ? ? ? },'json') ? ? },'json') },'json') 復制代碼
封裝jsonp跨域
<script> ? ? const btn = $('#btn') ? ? ? ? btn.click(()=>{ ? ? ? ? ? ? $.getJSON('http://localhost:8080/test_jsonp?callback=?',{參數(shù)},(data)=>{ ? ? ? ? ? ? console.log(data); ? ? ? ? }) ? ? ?}) </script> 復制代碼
四、axios(封裝XHR)(一般http請求:非Ajax請求)
前端流行的Ajax請求庫,react/vue都推薦使用axios發(fā)Ajax請求 axios特點:
基本promise的異步Ajax請求庫
瀏覽器端/node端都可以使用
支持請求/響應攔截器
支持請求取消
請求/響應數(shù)據(jù)轉換
批量發(fā)送多個請求
<!-- 1.axios調用的返回值是Promise實例 2.成功的值叫response,失敗的值叫error 3.axios成功的值是一個axios封裝的response對象,服務器返回的數(shù)據(jù)在response.data中 --> <body> ? ? <button id="btn1">獲取</button> ? ? <script type= "text/javascript"> ? ? // 獲取按鈕 ? ? const btn1 = document.getElementById('btn1') ? ? // 發(fā)送GET請求不攜帶參數(shù) ? ? btn1.onclick = ()=>{ ? ? const result = axios({ ? ? ? ? url:'http://localhost: 5000/persons', // 請求地址 ? ? ? ? method: 'GET', // 請求方式 ? ? ? ? result.then( ? ? ? ? response => {console.log('請求成功了', response.data);}, ? ? ? ? error => {console.log('請求失敗了', error);} ? ? ? ? ) ? ? } ? ? </script> </body> 復制代碼
四、fetch(Ajax請求)
復制代碼
http狀態(tài)碼
分類
1xx:服務器已經(jīng)收到了本次請求,但是還需要進一步的處理才可以
2xx:服務器已經(jīng)收到了本次請求,且已經(jīng)分析、處理等,最終處理完畢
3xx:服務器已經(jīng)接收到了請求,還需要其他的資源,或者重定向到其他位置,甚至交給其他服務器處理
4xx:一般指請求的參數(shù)或者地址有錯誤,出現(xiàn)了服務器無法理解的請求(一般是前端的問題)
5xx:服務器內部錯誤(不是因為請求地址或者請求參數(shù)不當造成的),無法響應用戶請求(一般是后端人員的問題)
常見的狀態(tài)碼
200:成功
301:重定向,被請求的舊資源永久移除了(不可以訪問了),將會跳轉到一個新資源,搜索引擎在抓取新內容的同時也將舊的網(wǎng)址重定向為新網(wǎng)址
302:重定向,被請求的舊資源還在(仍然可以訪問),但會臨時跳轉到一個新資源,搜索引擎會抓取新的內容而保留舊網(wǎng)址
304:請求資源重定向到緩存中(命中了協(xié)商緩存)
404:資源未找到,一般是客戶端請求了不存在的資源
500:服務器收到了請求,但是服務器內部產(chǎn)生了錯誤
502:連接服務器失敗(服務器在處理一個請求的時候,或許需要其他的服務器配合,但是聯(lián)系不上其他的服務器了
API分類
1.REST API(restful風格的API)
①發(fā)送請求進行CRUD哪個操作由請求方式來決定 ②同一個請求路徑可以進行多個操作 ③請求方式會用到GET/POST/PUT/DELETE
const app = express() app.get('/person', (req,res)=>{ ? ? res.send('數(shù)據(jù)接收!') }) app.post('/person',(req,res)=>{ ? ? res.send('添加數(shù)據(jù)') }) app.put('/person',(req,res)=>{ ? ? res.send('修改數(shù)據(jù)') }) app.delete('/person',(req,res)=>{ ? ? res.send('刪除數(shù)據(jù)') }) app.listen(8090, (error)=>{ ? ? if(!error) console.log('服務器開啟'); ? ? else console.log(err); }) 復制代碼
2.非REST API (restless風格的API)
①請求方式不決定請求的CRUD操作 ②一個請求路徑只對應一個操作 ③一般只有GET/POST
const app = express() app.get('/get_person',(req,res)=>{ ? ? res.send('數(shù)據(jù)接收!') }) app.post('/add_person',(req,res)=>{ ? ? res.send('添加數(shù)據(jù)') }) app.post('/update_person',(req,res)=>{ ? ? res.send('修改數(shù)據(jù)') }) app.post('/delete_person',(req,res)=>{ ? ? res.send('刪除數(shù)據(jù)') }) app.listen(8090, (error)=>{ ? ? if(!error) console.log('服務器開啟'); ? ? else console.log(err); }) 復制代碼
HTTP1.1請求方式與請求參數(shù)
請求方式
GET(簡單請求 索取): 從服務器端讀取數(shù)據(jù)→查(R)
POST(簡單請求 提交): 向服務器端添加新數(shù)據(jù)→增(C)
PUT(復雜請求 修改): 更新服務器端已存在的數(shù)據(jù)→改(U)
DELETE(復雜請求 刪除): 刪除服務器端數(shù)據(jù)→刪(D)
HEAD 類似于get請求,只不過返回的響應中沒有具體的內容,用于獲取報頭
CONNECT HTTP/1.1協(xié)議中預留給能夠將連接改為管道方式的代理服務器
OPTIONS(復雜請求put和delete發(fā)送前攜帶的嗅探請求)允許客戶端查看服務器的性能
TRACE 回顯服務器收到的請求,主要用于測試或診斷
請求參數(shù)
query 參數(shù)(查詢字符串參數(shù))
參數(shù)包含在請求地址中,格式為: http://localhost:0000?name=tom&age=18
敏感數(shù)據(jù)不要用query參數(shù),因為參數(shù)是地址的一部分,比較危險
query參數(shù)又稱“查詢字符串參數(shù)”,編碼方式為unlencoded
params 參數(shù)
參數(shù)包含在請求地址中,格式為: http://localhost:0000/add_person/tom/18
敏感數(shù)據(jù)不要用params參數(shù),因為參數(shù)是地址的一部分,比較危險
請求體參數(shù)
格式一: urlencoded格式?? 例如: hame=tom&age=18?? 對應請求頭Content-Type: application/x-www-form-urlencoded
格式二: json格式?? 例如: {"name:"tom", "age": 12)?? 對應請求頭: Content-Type: application/json 特別注意: GET請求不能攜帶請求體參數(shù),因為GET請求沒有請求體
參數(shù)包含在請求體中,可通過瀏覽器開發(fā)工具查看
常用的兩種格式:
格式一: urlencoded格式?? 例如: hame=tom&age=18?? 對應請求頭Content-Type: application/x-www-form-urlencoded
格式二: json格式?? 例如: {"name:"tom", "age": 12)?? 對應請求頭: Content-Type: application/json 特別注意: GET請求不能攜帶請求體參數(shù),因為GET請求沒有請求體