最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js?搭建后端服務(wù)器內(nèi)置模塊(?http+url+querystring?的使用)

 更新時(shí)間:2022年09月08日 16:20:33   作者:海底燒烤店ai  
這篇文章主要介紹了Node.js搭建后端服務(wù)器內(nèi)置模塊(http+url+querystring的使用),文章圍繞主題展開詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的朋友可以參考一下

 前言

這一節(jié)我們?nèi)W(xué)習(xí)NodeJs的內(nèi)置模塊:http、urlquerystring ,并使用它們來搭建我們的node后端服務(wù)器,正式邁入后端開發(fā)!

一、創(chuàng)建服務(wù)器

httpnode的內(nèi)置模塊,我們可以直接引入它進(jìn)行使用,http這個(gè)模塊內(nèi)有一個(gè)createServer方法,這個(gè)方法可以幫助我們快速創(chuàng)建一個(gè)服務(wù)器:

// 引入http模塊
const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
  // req:接受瀏覽器傳的參數(shù)
  // res:返回渲染的內(nèi)容
}).listen(3000, () => { // 服務(wù)器端口號(hào)為3000
    console.log("服務(wù)器啟動(dòng)啦!");
});

另一種寫法(推薦寫法):

const http = require("http");

// 創(chuàng)建服務(wù)器
const server = http.createServer();

server.on("request", (req, res) => {
    // req:接受瀏覽器傳的參數(shù)
    // res:返回渲染的內(nèi)容
});

server.listen(3000,() => { // 服務(wù)器端口號(hào)為3000
    console.log("服務(wù)器啟動(dòng)啦!");
});

createServer方法返回一個(gè)服務(wù)器對(duì)象,對(duì)象內(nèi)有一個(gè)listen方法,可以設(shè)置服務(wù)器啟動(dòng)的端口號(hào)和啟動(dòng)成功的回調(diào)內(nèi)容

通過nodemon運(yùn)行這個(gè)文件(nodemon可以在我們修改文件后幫助我們自動(dòng)重啟服務(wù)器),控制臺(tái)打印出了我們?cè)?code>listen中設(shè)置的回調(diào)內(nèi)容,說明服務(wù)器啟動(dòng)成功了

我們直接在瀏覽器訪問http://localhost:3000/時(shí)會(huì)發(fā)現(xiàn)瀏覽器一直在轉(zhuǎn)圈圈加載

注意: 直接在瀏覽器地址欄里訪問服務(wù)器接口,相當(dāng)于是使用get請(qǐng)求訪問服務(wù)器接口(并且沒有跨域限制)

這是因?yàn)槲覀儾]有在我們創(chuàng)建的node服務(wù)器中返回任何內(nèi)容

二、返回響應(yīng)數(shù)據(jù)

我們可以通過我們定義的resresponse對(duì)象)參數(shù)向客戶端發(fā)送響應(yīng)內(nèi)容:

const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
    // req:接受瀏覽器傳的參數(shù)
    // res:返回渲染的內(nèi)容

    // 傳遞數(shù)據(jù)
    res.write("hello world");
    res.write("Ailjx");
    res.end();
}).listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

write方法傳遞內(nèi)容,可以調(diào)用多次write傳遞多條內(nèi)容,內(nèi)容必須是字符串格式最后必須調(diào)用end方法告訴請(qǐng)求的調(diào)用者我們響應(yīng)結(jié)束了也可以直接在end方法內(nèi)傳遞內(nèi)容,效果與使用write方法相同,但end方法只能調(diào)用一次

運(yùn)行上述代碼,我們直接在瀏覽器調(diào)用服務(wù)器接口:

如果服務(wù)器中不調(diào)用end方法,瀏覽器會(huì)收不到服務(wù)器傳遞的響應(yīng)結(jié)束的信號(hào),便會(huì)一直加載請(qǐng)求:

返回復(fù)雜對(duì)象數(shù)據(jù)

可以傳遞復(fù)雜對(duì)象數(shù)據(jù),但必須是字符串(JSON)的格式:

const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
    // end方法也可以傳遞內(nèi)容,效果與write相同
    res.end("{name:{me:'Ailjx'}}");
    // 或者res.end(JSON.stringify({ name: { me: "Ailjx" } }));
}).listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

上面瀏覽器顯示的數(shù)據(jù)被格式化了,是因?yàn)槲以跒g覽器中安裝了FeHelper(前端助手)插件,能夠自動(dòng)格式化JSON數(shù)據(jù)

返回html文檔數(shù)據(jù)

const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
    // 傳遞html內(nèi)容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `);
}).listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

這時(shí)發(fā)現(xiàn)我們傳遞的中文亂碼了,我們可以在響應(yīng)頭的Content-Type中指定utf-8的字符集來解析中文,下面會(huì)講到

三、設(shè)置響應(yīng)頭和狀態(tài)碼

我們可以使用response對(duì)象的writeHead方法來同時(shí)設(shè)置狀態(tài)碼和響應(yīng)頭信息:

const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
    // 設(shè)置相應(yīng)頭,第一參數(shù)為狀態(tài)碼,第二個(gè)參數(shù)為響應(yīng)頭配置,第二個(gè)參數(shù)可不填
    res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
    // 傳遞html內(nèi)容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `); // 直接在end中傳遞,效果與write方法相同
}).listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

我們也可以使用setHeader單獨(dú)設(shè)置響應(yīng)頭信息,statusCode單獨(dú)設(shè)置狀態(tài)碼:

const http = require("http");

// 創(chuàng)建服務(wù)器
http.createServer((req, res) => {
    // 設(shè)置相應(yīng)頭信息
    res.setHeader("Content-Type", "text/html;charset=utf-8");
    // 設(shè)置狀態(tài)碼
    res.statusCode = 200;
    // 傳遞html內(nèi)容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `); // 直接在end中傳遞,效果與write方法相同
}).listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

四、實(shí)現(xiàn)路由接口

上面我們已經(jīng)成功創(chuàng)建了一個(gè)服務(wù)器,并能夠使用res參數(shù)中的writeend方法向調(diào)用者發(fā)送內(nèi)容,但發(fā)送的這些內(nèi)容是不會(huì)隨著我們請(qǐng)求的路徑而變化的:

const http = require("http");

// 創(chuàng)建服務(wù)器
const server = http.createServer();

server.on("request", (req, res) => {
    res.end("Ailjx");
});

server.listen(3000);

可以看到,我們請(qǐng)求(訪問)不同路徑,服務(wù)器返回的數(shù)據(jù)都是一樣的,而我們?cè)趯?shí)際開發(fā)中往往是需要不同的路徑有不同的數(shù)據(jù)

這時(shí)我們就可以利用我們創(chuàng)建服務(wù)器時(shí)定義的req參數(shù)(request對(duì)象)來獲取用戶請(qǐng)求的路徑從而判斷需要返回哪些數(shù)據(jù):

const http = require("http");

// 創(chuàng)建服務(wù)器
const server = http.createServer();

server.on("request", (req, res) => {
    // req.url拿到用戶請(qǐng)求的路徑
    console.log(req.url);
    res.end();
});
server.listen(3000);

運(yùn)行代碼,之后在瀏覽器訪問調(diào)用一下http://localhost:3000/list,控制臺(tái)會(huì)打印出:

可以看到我們?cè)L問的/list路徑確實(shí)被打印出來了,但怎么還打印了一個(gè)/favicon.ico呢?

這其實(shí)是瀏覽器在訪問一個(gè)域名時(shí)會(huì)自動(dòng)訪問該域名下的/favicon.ico靜態(tài)文件,來作為網(wǎng)頁標(biāo)簽欄的小圖標(biāo),所以我們的服務(wù)器才會(huì)打印出/favicon.ico

如果是普通的ajax調(diào)用接口是不會(huì)出現(xiàn)這種情況的,這里我們是為了方便,直接使用瀏覽器訪問接口來進(jìn)行演示,所以才出現(xiàn)這種請(qǐng)求,我們可以簡(jiǎn)單做一下處理:

const http = require("http");
// 創(chuàng)建服務(wù)器
const server = http.createServer();
server.on("request", (req, res) => {
    // req.url獲取用戶請(qǐng)求的路徑
    if (req.url === "/favicon.ico") {
    	// 讀取本地圖標(biāo)
        return;
    }
    console.log(req.url);
    res.end("Ailjx");
});
server.listen(3000);

這樣當(dāng)服務(wù)器收到/favicon.ico請(qǐng)求時(shí)就能直接跳過不對(duì)其進(jìn)行處理

創(chuàng)建簡(jiǎn)易路由應(yīng)用

現(xiàn)在,我們開始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的路由應(yīng)用,我們先創(chuàng)建兩個(gè)模塊:

renderContent.js用來根據(jù)用戶請(qǐng)求路徑來返回對(duì)應(yīng)的內(nèi)容:

function renderContent(url) {
    switch (url) {
        case "/api/home":
            return `
                {
                    page:'首頁'
                }
            `;
        case "/api/about":
            return `
                {
                    page:'關(guān)于頁'
                }
                `;

        default:
            return "404";
    }
}
exports.renderContent = renderContent;

renderStatus.js用來根據(jù)用戶請(qǐng)求的路徑來返回對(duì)應(yīng)的響應(yīng)狀態(tài)碼:

function renderStatus(url) {
    const arr = ["/api/home", "/api/about"];
    return arr.includes(url) ? 200 : 404;
}

module.exports = {
    renderStatus,
};

之后在我們的服務(wù)器文件server.js中調(diào)用這兩個(gè)模塊:

const http = require("http");
const { renderContent } = require("./module/renderContent");
const { renderStatus } = require("./module/renderStatus");

// 創(chuàng)建服務(wù)器
const server = http.createServer();

server.on("request", (req, res) => {
    // req.url獲取用戶請(qǐng)求的路徑
    if (req.url === "/favicon.ico") {
        return;
    }
	// 響應(yīng)頭
    res.writeHead(renderStatus(req.url), {
    	 // 標(biāo)志返回JSON數(shù)據(jù) 
        "Content-Type": "application/json",
    });
	// 返回的內(nèi)容
    res.end(renderContent(req.url));
});

server.listen(3000);

之后啟動(dòng)服務(wù)器,在瀏覽器調(diào)用接口查看效果:

五、處理URL

在上面我們通過判斷req.url來實(shí)現(xiàn)了簡(jiǎn)易的路由接口應(yīng)用,但當(dāng)用戶調(diào)用帶有url參數(shù)的接口時(shí),這就會(huì)出現(xiàn)問題:

這是因?yàn)檫@時(shí)req.url/api/about?name=ailj而并不是/api/about,我們可以手動(dòng)的對(duì)這個(gè)字符串進(jìn)行處理來獲得正確的路由路徑,也可以使用node.js的內(nèi)置模塊url來處理

URL格式轉(zhuǎn)換

修改上面的server.js文件:

const http = require("http");
const url = require("url");
const { renderContent } = require("./module/renderContent");
const { renderStatus } = require("./module/renderStatus");
// 創(chuàng)建服務(wù)器
const server = http.createServer();
server.on("request", (req, res) => {
    // req.url獲取用戶請(qǐng)求的路徑
    if (req.url === "/favicon.ico") {
        return;
    }
    // 新版使用全局的URL構(gòu)造函數(shù)
    // 傳兩個(gè)參數(shù)用法
    const myUrl = new URL(req.url, "http://127.0.0.1:3000").pathname;
    // 傳一個(gè)參數(shù)用法
    // const myUrl = new URL("http://127.0.0.1:3000" + req.url).pathname;
    console.log(new URL(req.url, "http://127.0.0.1:3000"));
    res.writeHead(renderStatus(myUrl), {
        "Content-Type": "application/json",
    });

    res.end(renderContent(myUrl));
});

server.listen(3000, () => {
    // 服務(wù)器端口號(hào)為3000
    console.log("服務(wù)器啟動(dòng)啦!");
});

全局的構(gòu)造函數(shù)UR可以將完整的 url地址轉(zhuǎn)換成url對(duì)象(WHATWG URL標(biāo)準(zhǔn)的對(duì)象)

我們可以對(duì)其傳遞兩個(gè)參數(shù),第一個(gè)是用戶請(qǐng)求的路徑(路由),第二個(gè)參數(shù)是地址的根域名(我們這里是本地啟動(dòng)的服務(wù)器,根域名為http://127.0.0.1:3000

也可以直接傳遞一個(gè)參數(shù),該參數(shù)是帶有域名的完整url地址

當(dāng)我們?cè)L問http://localhost:3000/api/about?name=ailjx時(shí)server.js會(huì)打印出:

URL {
  href: 'http://127.0.0.1:3000/api/about?name=ailjx',
  origin: 'http://127.0.0.1:3000',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1:3000',
  hostname: '127.0.0.1',
  port: '3000',
  pathname: '/api/about',
  search: '?name=ailjx',
  searchParams: URLSearchParams { 'name' => 'ailjx' },
  hash: ''
}

上面Url對(duì)象里 searchParamsurl的參數(shù)部分,是一個(gè)迭代器對(duì)象URLSearchParams對(duì)象

 // searchParams對(duì)象是一個(gè)迭代器對(duì)象
 const query = new URL(req.url, "http://127.0.0.1:3000").searchParams;
 // 使用get方法獲取指定的值
 console.log(query.get("name")); // ailjx

我們還可以從組成部分構(gòu)造 URL獲取構(gòu)造的字符串

const myURL = new URL("https://www.baidu.com");
myURL.port = "443";
myURL.pathname = "/ad/index.html";
myURL.search = "?id=8&name=mouse";
myURL.hash = "#tag=110";
// 獲取構(gòu)造的 URL 字符串,請(qǐng)使用href屬性訪問器
console.log(myURL.href); // https://www.baidu.com/ad/index.html?id=8&name=mouse#tag=110

或者:

const pathname = '/a/b/c';
const search = '?d=e';
const hash = '#fgh';
const myURL = new URL(`https://example.org${pathname}${search}${hash}`);
console.log(myURL.href);

使用url.format方法可以自定義序列化url字符串,format方法接收兩個(gè)參數(shù):

  • new URL返回的一個(gè)WHATWG URL格式的對(duì)象
  • 配置對(duì)象:
    • fragment:序列化的網(wǎng)址字符串是否包含片段,默認(rèn)為true
    • auth:序列化的網(wǎng)址字符串是否包含用戶名和密碼,默認(rèn)為true
    • unicode:是否將出現(xiàn)在 URL 字符串的主機(jī)組件中的 Unicode 字符直接編碼而不是Punycode 編碼,默認(rèn)是false
    • search:序列化的網(wǎng)址字符串是否包含搜索查詢(參數(shù)),默認(rèn)為true
const myURL = new URL(
    "https://username:password@URL路徑序列化測(cè)試?name=ailjx#foo"
);

console.log(
    url.format(myURL, {
        fragment: false, // 不顯示片段(#foo)
        unicode: true, // 不轉(zhuǎn)化Unicode字符(中文字符)
        auth: false, // 不包含用戶名和密碼(username:password)
        search: false, // 不顯示參數(shù)(?name=ailjx)
    })
);
// 打印結(jié)果: 'https://url路徑序列化測(cè)試/'

舊版Node使用parse和format處理URL:

注意:舊版的parse方法官方表示已棄用,format方法在新版中使用方式有所更改

const http = require("http");
const url = require("url");
const { renderContent } = require("./module/renderContent");
const { renderStatus } = require("./module/renderStatus");
// 創(chuàng)建服務(wù)器
const server = http.createServer();
server.on("request", (req, res) => {
    // req.url獲取用戶請(qǐng)求的路徑
    if (req.url === "/favicon.ico") {
        return;
    }
    console.log(url.parse(req.url));
    
    const myUrl = url.parse(req.url).pathname;
    res.writeHead(renderStatus(myUrl), {
        "Content-Type": "application/json",
    });

    res.end(renderContent(myUrl));
});
server.listen(3000, () => {
    // 服務(wù)器端口號(hào)為3000
    console.log("服務(wù)器啟動(dòng)啦!");
});

url模塊的parse方法可以將完整的url地址轉(zhuǎn)換成url對(duì)象,如當(dāng)我們?cè)L問http://localhost:3000/api/about?name=ailjx時(shí)server.js會(huì)打印出:

Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?name=ailjx',
  query: 'name=ailjx',
  pathname: '/api/about',
  path: '/api/about?name=ailjx',
  href: '/api/about?name=ailjx'
}

上面Url對(duì)象里 queryurl的參數(shù)部分,默認(rèn)是字符串的格式,可以給parse方法傳遞第二個(gè)參數(shù),將其轉(zhuǎn)換成對(duì)象格式:

url.parse(req.url,true)
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?name=ailjx',
  query: [Object: null prototype] { name: 'ailjx' },
  pathname: '/api/about',
  path: '/api/about?name=ailjx',
  href: '/api/about?name=ailjx'
}

這時(shí)通過url.parse(req.url, true).query.name就可以拿到ailjx這個(gè)參數(shù)

parse方法相反的有一個(gè)format方法,它能將一個(gè) url對(duì)象轉(zhuǎn)換成url地址 :

const url = require("url");

