1. 程式人生 > >node.js——http和服務、服務代理、後臺跨域

node.js——http和服務、服務代理、後臺跨域

http

  • HTTP協議(HyperText Transfer Protocol,超文字傳輸協議)是用於從WWW伺服器傳輸超文字到本地瀏覽器的傳輸協議。它可以使瀏覽器更加高效,使網路傳輸減少。它不僅保證計算機正確快速地傳輸超文字文件,還確定傳輸文件中的哪一部分,以及哪部分內容首先顯示(如文字先於圖形)等。

http請求頭

  • 四中協議基礎: 通用頭域、請求訊息、響應訊息、實體資訊;

就是訪問每個網頁的時候,資源和資料請求的這些各種’頭’ 在這裡插入圖片描述

請求頭包含基本內容

  • Accept:瀏覽器可接受的MIME型別(文字、聲音、影象等)。
  • Accept-Charset:瀏覽器可接受的字符集(utf-8,ASCII,GB2312等)。
  • Accept-Encoding:瀏覽器能夠進行解碼的資料編碼方式,比如gzip。Servlet+ `能夠向支援gzip的瀏覽器返回經gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時間。
  • Accept-Language:瀏覽器所希望的語言種類,當伺服器能夠提供一種以上的語言版本時要用到。
  • Authorization:授權資訊,通常出現在對伺服器傳送的WWW-Authenticate頭的應答中。
  • Connection:表示是否需要持久連線。如果Servlet看到這裡的值為“Keep-Alive”,或者看到請求使用的是HTTP 1.1(HTTP 1.1預設進行持久連線),它就可以利用持久連線的優點,當頁面包含多個元素時(例如Applet,圖片),顯著地減少下載所需要的時間。要實現這一點,Servlet需要在應答中傳送一個Content-Length頭,最簡單的實現方法是:先把內容寫入ByteArrayOutputStream,然後在正式寫出內容之前計算它的大小。
  • Content-Length:表示請求訊息正文的長度。
  • Cookie:這是最重要的請求頭資訊之一
  • From:請求傳送者的email地址,由一些特殊的Web客戶程式使用,瀏覽器不會用到它。
  • Host:初始URL中的主機和埠。
  • If-Modified-Since:只有當所請求的內容在指定的日期之後又經過修改才返回它,否則返回304“Not Modified”應答。
  • Pragma:指定“no-cache”值表示伺服器必須返回一個重新整理後的文件,即使它是代理伺服器而且已經有了頁面的本地拷貝。
  • Referer:包含一個URL,使用者從該URL代表的頁面出發訪問當前請求的頁面。
  • User-Agent:瀏覽器型別,如果Servlet返回的內容與瀏覽器型別有關則該值非常有用。
  • UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE瀏覽器所傳送的非標準的請求頭,表示螢幕大小、顏色深度、作業系統和CPU型別。

http狀態碼

在這裡插入圖片描述

詳細請看百度百科

nodejs建立服務

建立監聽

const http = require("http");

const server = http.createServer((req,res) => {
    res.write("向客戶端返回資料")
    res.end()//主動終端會話,不然瀏覽器會一直載入,圈圈一直轉
})//require reponse

server.listen(3001)//監聽埠

由於沒有寫請求頭,得到的資料是亂碼: 在這裡插入圖片描述

請求頭

const http = require("http");

const server = http.createServer((req,res) => {
    res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
    })//設定成功狀態碼,設定請求頭傳遞的資料編碼
    res.write("向客戶端返回資料")
    res.end()//end為可選引數,傳遞buffer物件或者字串,會作為結束標誌拼接在write的最後,而且end代表上下文結束,後面的程式碼將沒有意義。
})//require reponse

server.listen(3001)
console.log('開啟了服務');

在這裡插入圖片描述

至關重要的請求訊息

  • 我們先看一下一次請求向後臺傳送了寫什麼訊息:

這一長串滾動條都是請求傳送給後臺的訊息。 在這裡插入圖片描述

  • 我們將最重要的一部分打印出來看看:
const http = require("http");

const server = http.createServer((req,res) => {
    res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
    })
    for (const index in req.headers) {
        res.write("\n")
        res.write(index+":"+req.headers[index])
    }
    res.end()
})//require reponse

server.listen(3001)
console.log('開啟了服務');

在這裡插入圖片描述

拿到路由資訊

const http = require("http");

const server =http.createServer((req,res) => {
    res.writeHead(200,{
        "Content-Type":"text/plain;charset=utf-8"
    })
    res.end(req.url)
})//require reponse

server.listen(3001)
console.log('開啟了服務');

如果我們再通過邏輯程式碼,通過不同的路由返回不同的資訊!是不是突然有種通透的感覺,一個網站不就是這樣嗎?不然你的fs是學來幹嘛的嘛,讀取檔案嘛,返回utf8字串嘛,所以說,nodejs的後臺非常的輕、簡單!

在這裡插入圖片描述

不過這裡要注意的是,在設定請求頭的時候,如果Content-Type如果依然設定為text/plain,那麼返回的字元只會被當做普通txt文字解析,如果改成html就會被解析成html程式碼了!

const http = require("http");

const server =http.createServer((req,res) => {
    res.writeHead(200,{
        "Content-Type":"text/html;charset=utf-8"
    })
    res.end("<h1>hello word</h1>")
})//require reponse

server.listen(3001)
console.log('開啟了服務');

在這裡插入圖片描述

如果是ajax請求呢?

  • 我們先隨便寫個html程式碼:
<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8' />
        <meta http-equiv='X-UA-Compatible' content='IE = edge'>
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- <link rel="stylesheet" type="text/css" media="screen" href="css/style.css" /> -->
</head>
<body>
     <h1 id="app">Hello word</h1>
</body>
<script src="ajax.js"></script>
<script type="text/javascript">
    app.onclick = ()=>{
        ajax({
            url:"http://localhost:3001",
            method:"get",
            data:{
                name:"Lucy",
                age:"19"
            },
            sucess:function(result) {
                console.log(result);
            }
            
        })
    }

</script>
</html>
  • 我這裡是自己以前封裝的ajax,用jQuery的也是一樣的,如果你想用我的:
/*
ajax 方法
options 配置資訊
*/
function ajax(options) {
    options.type = /post/i.test(options.type) ? 'POST' : 'GET';
    //console.log( options.type);
    //非同步或者同步
    options.async = options.async === false ? false : true;
    /*console.log(options.async)*/
    var xhr = new XMLHttpRequest(); 
    var data = ''; //user=韓梅梅&age=18
    for (var k in options.data) {
        data += k + '=' + encodeURIComponent(options.data[k]) + '&';
    }
    //是get還是post
    if (options.type === 'GET') {
        if (!/\?/.test(options.url)) {
            options.url += '?';
        } else {
            if (!/(&\s*)$/.test(options.url)) {
                options.url += '&';
            }
        }
        options.url += data + '_=' + new Date().getTime();
        data = null;
    }
    xhr.open(options.type, options.url, options.async);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
    xhr.onreadystatechange = function () {
        if (this.readyState === 4) { //0 1 2 3 4
            if (this.status > 199 && this.status < 300 || this.status === 304) {
                options.success && options.success.call(this, strJsonCode(this.response));
            } else {
                options.error && options.error.call(this, this.status);
            }
        }
    };
    xhr.send(data);
}

//將字串打包成json資料,打包失敗,預設返回原字串
function strJsonCode(str) {
    try {
        return JSON.parse(str);
    } catch (e) { 

        return str;
    }
}
//時間打包函式	
function formatterDateTime() {
    var date = new Date()
    var month = date.getMonth() + 1
    var datetime = date.getFullYear() +
        "" // "年"
        +
        (month >= 10 ? month : "0" + month) +
        "" // "月"
        +
        (date.getDate() < 10 ? "0" + date.getDate() : date
            .getDate()) +
        "" +
        (date.getHours() < 10 ? "0" + date.getHours() : date
            .getHours()) +
        "" +
        (date.getMinutes() < 10 ? "0" + date.getMinutes() : date
            .getMinutes()) +
        "" +
        (date.getSeconds() < 10 ? "0" + date.getSeconds() : date
            .getSeconds());
    return datetime;
}

  • 當前服務端程式碼;
const http = require("http");

const server =http.createServer((req,res) => {
    res.writeHead(200,{
        "Content-Type":"text/html;charset=utf-8"
    })
    const obj = {
        name:"Bob",
        age:"18"
    }
    console.log(1);
    
    res.end()
})//require reponse

server.listen(3001)
console.log('開啟了服務');

本地開啟頁面,觸發ajax,這裡出現了跨域問題

在這裡插入圖片描述 這裡的1打印出來了,說明服務端準確地接收到了請求並執行了裡面的程式碼,只是有跨域無法獲取到伺服器的返回資料:

在這裡插入圖片描述

跨域並不是因為伺服器或者程式碼的問題,它是因為瀏覽器在中間作祟,瀏覽器"不同意這門婚事"! 但是,出於安全的考慮,瀏覽器是預設將沒有設定請求頭被返回訊息的物件時,預設保護服務方。

  • 設定請求頭的跨域資訊:
res.writeHead(200,{
    "Content-Type":"text/html;charset=utf-8",
    "Access-Control-Allow-Origin": "*"
})

在這裡插入圖片描述

我點選後不再出現跨域問題,而且後臺也顯示了我觸發的次數: 在這裡插入圖片描述

//沒錯,返回資料就是靠write
res.write(JSON.stringify(obj))

在這裡插入圖片描述

請求代理

  • 因為後臺沒有設定跨域問題,那麼預設情況下,或者允許情況下,我可以讓遠在上海的伺服器訪問北京的伺服器,上海伺服器是個代理,上海伺服器被黑了,但是因為北京伺服器本身地址和埠以及跨域問題,使用者沒有辦法直接獲取到,所以北京伺服器是安全的!
  • 增加一個server.js作為遠在上海的伺服器: 我把原來的3001埠的服務修改一下:
const http = require("http");

const server =http.createServer((req,res) => {
    const obj = {
        name:"Bob",
        age:"18"
    }
    console.log(1);
    res.write(JSON.stringify(obj))
    res.end()
})//require reponse

server.listen(3001)
console.log('開啟了服務');

const http = require("http");
const option = {
    host: "localhost",
    port: 3001,
    method: "get",
    path: "/"
}
let httpServer = (response)=>{
	//request 是作為nodejs後臺用於請求的方法
    const myreq = http.request(option, res => {
        let obj = ""; //外部用於儲存從3002埠請求的資料
        res.setEncoding("utf8");
        res.on("data", chunk => {
            console.log(chunk);
            obj = chunk
        })
        res.on("end", () => {
            response.end(JSON.stringify(obj)) //將請求的訊息傳送到傳遞進來的外部請求物件
        })
    })
    myreq.on("error", err => {
        console.log(err);
    })
    myreq.write("")
    myreq.end()
}

const server = http.createServer((req, res) => {
    res.writeHead(200, {
        "Content-Type": "text/html;charset=utf-8",
        "Access-Control-Allow-Origin": "*"
    })
    httpServer(res);//呼叫請求函式,將本次外部請求的物件傳遞過去
}) //require reponse
server.listen(3002) //建立新的服務3002埠
console.log('開啟了服務');

在這裡插入圖片描述

一個視窗只能開一個服務: 在這裡插入圖片描述

完美實現伺服器代理,保護了源伺服器!