http 模块

引入

import http from 'http'
const http = require('http')

创建服务器

使用 createServer 可以创建一个 Server 实例,可以传入一个回调函数,之后每次请求都会调用这个函数,并传参 reqres

const server = http.createServer((req, res) => {
    // do
})

也可以监听 request 方法来对每次请求处理

server.on('request', (req, res) => {
    // do
})

然后需要监听某个端口号

server.listen(3000, () => {
    // do
})

现在你可以访问localhost:3000来请求服务器了,但是没有内容,我们还需要响应请求

req

包含了请求时的信息,比如 url

处理 post 请求

let data = ''
req.on('data', chunk => {
    data += chunk
})
req.on('end', () => {
    console.log(data)
})

res

调用 res.write() 可以返回内容给客户端,最后需要调用 res.end() 来结束返回

响应请求

res.write()

可以返回各种内容

// 返回普通的字符串
res.write('abc')
// 返回html标签
res.write('<b>abc</b>')
// 返回json
res.wirte('[1,2,3]')

res.writeHead()

用来指定响应头

如下,返回了 200 的状态,指定返回类型 html ,编码使用 urf-8

res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' })

使用utf-8编码可以支持我们返回中文

res.end()

结束返回,你也可以在这里最后返回一些数据

res.end('abc')

根据 url 返回不同的内容

现在你改变路径看到的都是相同的内容,我们可以在每次请求时候根据 url 返回不同内容

首先从 req 中获取 url

const { url } = req

接着就可以根据 url 来做一些事情

下面定义了一个 renderHtml 函数,可以根据传入的 url 返回不同内容

const container = content => `<div>${content}</div>`

const urlCollect = {
    '/home': container('this is home'),
    '/page': container('this is page'),
    '/api/list': '[1,2,3]'
}

const renderHtml = url => {
    return urlCollect[url] || container('not found')
}

使用

res.write(renderHtml(url))

有关 url 的更多内容可以查看 url 模块

JSONP

JSONP 是一种跨域手段,主要原理就是利用了 script 标签的 src 请求不受同源限制,去请求一个 js 文件,文件中执行我们指定的方法同时将我们需要的数据作为参数传递进去

现在我们通过 Node.js 去实现一个 JSONP 接口

import http from 'http'
import { URL } from 'url'

const jsonp = fnName => {
    const data = {
        name: 'mo',
        age: 12
    }

    // 将函数名和数据拼合成执行字符串返回
    return `${fnName}(${JSON.stringify(data)})`
}

const server = http.createServer()

server.on('request', (req, res) => {
    const { url } = req

    const { pathname, searchParams } = new URL(url, 'http://127.0.0.1:3000')

    const fnName = searchParams.get('fn')

    if (pathname === '/api/aaa' && fnName) {
        // 当 url 路径是 /api/aaa 同时有fn参数的的时候返回JSON
        res.end(jsonp(fnName))
    } else {
        res.end('not found')
    }
})

server.listen('3000')

接着编写前端的请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jsonp</title>
</head>
<body>
    
</body>
<script>
    // 创建script标签并指定src
    const script = document.createElement('script')
    script.src = 'http://localhost:3000/api/aaa?fn=getData'
    document.body.appendChild(script)

    // 定义好要执行的函数
    const getData = (data) => {
        console.log(data)
    }
</script>
</html>

然后在不同的端口号上打开页面,可以看到控制台成功输出了数据

CORS

除了 JSONP,也可以在后端直接设置 CORS,解除其他源访问我们 api 的限制

res.writeHead(200, {
    'Content-Type': 'application/json;charset=utf-8',
    // 设置cors头,允许所有域访问
    'access-control-allow-origin': '*'
})

也可以指定某个域名

代理

以上是对自己服务器 api 跨域的处理,而如果在前端想访问其他网站的 api,可以在 Node.js 进行一个代理。用 Node.js 去请求其他接口,然后返回给前端

https

接下来需要用到 https 模块

import https from 'https'
const https = require('https')

get

使用 https.get 来发起 GET 请求

创建一个 get 函数,接收一个回调并将最终的数据传递出去

const get = cb => {
    https.get(
        // 猫眼网站上的一个api接口
        'https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=杭州&ci=50&channelId=4',
        res => {
            let data = ''
            // 监听data事件,把数据流合并到最终的数据中
            res.on('data', chunk => {
                data += chunk
            })
            // 在结束的时候调用传进来的回调函数,将最终数据传递出去
            res.on('end', () => {
                cb(data)
            })

使用

get(data => {
    res.end(data)
})

request

使用 https.request 来发起 POST 请求

创建一个 request 函数,和 get 函数相同的效果,不过这次多了配置以及用 write() 传入 POST 数据

const request = cb => {
    const options = {
        hostname: 'api.juejin.cn',
        port: '443',
        path: '/recommend_api/v1/article/recommend_all_feed?aid=2608&uuid=&spider=0',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        }
    }
    const req = https.request(options, res => {
        let data = ''
        res.on('data', chunk => {
            data += chunk
        })
        res.on('end', () => {
            cb(data)
        })
    })

    // POST数据在这里传入
    req.write(
        JSON.stringify({
            id_type: 2,
            client_type: 2608,
            sort_type: 200,
            cursor: '0',
            limit: 20
        })
    )
    req.end()
}

使用

request(data => {
    res.end(data)
})

爬虫

当把 get 中的 api 换成网址后就可以直接获取到 html 文本,接着我们使用 cheerioopen in new window 这个库对文本进行解析 (当然,如果你想用正则表达式也可以)

const spider = cb => {
    https.get('https://www.w2solo.com/', res => {
        let data = ''
        res.on('data', chunk => {
            data += chunk
        })
        res.on('end', () => {
            // 解析html文本
            const $ = cheerio.load(data)

            // 就像操作jquery一样
            const topicList = $('.topic')

            const topics = []

            // 遍历dom,获取其中的文本并处理
            topicList.each((index, val) => {
                topics.push(
                    $(val).find('.title').text().trim().replace(/\s+/, ': ')
                )
            })

            cb(JSON.stringify(topics))
        })
    })
}

使用

spider(data => {
    res.end(data)
})