const urlObject = {
    protocol: "https:",
    slashes: true,
    auth: null,
    host: "www.baidu.com:443",
    port: "443",
    hostname: "www.baidu.com",
    hash: "#tag=110",
    search: "?id=8&name=mouse",
    query: { id: "8", name: "mouse" },
    pathname: "/ad/index.html",
    path: "/ad/index.html?id=8&name=mouse",
};

const parsedObj = url.format(urlObject);

console.log(parsedObj); // https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110

URL路徑拼接

URL構(gòu)造函數(shù),傳遞兩個(gè)字符串路徑時(shí)能夠?qū)崿F(xiàn)路徑的拼接:

let myURL = new URL('http://Example.com/', 'https://example.org/');
// http://example.com/

myURL = new URL('https://Example.com/', 'https://example.org/');
// https://example.com/

myURL = new URL('foo://Example.com/', 'https://example.org/');
// foo://Example.com/

myURL = new URL('http:Example.com/', 'https://example.org/');
// http://example.com/

myURL = new URL('https:Example.com/', 'https://example.org/');
// https://example.org/Example.com/

myURL = new URL('foo:Example.com/', 'https://example.org/');
// foo:Example.com/

舊版Node使用resolve方法拼接路徑:

注意:舊版node的resolve方法官方表示已棄用

const url = require('url')

var a = url.resolve("/one/two/three", "four"); // /one/two/four
// var a = url.resolve("/one/two/three/", "four"); // /one/two/three/four
// var a = url.resolve("/one/two/three", "/four"); // /four
// var a = url.resolve("/one/two/three/", "/four"); // /four

var b = url.resolve("http://example.com/", "/one");
// var b = url.resolve("http://example.com/", "one");
// var b = url.resolve("http://example.com", "one");
// var b = url.resolve("http://example.com", "/one");
// 以上b的結(jié)果都是:http://example.com/one

var c = url.resolve("http://example.com/one", "two");
// var c = url.resolve("http://example.com/one", "/two");
// var c = url.resolve("http://example.com/one/", "/two");
// var c = url.resolve("http://example.com/one/a/b", "/two");
// 以上c的結(jié)果都是:http://example.com/two

var d = url.resolve("http://example.com/one/", "two"); // http://example.com/one/two

var e = url.resolve("http://example.com/one/aaa", "http://example.com/one/two");
// var e = url.resolve("/one/aaa", "http://example.com/one/two");
// 以上e的結(jié)果都是:http://example.com/one/two

resolve方法并不是簡(jiǎn)單的將兩個(gè)路徑直接拼接在一起,而是具有它自己的一些拼接規(guī)則:

  • 如果第二個(gè)路徑(接收的第二個(gè)參數(shù))開頭前帶/,則將直接用第二個(gè)路徑替代第一個(gè)路徑的路由部分(不會(huì)替代第一個(gè)路徑的根域名,如上面的最后一個(gè)變量c所示:/two替代了/one/a/b
  • 如果第二個(gè)路徑開頭前不帶/,第一個(gè)路徑結(jié)尾處不帶/,則第二個(gè)路徑將會(huì)替代第一個(gè)路徑的最后一個(gè)路由,如上邊的第一個(gè)變量a和第一個(gè)變量c
  • 第二個(gè)路徑開頭前不帶/,第一個(gè)路徑結(jié)尾處帶/,則直接將第二個(gè)路徑拼接在第一個(gè)路徑后面,如上邊的第變量d和第二個(gè)變量a
  • 如果第二個(gè)路徑包含根域名(http://xxx),則直接以第二個(gè)路徑為主(第一個(gè)路徑失效) 處理URL路徑參數(shù)

注意:querystring方法官方表示已棄用

NodeJS有一個(gè)內(nèi)置模塊querystring,它里面的parsestringify方法可以幫助我們快速處理URL上的形如id=8&name=mouse的參數(shù):

const querystring = require("querystring");
var qs = "id=8&name=Ailjx";
// parse:路徑將參數(shù)轉(zhuǎn)化為對(duì)象
var parsed = querystring.parse(qs);
console.log(parsed.id, parsed.name); // 8 Ailjx

var qo = {
    x: 3,
    y: 4,
};
//stringify:將對(duì)象轉(zhuǎn)化為路徑參數(shù)
var parsed = querystring.stringify(qo);
console.log(parsed); // x=3&y=4

querystring中還有一對(duì)能夠轉(zhuǎn)義特殊字符的方法: escape/unescape

const querystring = require("querystring");
var str = 'ns"--';
// escape:將特殊字符轉(zhuǎn)義
var escaped = querystring.escape(str);
console.log(escaped); //ns%22--

// unescape:恢復(fù)轉(zhuǎn)義的特殊字符
console.log(querystring.unescape(escaped)); // ns"--

對(duì)于特殊字符的轉(zhuǎn)義在一些特殊情況下特別重要,例如我們通過用戶傳遞的參數(shù)來向mysql數(shù)據(jù)庫查詢數(shù)據(jù),我們?yōu)榱朔乐褂脩魝鬟f的參數(shù)與sql語句發(fā)送沖突,就可以對(duì)該參數(shù)進(jìn)行轉(zhuǎn)義,以防止sql注入

例如有一條含有用戶傳遞的param參數(shù)的sql語句:

let sql = `select * from users where name = "${param}" and del_status=1`

上面的sql語句在正常情況下是只能查詢到一條數(shù)據(jù),如:

// let param = 'Ailjx' ,對(duì)應(yīng)的sql語句如下:
select * from users where name = "Ailjx" and del_status=1

但當(dāng)paramsql語句沖突時(shí):

// let param = 'ns"--',對(duì)應(yīng)的sql語句如下:
select * from tb_nature where nature = "ns"-- " and del_status=1

可以看到del_status被參數(shù)中的--注釋掉了,失去了作用,這時(shí)這條sql能查詢到多條數(shù)據(jù),這就是sql注入的危害,也就是我們需要轉(zhuǎn)義特殊字符的原因

正確轉(zhuǎn)換文件路徑

url模塊針對(duì)轉(zhuǎn)換文件路徑提供了單獨(dú)的fileURLToPathpathToFileURL方法,它們不僅能正確進(jìn)行文件路徑的轉(zhuǎn)換而且能自動(dòng)適配不同的操作系統(tǒng)

fileURLToPath該方法能夠正確將文件網(wǎng)址url轉(zhuǎn)換成文件路徑:

const { fileURLToPath } = require("url");

console.log(new URL("file:///C:/path/").pathname); // 獲得錯(cuò)誤路徑:/C:/path/
console.log(fileURLToPath("file:///C:/path/")); // 獲得正確路徑:C:\path\ (Windows)

console.log(new URL("file://nas/foo.txt").pathname); //  獲得錯(cuò)誤路徑:/foo.txt
console.log(fileURLToPath("file://nas/foo.txt")); //  獲得正確路徑: \\nas\foo.txt (Windows)

console.log(new URL("file://c://你好.txt").pathname); //  獲得錯(cuò)誤路徑:/c://%E4%BD%A0%E5%A5%BD.txt
console.log(fileURLToPath("file://c://你好.txt")); //  獲得正確路徑: c:\\你好.txt (Windows)

pathToFileURL方法,能夠?qū)⑽募窂睫D(zhuǎn)換成文件網(wǎng)址url對(duì)象

