yalin0112
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 面試系列文 - Ajax / 同源問題及解法 ## 前言 目前大多是動態網站,透過互動的方式,讓使用者的體驗更好。而隨著動態網站興起,開始會將前後端分開,讓前後端各自著墨自己的領域。 此次要說明前端和後端交換資料(API),Ajax的興起,隨之造成的同源問題,以及該如何解決同源。 ## 目標 希望在閱讀本篇後,理解 * API * Ajax * 一點 Same-Origin Policy * 一點 CORS * JSONP * Fetch() ## API ### 定義 全名Application Programming Interface,中文翻作應用程式**介面**。 介面就是用來串接用的,而 API 就是在一套規範下,**程式跟程式之間的串接**。 例如 : Facebook 登入,利用Facebook 提供 的 API,等於說是 Facebook 向外提供給大家的一套介面、一套標準,任何想要接入 Facebook 服務的開發者們,都可以遵循著那套規範拿到自己想要的資料,這個東西就叫做 API。 ### 怎麼串呢? API 的串接,一定要有文件你才知道怎麼串,不然根本串不起來,因為你連要傳什麼參數都不知道。 就如 : [Facebook 登入](https://developers.facebook.com/?locale=zh_TW),你必須先看他的官方**標準**(通常都有範例)。在標準下做自己程式與Facebook程式串接,這裡應該說Facebook資料和自己資料庫串接,在標準下去客製化設定。 ## 怎麼利用程式去發送 Request ? > Client 會發 request 給 server,server 會 response 給 Client 。 知道這套標準,知道怎麼拿資料後,該怎麼利用自己撰寫的程式(JS)去發送 Reuqest ? 1. 傳統JS串法 - XMLHttpRequest 物件 3. 使用 request library 4. Ajax ### 使用 request library npm 完 request 這套 [library](https://github.com/request/request),直接以例子帶入, ``` const request = require('request'); request('http://www.google.com', function (error, response, body) { console.error('error:', error); // Print the error if one occurred console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received console.log('body:', body); // Print the HTML for the Google homepage. }); ``` 1. 宣告一個變數來引入 request 之後 1. request 的第一個參數是要串接 API 的 url,第二個參數是箭頭函式,可以取得 response, error, statusCode 和 body 等資料都放在這裡。 2. 可以用Promise、async/await方式達到非同步 ## Ajax ### 定義 全名 Asynchronous JavaScript And XML,中文 非同步的JavaScript與XML技術。 以前兩種方式來說,每次傳送資料之後都要換頁面。但很多時候,只是需要從 server 拿資料而已,就只需要畫面**部分有改變**而已,不需要整個畫面都變動。這個時候我們可以利用 JavaScript 發一個 request 到 server,然後得到我們要的資料,就只要改變部分畫面就好了。 ### 如何實作? **非同步**,當JS一行行向下執行時,執行完之後就不管它,繼續執行下一行。所以非同步的 Function 不能直接透過 return 把結果傳回來,因為發送 Request 之後就會執行到下一行,這個時候可能根本就還沒有 Response。所以必須透過Callback Function,回呼函式。 ``` // 假設有個發送 Request 的函式叫做 sendRequest var result = sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx'); // 上面 Request 發送完之後就執行到這一行,所以 result 不會有東西 // 因為 Response 根本沒有回來 console.log(result); ``` ``` // 假設有個發送 Request 的函式叫做 sendRequest sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx', callMe); function callMe (response) { console.log(response); } // 或者寫成匿名函式 sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx', function (response) { console.log(response); }); ``` ## 串接後。災難開始 自己處理前後端都還好,但...串接別的 API ,出現 error!!!!!! ``` XMLHttpRequest cannot load http://odata.tn.edu.tw/ebookapi/api/getOdataJH/?level=all. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. ``` 從 error 關鍵字得知,No 'Access-Control-Allow-Origin'。 ## Same Origin Policy 同源政策 基於網絡安全的考量,避免有駭客惡意呼叫其他人的網絡服務。若沒有這個政策保護,別人就可以任意修改和存取你網頁裏的資源。 意思就是說如果你現在這個網站的跟你要呼叫的 API 的網站「不同源」的時候,瀏覽器一樣會幫你發 Request,但是會把 Response 給擋下來,不讓你的 JavaScript 拿到並且傳回錯誤。 #### 如何直接判斷同源?? 判斷是否同源,就看這兩個網址在以下的部分是否相同: * scheme (通訊協定,比如:http, https是不一樣的!) * domain * port (埠號,如有指定) ![](https://i.imgur.com/jd35Hrt.png) MDN的例子: ![](https://i.imgur.com/ITjlv5X.png) 簡單來說,不同 domain 就是不同源,http和 https 就是不同源,port 不同就是不同源。當我們接別人的 API 時,多數就是不同源的情況。 要注意一點:**我的請求(request)的確是有發出去,我的瀏覽器之後也收到回應(response)**。但因瀏覽器的同源政策,它把回應擋下來了,不會把拿到的回應給JavaScript去做另一些的處理。 ### 同源政策並非完全禁止跨來源存取 但在某些情況下,即使兩個網站是「不同源」,也可以允許存取的。例如以下情況: * 跨來源寫入(Cross-origin writes) 例如允許:表單送出(form)、連結(link)、重新導向(redirect) * 跨來源嵌入(Cross-origin embedding) 例如允許:嵌入圖片`<img>`、影片`<video>`、`<iframe>`、放在`<script>`裏的程式碼、CSS stylesheet `<link rel="stylesheet" href="...">`等等。然而,雖然我的**網頁可以顯示到這些資源,但我的JavaScript並不能讀取這些資源的內容**。 ## 解決方案 ### CORS 全名 Cross-Origin-Resource Sharing,中文 : 跨來源資源共用。 如果你想開啟跨來源 HTTP 請求的話,Server 必須在 Response 的 Header 裡面加上Access-Control-Allow-Origin。 瀏覽器收到 Response 之後,會先檢查Access-Control-Allow-Origin裡面的內容,如果裡面有包含現在這個發起 Request 的 Origin 的話,就會允許通過,讓程式順利接收到 Response。 ``` Content-Type: application/json Content-Length: 71 Connection: keep-alive Server: nginx Access-Control-Allow-Origin: * //任何一個 Origin 都接受 Cache-Control: no-cache, no-store, must-revalidate, private Expires: 0 Pragma: no-cache Twitch-Trace-Id: e316ddcf2fa38a659fa95af9012c9358 X-Ctxlog-Logid: 1-5920052c-446a91950e3abed21a360bd5 Timing-Allow-Origin: https://www.twitch.tv ``` * Simple request 簡單請求 沒有自訂義、Get,直接發出請求 * Preflight Request 預檢請求 自定義(帶有一些關於想發的請求的一些資訊,如:POST、`Authorization`)、client-id=...,發出兩個請求, 發出請求前的一個「預檢請求」,這個預檢請求是負責查問伺服器,問它是否批准我們發出請求給它,批准後才發出該請求。 ### 舉例說明 假設今天某個 Server 提供了一個 API 網址叫做:`https://example.com/data/16`,你只要對它發送 `GET`,就能夠拿到 id 是 16 的資料,只要對它發送 `DELETE`,就可以把這筆資料刪除。 如果今天沒有 Preflight Request 這個機制的話,就可以在隨便一個 Domain 的網頁上面發送一個 DELETE 的 Request 給這個 API。而瀏覽器的 CORS 機制,會幫你發送 Request,但是 Response 會被瀏覽器擋住。 因此呢,儘管沒有 Response,但是 Server 端的確收到了這個 Request,因此就會把這筆資料給**刪除**。 如果有 Preflight Request 的話,在發送出去收到結果的時候,就會知道這個 API 並沒有提供 CORS,因此真的 DELETE 請求就不會送出,到這邊就結束了。 意思就是先用一個 OPTIONS 的請求去確認之後的 Request 能不能送出,這就是 Preflight Request 的目的。 ### JSONP 全名 JSON with Padding,是資料格式JSON的一種“使用模式”。 ### JSONP 是什麼? JSONP的做法就是,在一個 `<script>` tag 裏的放入伺服器端提供的網址,之後在另一個`<script>` tag 裏宣告一個函式,函式名字是由伺服器端提供,也可以在伺服器端所提供的網址裏找到,例如它提供了 `https://...callback=abc` 這個網址,那麼該函式的名字就是 `abc` 。 ``` <script> function randomuserdata(response){ console.log(response); } </script> <script src="https://randomuser.me/api/?gender=female&nat=us&callback=randomuserdata"></script> ``` JSONP 利用 `<script>` 裡面放資料,透過指定好的 Callback Function 把資料給帶回去。 把第一段的 `<script>` 那邊想成是 Server 的回傳值,Server 通常會提供一個 callback 的參數讓 client 端帶過去,把 JavaScript 物件整個傳到 Function 裡面,你就可以在 Function 裡面拿到資料。。 ``` <script> receiveData({ data: 'test' }); </script> <script> function receiveData (response) { console.log(response); } </script> ``` 不過 JSONP 只適用於 GET 請求(要帶的那些參數永遠都只能用附加在網址上的方式 GET 帶過去),無法做到 POST,所以首選還是上面提及的 CORS 的方法。 ### Fetch API The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set. 類似於 jQuery $.ajax ,不過兩者亦有不同概念之處。 * fetch 會使用 ES6 的 Promise 作回應 * then 作為下一步 * catch 作為錯誤回應 (404, 500…) ``` fetch('https://randomuser.me/api/', {}) .then((response) => { // 這裡會得到一個 ReadableStream 的物件 console.log(response); // 可以透過 blob(), json(), text() 轉成可用的資訊 return response.json(); }).then((jsonData) => { console.log(jsonData); }).catch((err) => { console.log('錯誤:', err); }); ``` * ReadableStream Fetch API 的 Response 物件中的 body 屬性提供了一個 ReadableStream 的實體,這個階段我們無法直接讀取資料內容,而 ReadableStream 物件中可用以下對應的方法來取得資料,如 : arrayBuffer()、blob()、formData()、json()、text() ## 參考資料 * [輕鬆理解 Ajax 與跨來源請求](https://blog.huli.tw/2017/08/27/ajax-and-cors/) * [JavaScript基本功修練:Day28 - Fetch練習(GET和POST請求)](https://ithelp.ithome.com.tw/articles/10252941) ## 後記 以往在學習技能時,習慣不斷輸入,而忽視了輸出,導致對於某項技術明明知道,卻又無法清楚地和別人介紹,在面試期間深深感到無力,決心好好消化之前的技術筆記,學習重新輸出。 主要針對相關性技術或是名詞作介紹,偶爾可能會看到之前的專案心得,歡迎一起討論學習,雙手合十,感恩。 ###### tags: `面試大哉問`、`JavaScript` 、 `CORS` 、`WEB`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully