# CORS跨域問題 ## 前端解決版本 ### fetch 使用Fetch API:Fetch API是現代瀏覽器提供的一種用於發送網絡請求的接口。它内置了對CORS的支持。 限制:如果目標資源沒有設置CORS標頭,會導致瀏覽器阻止跨域請求的情況發生。 #### 未使用令牌 ```javascript= async function opendata() { try { const response = await fetch('https://od.moi.gov.tw/api/v1/rest/datastore/A01010000C-002150-013'); const data = await response.json(); console.log(data); } catch (error) { console.log(error); } } ``` #### 使用令牌 ```javascript async function fetchData() { try { // your_token_here:請設置為實際令牌值 const token = 'your_token_here'; const response = await fetch('https://api.example.com/api-endpoint', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); const data = await response.json(); console.log(data); // 處理 API 回傳的資料 } catch (error) { console.error(error); // 處理錯誤 } } fetchData(); ``` #### 使用表單 ##### 情境:在新專案中串接玉山金流時遇到了CORS,使用fetch無法解決後改用表單方式傳送可正常傳送 ###### HTML ```htmlembedded= <template> <div> <button @click="CreditCardPayment">付款</button> <!-- 建立一個隱藏表單 --> <form id="order" style="display: none;" method="post" :action="orderAction"> <input name="data" type="hidden" required> <input name="mac" type="hidden" required> <input name="ksn" type="hidden" required> </form> </div> </template> ``` ###### JavaScript ```htmlembedded= <script setup> // 玉山服務網址(規格書內有) const orderAction = ref("玉山服務網址") const payApiUrl = ref("將資料帶給後端取得data、mac、ksn的apiUrl") const CreditCardPayment = () =>{ let formData = new FormData(); formData.append('callback_url', 付款完返回的網址) formData.append('amount', 付款的金額); // 其他帶給後端的付款資訊 } try { // 將付款資訊post給後端取得 data、mac、ksn const response = await axios.post(payApiUrl.value, formData) if(response.status){ response => response.data.json() try { document.querySelector('input[name=data]').value = response回來的data document.querySelector('input[name=mac]').value = response回來的mac document.querySelector('input[name=ksn]').value = response回來的ksn document.querySelector('form#order').submit() } catch (error) { console.log("付款失敗:", error); } } else { console.log("付款資訊後端接收失敗") } } catch (error) { console.log("付款失敗:", error); } </script> ``` ## 後端解決版本(PHP) * 前端發請求 ```javascript async function opendata(){ try{ const response = await axios.post(`${API_URL}get_p03_opendata.php`) console.log(response.data) } catch (error){ console.log(error) } } ``` * 後端PHP檔 ### 不使用令牌 ```php= // 跨域連線 // 建立一個 Express application 物件 header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // 目標API的URL $targetUrl = 'https://od.moi.gov.tw/api/v1/rest/datastore/A01010000C-002150-013'; //判斷所使用的瀏覽器切換不同的User-Agen function getUserAgent() { $userAgent = $_SERVER['HTTP_USER_AGENT']; if (strpos($userAgent, 'Chrome') !== false) { // Chrome 瀏覽器 return 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36'; } else if (strpos($userAgent, 'Firefox') !== false) { // Firefox 瀏覽器 return 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0'; } else if (strpos($userAgent, 'Safari') !== false) { // Safari 瀏覽器 return 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15'; } else if (strpos($userAgent, 'Edge') !== false) { // Microsoft Edge 浏览器 return 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36 Edge/99.0.999.999'; } else { // 默認情況下使用一個常見瀏覽器的 User-Agent return 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36'; } } // 使用獲取的 User-Agent 發送請求 $options = [ 'http' => [ 'method' => 'GET', 'header' => getUserAgent(), ], ]; //發送請求 $context = stream_context_create($options); $response = file_get_contents($targetUrl, false, $context); // 將得到的api回傳給前端 echo $response; ``` :::spoiler {state="open"} 註解 * `$_SERVER['HTTP_USER_AGENT']`:獲取瀏覽器相關參數 * `strpos()`:用於在字串中查找字符,併法回第一個匹配的位置。 * `User-Agent`:告訴服務器發送請求的客戶端的類型和版本信息。 > 常見的User-Agent: > - Chrome: **`'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36'`** > - Firefox: **`'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0'`** > - Safari: **`'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15'`** > - Edge: **`'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.9999.999 Safari/537.36 Edge/99.0.999.999'`** * `stream_context_create()`:用於file_get_contents()、fopen()等函數傳遞額外的選項和參數 * `file_get_contents()`:函數在請求時會應用上下文資源中定義的選項和參數。這允許自定義請求,例如請求標頭、代理、超時時間等。 * 補充:。請求標頭是 HTTP 請求中包含的元信息,它包含了用於描述請求的各種屬性和參數,通常指的是 HTTP 請求中的標頭字段,用於描述請求的相關訊息,例如 User-Agent、Content-Type、Authorization 等。 ::: ### 需要令牌 限制:如果目標資源有設定令牌,需要在PHP檔案中添加令牌訊息。 ```php= // 跨域連線 // 建立一個 Express application 物件 header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: GET, POST"); header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"); // 目標API的URL $targetUrl = 'https://od.moi.gov.tw/api/v1/rest/datastore/A01010000C-002150-013'; // 獲得前端請求的方式與數據 $method = $_SERVER['REQUEST_METHOD']; $data = file_get_contents('php://input'); // 建立一個curl $curl = curl_init(); //設置請求的自訂標頭,令牌訊息也設定在這 $headers = [ //your_token_here:請設置為實際令牌值 'Authorization: Bearer your_token_here', 'Content-Type: application/json', // 其他標頭設置 ]; // 設置curl選項 curl_setopt($curl, CURLOPT_URL, $targetUrl); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_MAXREDIRS, 5); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); // 根据前端發縙的請求方法設置cURL選項 if ($method === 'POST') { curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } // 執行cURL $response = curl_exec($curl); // 檢查是否有發生錯誤 if ($response === false) { $error = curl_error($curl); echo 'cURL Error: ' . $error; } // 關閉cURL curl_close($curl); // 將得到的api回傳給前端 echo $response; ``` :::spoiler {state="open"} 註解 - **`curl_setopt()`:** 函數是用於設置 cURL 請求的選項。 - **`CURLOPT_URL`:** cURL 函數提供的一個選項常數,用於指定要請求的 URL。 - **`CURLOPT_RETURNTRANSFER`:** 是一個 cURL 選項常數,它指示 cURL 函數將請求的結果作為字串返回,而不是直接輸出到標準輸出流。這樣可以方便在程式中捕獲和處理請求的結果。 - 備註:通過將第三個參數設置為 true,表示開啟 CURLOPT_RETURNTRANSFER 選項。這意味著 cURL 函數在發送請求後,將返回請求的結果作為字串。 - **`CURLOPT_HEADER`:** 是一個 cURL 選項常數,它指示 cURL 函數在請求中是否包含標頭信息。 - 通過將第三個參數設置為 **`false`**,表示關閉 **`CURLOPT_HEADER`** 選項,即在請求中不包含標頭信息。 - **`CURLOPT_FOLLOWLOCATION`:**:用於設置 cURL 請求是否要自動跟隨重定向。 - 備註:當設置為 **`true`** 時,cURL 將自動跟隨伺服器返回的重定向,從而完成整個請求過程。如果目標 URL 返回重定向響應(例如 301 或 302 狀態碼),cURL 將自動重新發送請求到新的位置。 - 注意事項:為了避免無限重定向的情況,cURL 限制了最大重定向次數。如果重定向次數超過限制,則請求將被終止並返回錯誤 - **`CURLOPT_MAXREDIRS`:** 設置最大重定向次數,如果超過這個數量,請求將被終止並返回錯誤。你可以根據需要調整最大重定向次數。 - **`CURLOPT_HTTPHEADER`:** 是用於設置 cURL 請求的自訂標頭(HTTP headers)。 - **`curl_exec($curl)`:** 函數用於獲取請求結果,該函數會返回一個包含請求結果的字串。 ::: ###### 持續更新中! ###### 以上為自學中的筆記如有錯誤,懇請留言指正,感謝!