const { pathToFileURL } = require("url");
console.log(new URL("/foo#1", "file:").href); // 錯(cuò)誤: file:///foo#1
console.log(pathToFileURL("/foo#1").href); // 正確:   file:///D:/foo%231 

console.log(new URL("/some/path%.c", "file:").href); // 錯(cuò)誤: file:///some/path%.c
console.log(pathToFileURL("/some/path%.c").href); // 正確:   file:///D:/some/path%25.c 

轉(zhuǎn)換為Options Url對(duì)象

urlToHttpOptions方法可以將new URL返回的WHATWG URL對(duì)象轉(zhuǎn)換成http.request()https.request()需要的Options Url對(duì)象

http.request()https.request()在下面會(huì)講到

const { urlToHttpOptions } = require("url");
const myURL = new URL('https://a:b@測(cè)試?abc#foo');
console.log(urlToHttpOptions(myURL));
/*
{
  protocol: 'https:',
  hostname: 'xn--g6w251d',
  hash: '#foo',
  search: '?abc',
  pathname: '/',
  path: '/?abc',
  href: 'https://a:b@xn--g6w251d/?abc#foo',
  auth: 'a:b'
}
*/

六、跨域處理

在不做處理的情況下,前后端交互會(huì)出現(xiàn)CORS跨域的問題,如:

定義一個(gè)簡(jiǎn)單的服務(wù)器:

server.js

const http = require("http");
const url = require("url");
const server = http.createServer();
server.on("request", (req, res) => {
    const urlObj = url.parse(req.url, true);
    switch (urlObj.pathname) {
        case "/api/user":
            // 模擬數(shù)據(jù)
            const userinfo = {
                name: "Ailjx",
            };
            res.end(JSON.stringify(userinfo));
            break;
        default:
            res.end("404");
            break;
    }
});
server.listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

可以看到上面我們定義的服務(wù)器并沒有設(shè)置跨域,我們直接在html文件內(nèi)請(qǐng)求該服務(wù)器的接口:

index.html

<body>
    <div>jsonp接口測(cè)試</div>
    <script>
        fetch('http://localhost:3000/api/user')
        .then(res => res.json())
        .then(res => console.log(res))
    </script>
</body>

打開該html文件,可以看到果然出現(xiàn)了CORS跨域的報(bào)錯(cuò)

這種問題有多種解決方案:

  • 后端設(shè)置響應(yīng)頭來允許前端訪問服務(wù)器(只需要后端進(jìn)行修改)
  • 前后端使用jsonp方式進(jìn)行交互(前后端都需要進(jìn)行相應(yīng)修改)
  • 前端配置代理(只需要前端進(jìn)行修改,這在像vuereact等這些框架中經(jīng)常使用)

后端設(shè)置跨域

后端可以直接在響應(yīng)頭里設(shè)置跨域,而前端不需要做額外的操作

我們下載一個(gè)vscodeLive Server插件,用于在線運(yùn)行html文件:

右鍵html文件選擇Open with Live Server

打開后發(fā)現(xiàn)前端html的在線運(yùn)行地址為http://127.0.0.1:5500,我們?cè)诤蠖朔祷財(cái)?shù)據(jù)的響應(yīng)頭里允許該地址訪問即可:

修改一下上邊的server.js

const http = require("http");
const url = require("url");
const server = http.createServer();
server.on("request", (req, res) => {
    const urlObj = url.parse(req.url, true);
    res.writeHead(200, {
        "content-type": "application/json;charset=utf-8",
         // 設(shè)置跨域允許http://127.0.0.1:5500訪問
        "Access-Control-Allow-Origin": "http://127.0.0.1:5500",
         // "Access-Control-Allow-Origin": "*", 也可以使用'*',代表允許所有地址訪問
    });
    switch (urlObj.pathname) {
        case "/api/user":
            // 模擬數(shù)據(jù)
            const userinfo = {
                name: "Ailjx",
            };
            res.end(JSON.stringify(userinfo));
            break;
        default:
            res.end("404");
            break;
    }
});

server.listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

這時(shí)前端就能正常調(diào)用該接口了:

jsonp接口

我們知道htmlscript標(biāo)簽可以引入js文件,并且重要的是使用script標(biāo)簽引入js沒有跨域的要求,所以我們可以在script標(biāo)簽的src屬性中調(diào)用我們的接口來獲取后端返回的數(shù)據(jù)

前端處理:

<body>
    <div>jsonp接口測(cè)試</div>
    <div>
        name: <b id="myName"></b>
    </div>
    <script>
        // 定義一個(gè)接收后端返回?cái)?shù)據(jù)的函數(shù)
        function getUser(params) {
            const myName = document.getElementById('myName')
            // 可以做一些操作
            myName.innerText = params.name
        }
        // 創(chuàng)建一個(gè)script標(biāo)簽
        const myScript = document.createElement('script')
        // script標(biāo)簽的src內(nèi)調(diào)用接口,需要將我們定義的接收數(shù)據(jù)的函數(shù)名傳遞給后端
        myScript.src = 'http://localhost:3000/api/user?cb=getUser'
        // 向文檔內(nèi)插入該script標(biāo)簽
        document.body.appendChild(myScript)
    </script>
</body>

或者直接用script調(diào)用后端接口:

<body>
    <div>jsonp接口測(cè)試</div>
    <div>
        name: <b id="myName"></b>
    </div>
    <script>
        // // 定義一個(gè)接收后端返回?cái)?shù)據(jù)的函數(shù)
        function getUser(params) {
            const myName = document.getElementById('myName')
            myName.innerText = params.name
        }
    </script>
    <!-- 調(diào)用后端接口 -->
    <script src="http://localhost:3000/api/user?cb=getUser"></script>
</body>

后端處理:

const http = require("http");
const url = require("url");
const server = http.createServer();

server.on("request", (req, res) => {
    const urlObj = url.parse(req.url, true);
    switch (urlObj.pathname) {
        case "/api/user":
            // 模擬數(shù)據(jù)
            const userinfo = {
                name: "Ailjx",
            };
            // urlObj.query.cb是前端傳遞的接收數(shù)據(jù)的函數(shù)名稱
            // 返回給前端一個(gè)函數(shù)調(diào)用
            res.end(`${urlObj.query.cb}(${JSON.stringify(userinfo)})`);
            break;
        default:
            res.end("404");
            break;
    }
});

server.listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

可以看到使用jsonp的原理就是在script標(biāo)簽中調(diào)用后端接口,因?yàn)?code>script標(biāo)簽內(nèi)的js代碼是立即執(zhí)行

所以我們需要提前定義一個(gè)接收后端參數(shù)的處理函數(shù),然后將該函數(shù)名傳遞給后端,后端根據(jù)這個(gè)函數(shù)名返回給前端一個(gè)該函數(shù)的調(diào)用并將需要給前端的數(shù)據(jù)作為該函數(shù)的參數(shù)

上述代碼的最終效果如下:

<script>
    // 定義一個(gè)接收后端返回?cái)?shù)據(jù)的函數(shù)
    function getUser(params) {
        const myName = document.getElementById('myName')
        myName.innerText = params.name
    }
</script>
  <!-- <script src="http://localhost:3000/api/user?cb=getUser"></script>的效果如下 -->
<script>
    getUser({
        name: "Ailjx",
    })
</script>

七、Node作為中間層使用

上面我們使用node搭建了后端服務(wù)器,使其作為服務(wù)端運(yùn)行,但其實(shí)node還能當(dāng)作客戶端反過來去調(diào)用其它服務(wù)端的接口,這使得node成為了一個(gè)中間層

因?yàn)榭缬蛑皇菫g覽器的限制,服務(wù)端之間的通信并不存在跨域的問題,這樣我們就能借助node去調(diào)用第三方具有跨域限制的接口,這是將node作為中間層的一個(gè)非常實(shí)用的功能

模擬get請(qǐng)求(轉(zhuǎn)發(fā)跨域數(shù)據(jù))

在貓眼電影網(wǎng)上隨便找了一個(gè)帶有跨域的接口,我們直接調(diào)用時(shí)會(huì)報(bào)CORS跨域問題:

<h1>使用node模擬get請(qǐng)求</h1>
<script>
    fetch('https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4')
        .then(res => res.json())
        .then(res => {
            console.log(res);
        })
</script>

這時(shí)我們可以利用node幫我們?nèi)フ?qǐng)求這個(gè)接口的數(shù)據(jù):

const http = require("http");
const https = require("https");
// http和https的區(qū)別僅在于一個(gè)是http協(xié)議一個(gè)是https協(xié)議
const url = require("url");

const server = http.createServer();

server.on("request", (req, res) => {
    const urlObj = url.parse(req.url, true);

    res.writeHead(200, {
        "content-type": "application/json;charset=utf-8",
        "Access-Control-Allow-Origin": "http://127.0.0.1:5500",
    });

    switch (urlObj.pathname) {
        case "/api/maoyan":
            // 我們定義的httpget方法:使node充當(dāng)客戶端去貓眼的接口獲取數(shù)據(jù)
            httpget((data) => res.end(data));
            break;

        default:
            res.end("404");
            break;
    }
});

server.listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

function httpget(cb) {
    // 定義一個(gè)存放數(shù)據(jù)的變量
    let data = "";
    // 因?yàn)樨堁鄣慕涌谑莌ttps協(xié)議的,所以我們需要引入https
    // http和https都具有一個(gè)get方法能夠發(fā)起get請(qǐng)求,區(qū)別是一個(gè)是http協(xié)議,一個(gè)是https協(xié)議
    // http get方法第一個(gè)參數(shù)為接口地址,第二個(gè)參數(shù)為回調(diào)函數(shù)
    https.get(
        "https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4",
        (res) => {
            // http get方法獲取的數(shù)據(jù)是一點(diǎn)點(diǎn)返回的,并不是直接返回全部
            // 監(jiān)聽data,當(dāng)有數(shù)據(jù)返回時(shí)就會(huì)被調(diào)用
            res.on("data", (chunk) => {
                // 收集數(shù)據(jù)
                data += chunk;
            });
            // 監(jiān)聽end,數(shù)據(jù)返回完畢后調(diào)用
            res.on("end", () => {
                cb(data);
            });
        }
    );
}

之后我們調(diào)用我們node的接口即可:

<h1>使用node模擬get請(qǐng)求</h1>
<script>
    fetch('http://localhost:3000/api/maoyan')
        .then(res => res.json())
        .then(res => {
            console.log(res);
        })
</script>

這里node即作為服務(wù)端給我們提供接口/api/maoyan,又充當(dāng)了一下客戶端去調(diào)用貓眼的接口,這樣我們就繞過了貓眼的跨域限制獲取了它的數(shù)據(jù)

模擬post請(qǐng)求(服務(wù)器提交)

使用node模擬post請(qǐng)求需要使用httphttpsrequest方法來進(jìn)行請(qǐng)求的配置,稍微有點(diǎn)麻煩:

httphttps模塊的區(qū)別僅在于一個(gè)是http協(xié)議一個(gè)是https協(xié)議

const http = require("http");
const https = require("https");
const url = require("url");
const server = http.createServer();
server.on("request", (req, res) => {
    const urlObj = url.parse(req.url, true);

    res.writeHead(200, {
        "content-type": "application/json;charset=utf-8",
        "Access-Control-Allow-Origin": "http://127.0.0.1:5500",
    });

    switch (urlObj.pathname) {
        case "/api/xiaomiyoumin":
            // httpPost方法:使node充當(dāng)客戶端去小米有品的post接口獲取數(shù)據(jù)
            httpPost((data) => res.end(data));
            break;

        default:
            res.end("404");
            break;
    }
});

server.listen(3000, () => {
    console.log("服務(wù)器啟動(dòng)啦!");
});

function httpPost(cb) {
    // 定義一個(gè)存放數(shù)據(jù)的變量
    let data = "";
    // 這是小米有品的一個(gè)post接口:"https://m.xiaomiyoupin.com/mtop/market/search/placeHolder"
    // 這個(gè)接口調(diào)用時(shí)需要傳“[{}, { baseParam: { ypClient: 1 } }]”這樣一個(gè)參數(shù)才能返回?cái)?shù)據(jù)

    // 配置Options Url請(qǐng)求對(duì)象
    const options = {
        // 域名
        hostname: "m.xiaomiyoupin.com",
        // 接口端口號(hào),443代表https,80代表http
        port: "443",
        // 路徑
        path: "/mtop/market/search/placeHolder",
        // 請(qǐng)求方式
        method: "POST",
        // 請(qǐng)求頭
        Headers: {
            // 表示接收json數(shù)據(jù)
            "Content-Type": "application/json",
        },
    };

    // http request方法第一個(gè)參數(shù)為請(qǐng)求對(duì)象,第二個(gè)參數(shù)為回調(diào)函數(shù),request方法返回一個(gè)值(),在該值內(nèi)通過調(diào)用write向post請(qǐng)求傳遞數(shù)據(jù)
    const req = https.request(options, (res) => {
        // 監(jiān)聽data,當(dāng)有數(shù)據(jù)返回時(shí)就會(huì)被調(diào)用
        res.on("data", (chunk) => {
            // 收集數(shù)據(jù)
            data += chunk;
        });
        // 監(jiān)聽end,數(shù)據(jù)返回完畢后調(diào)用
        res.on("end", () => {
            cb(data);
        });
    });
    // 發(fā)送post的參數(shù)
    // req.write(JSON.stringify([{}, { baseParam: { ypClient: 1 } }]));
    // 這里的使用與我們server服務(wù)器中的req參數(shù)使用方式差不多,不要忘記最后調(diào)用end方法,并且也可以直接在end方法內(nèi)傳遞數(shù)據(jù)
    req.end(JSON.stringify([{}, { baseParam: { ypClient: 1 } }]));
}
<body>
    <h1>使用node模擬post請(qǐng)求</h1>
    <script>
        fetch('http://localhost:3000/api/xiaomiyoumin')
            .then(res => res.json())
            .then(res => {
                console.log(res);
            })
    </script>
</body>

八、使用Node實(shí)現(xiàn)爬蟲

我們使用node的一個(gè)cheerio包也可以實(shí)現(xiàn)爬蟲功能,如我們爬取貓眼移動(dòng)端https://i.maoyan.com首頁的一些數(shù)據(jù):

我們?cè)谝粋€(gè)文件夾內(nèi)打開終端,先生成package.json文件:

npm init

安裝cheerio

npm i cheerio

文件夾內(nèi)創(chuàng)建我們的服務(wù)器文件server.js

const https = require("https");
const http = require("http");
const cheerio = require("cheerio");

http.createServer((request, response) => {
    response.writeHead(200, {
        "content-type": "application/json;charset=utf-8",
    });
    const options = {
        hostname: "i.maoyan.com",
        port: 443,
        path: "/",
        method: "GET",
    };
    // 獲取頁面數(shù)據(jù)
    const req = https.request(options, (res) => {
        let data = "";
        res.on("data", (chunk) => {
            data += chunk;
        });

        res.on("end", () => {
            response.end(data);
        });
    });

    req.end();
}).listen(3000);

上面演示了使用 https.request方法配置get請(qǐng)求(接口為:https://i.maoyan.com)的寫法,你也可以直接使用https.get進(jìn)行調(diào)用

通過瀏覽器調(diào)用我們的服務(wù)器:

可以看到我們成功獲取了貓眼移動(dòng)端首頁的html文檔,我們需要的數(shù)據(jù)都在文檔中了,之后我們只需將我們需要的數(shù)據(jù)提取出來,這里就將用到cheerio

const https = require("https");
const http = require("http");
const cheerio = require("cheerio");
http.createServer((request, response) => {
    response.writeHead(200, {
        "content-type": "application/json;charset=utf-8",
    });

    const options = {
        hostname: "i.maoyan.com",
        port: 443,
        path: "/",
        method: "GET",
    };
    // 獲取頁面數(shù)據(jù)
    const req = https.request(options, (res) => {
        let data = "";
        res.on("data", (chunk) => {
            data += chunk;
        });

        res.on("end", () => {
            // 處理頁面數(shù)據(jù)
            filterData(data);
        });
    });
    function filterData(data) {
        let $ = cheerio.load(data);
        // 獲取class="column content"的元素
        let $movieList = $(".column.content");
        // console.log($movieList);
        let movies = [];
        $movieList.each((index, value) => {
            movies.push({
                // 獲取class為movie-title下的class為title的元素的文本值
                title: $(value).find(".movie-title .title").text(),
                detail: $(value).find(".detail .actor").text(),
            });
        });

        response.end(JSON.stringify(movies));
    }

    req.end();
}).listen(3000);

cheerio.load接收html文檔字符串,它返回一個(gè)對(duì)象,該對(duì)象與jQuery相似,我們可以對(duì)其使用jQuery的語法進(jìn)行操作

上面使用的class類名都是在貓眼首頁文檔的對(duì)應(yīng)類名,如:

重新訪問一下我們的服務(wù)器:

可以看到我們已經(jīng)爬蟲成功!

如果在請(qǐng)求https://i.maoyan.com接口時(shí)獲取不到數(shù)據(jù),可能是我們?yōu)g覽器上的貓眼網(wǎng)進(jìn)入了驗(yàn)證操作,我們?cè)谠L問我們服務(wù)器的這個(gè)瀏覽器上打開https://i.maoyan.com進(jìn)行驗(yàn)證一下,之后我們就能請(qǐng)求到https://i.maoyan.com的html文檔內(nèi)容了

到此這篇關(guān)于Node.js 搭建后端服務(wù)器內(nèi)置模塊( http+url+querystring 的使用)的文章就介紹到這了,更多相關(guān)Node.js 后端搭建內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Nodejs?Socket連接池及TCP?HTTP網(wǎng)絡(luò)模型詳解

    Nodejs?Socket連接池及TCP?HTTP網(wǎng)絡(luò)模型詳解

    這篇文章主要為大家介紹了Nodejs?Socket連接池及TCP?HTTP網(wǎng)絡(luò)模型,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Node.js中文件操作模塊File System的詳細(xì)介紹

    Node.js中文件操作模塊File System的詳細(xì)介紹

    FileSystem模塊是類似UNIX(POSIX)標(biāo)準(zhǔn)的文件操作API,用于操作文件系統(tǒng)——讀寫目錄、讀寫文件——Node.js底層使用C程序來實(shí)現(xiàn),這些功能是客戶端JS所不具備的。下面這篇文章就給大家詳細(xì)介紹了Node.js中的文件操作模塊File System,有需要的朋友們可以參考借鑒。
    2017-01-01
  • node.js基礎(chǔ)知識(shí)匯總

    node.js基礎(chǔ)知識(shí)匯總

    這篇文章主要介紹了node.js基礎(chǔ)知識(shí),幫助大家更好的理解和學(xué)習(xí)node.js,感興趣的朋友可以了解下
    2020-08-08
  • Node.js(v16.13.2版本)安裝及環(huán)境配置的圖文教程

    Node.js(v16.13.2版本)安裝及環(huán)境配置的圖文教程

    本文主要介紹了Node.js(v16.13.2版本)安裝及環(huán)境配置的圖文教程,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Nodejs下DNS緩存問題淺析

    Nodejs下DNS緩存問題淺析

    本文給大家一起探討nodejs下dns的緩存問題,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2016-11-11
  • 使用forever管理nodejs應(yīng)用教程

    使用forever管理nodejs應(yīng)用教程

    這篇文章主要介紹了使用forever管理nodejs應(yīng)用教程,本文介紹了forever的安裝、常用命令等,最有用的莫過于文件改動(dòng)監(jiān)聽并自動(dòng)重啟了,這可以增加開nodejs應(yīng)用的效率,需要的朋友可以參考下
    2014-06-06
  • npm?install?-g?@vue/cli常見問題解決匯總

    npm?install?-g?@vue/cli常見問題解決匯總

    這篇文章主要給大家介紹了關(guān)于npm?install?-g?@vue/cli常見問題解決的相關(guān)資料,文中通過實(shí)例代碼將解決的方式介紹的非常詳細(xì),對(duì)遇到這個(gè)問題的朋友具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-08-08
  • node?NPM庫qs?iconv-lite字符串編碼轉(zhuǎn)換及解析URL查詢學(xué)習(xí)

    node?NPM庫qs?iconv-lite字符串編碼轉(zhuǎn)換及解析URL查詢學(xué)習(xí)

    這篇文章主要為大家介紹了node?NPM庫之qs解析URL查詢字符串及iconv-lite字符串編碼轉(zhuǎn)換學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 利用node.js本地搭建HTTP服務(wù)器

    利用node.js本地搭建HTTP服務(wù)器

    這篇文章主要為大家詳細(xì)介紹了利用node.js本地搭建HTTP服務(wù)器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Visual?Studio?Code中npm腳本找不到圖文解決辦法

    Visual?Studio?Code中npm腳本找不到圖文解決辦法

    這篇文章主要給大家介紹了關(guān)于Visual?Studio?Code中npm腳本找不到的圖文解決辦法,做前端開發(fā)如果項(xiàng)目達(dá)到了一定的規(guī)模就離不開npm了,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07

最新評(píng)論

望都县| 南溪县| 静乐县| 烟台市| 琼结县| 高要市| 德清县| 新余市| 新余市| 云梦县| 寿光市| 固原市| 运城市| 南昌县| 北安市| 额敏县| 兴义市| 徐汇区| 通榆县| 论坛| 旌德县| 革吉县| 桂东县| 财经| 准格尔旗| 大石桥市| 望江县| 蕉岭县| 进贤县| 阿城市| 隆回县| 武夷山市| 旅游| 汕头市| 灵石县| 仁布县| 祁阳县| 合水县| 岳池县| 建昌县| 镇康县|