Выполнение различных операций через HTTP HEADER

Node.js HTTP JavaScript

Как профессиональный монтажник, я никогда не забочусь о заголовках веб-страниц, меня больше всего волнуетStatus CodeДа или нет200. Но HEADER действительно важен, клиент получает контент от сервера, и в первую очередь это общение через HEADER! HEADER может помочь нам выполнить множество сложных операций, улучшить производительность сайта и удобство для пользователей. Ладно, давай пощупаем.

первичная шутка

  • многоязычный(Accept-Language)
  • противоугонная цепь (Referer,Referered)
  • Gzip, проще говоря, для сохранения данных (Accept-Encoding,Content-Encoding)

многоязычный

Многоязычность означает, что веб-сайт может переключаться между несколькими языками.Мы не будем обсуждать здесь создание N веб-сайтов, и один язык соответствует одному веб-сайту. Вот обсуждение того, как разумно вернуть желаемый язык пользователя.

server client
Закинуть на серверAccept-Language
получить другойAccept-Language
Поле выглядит такzh,en-US;q=0.9,en;q=0.8
Начать обработку, превратить поля во взвешенныеqмассив
Отсортировано так[{"name":"zh","q":1},{"name":"en-US","q":0.9},{"name":"en","q":0.8}]
Возвращает принадлежащие языки в соответствии с весами, сzhвозвращениеzh,нетzhпросто вернисьen-US
Что делать, если у меня нет языкового пакета, необходимого другой стороне? Спешите, ждите онлайн!
Нет другого выбора, кроме как дать друг другу наш официальный язык (по умолчанию)
отправьте, пожалуйста, получите
Ваш ПРИНЯТЬ язык соответствует Этот веб-сайт довольно хорош.Хотя это иностранный веб-сайт, я знаю, что я китаец.
У нас нет языкового пакета для вашего региона эммм, это марсианский текст?

Поставляется с простой версией реализации на нескольких языках:

let languages = {
    zh:{
        title:"你好",
        content:"同学"
    },
    en:{
        title:"Hey",
        content:"guy"
    },
}
//设置默认语言,万一用户的语言我们不支持呢?
let defaultLanguage="zh"
let http = require('http');
function getLanguage(client_langs){
    let finalLanguage=defaultLanguage
    try{
        if(client_langs){
            //排序获取语言顺序
            client_langs=client_langs.split(',').map(l=>{
                let [name,q] = l.split(';');
                q = q?Number(q.split('=')[1]):1 
                return {name,q}
            }).sort((a,b)=>b.q-a.q);
            //匹配服务器有的语言,并返回
            for(let i = 0 ;i <languages.length;i++){
                let name= languages[i].name;
                if(languages[name]){
                    finalLanguage=name;
                    break;
                }
            }
        }
    }catch(e){}
    return languages[finalLanguage]
}
http.createServer(function (req,res) {
    //获取客户端的语言
    let client_langs = req.headers['Accept-Language'];
    let lan=getLanguage(client_langs)
    //将语言打印到客户端
    res.end(`<p>${lan.title}</p><p>${lan.content}</p>`)
}).listen(3000);

противоугонная цепь

Наиболее используемой технологией должно быть ограничение на изображения, можно получить только это доменное имя, а другие доменные имена не следует рассматривать.

server client
запросил изображение на сайте
пройти черезReferer,RefereredОбнаружено, что доменное имя этого веб-сайта отсутствует в нашем белом списке.
Это изображение недоступно для веб-сайта
В это время на землю положили кусок универсального грунта.
Поддержка подлинной, пожалуйста, посетите наш веб-сайт

Принцип реализации, здесь я использую iframe в качестве примера, на самом деле принцип очень прост, чтобы сравнить источник, либо такой же, как запрашиваемый ресурс, либо в белом списке, иначе он будет отклонен. Конечно, если исходника нет, он будет опубликован напрямую, на случай, если кто-то откроет его один, а не по хотлинку:

let http =  require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
// 设置白名单
let whiteList = ['localhost:3000'];
http.createServer(function (req,res) {
    //获取请求地址
    let { pathname } = url.parse(req.url);
    // 获取物理地址
    let realPath = path.join(__dirname,pathname);
    // 获取文件状态
    fs.stat(realPath,function(err,statObj) {
        if(err){
            res.statusCode = 404;
            res.end();
        }else{
             // 重点来了
            let Referer = req.headers['Referer'] || req.headers['referred'];
            //如果有来源
            if(Referer){
                //获取双方域名
                let current = req.headers['host'] 
                Referer = url.parse(Referer).host
                console.log(current,Referer)
                //如果域名相同活在白名单中,放行!
                if (current === Referer || whiteList.includes(Referer)){
                    fs.createReadStream(realPath).pipe(res);
                }else{
                    //不放行,此乃盗链!给你个眼神自行体会
                    fs.createReadStream(path.join(__dirname,'files/2.html')).pipe(res);
                }
            }else{
                //没有来源,也放行。万一是单独打开的呢~
                fs.createReadStream(realPath).pipe(res);
            }
        }
    })
}).listen(3000);

gzip

Современные браузеры достаточно продвинуты, чтобы принимать сжатые пакеты. Я восхищаюсь тобой. Итак, как вы передаете сжатые веб-страницы?

server client
Закинуть на серверAccept-Encoding
наверное такая структураgzip, deflate, br
добраться до намерения другой стороны, начать настраивать сжатие
Если сжатие поддерживается, сначала установите заголовокContent-Encoding
Существует много методов сжатия, в зависимости от соответствия, поддерживаемого сервером.
Сжать веб-страницы онлайн и вернуться к клиенту после успеха
Huanhuaxi экономит трафик, не влияя на опыт

С предложенным кодом при тестировании не забудьте создать тестовый html-файл.

let http = require('http');
//用于压缩文件所需的库
let fs = require('fs');
let path = require('path');
//压缩的库
let zlib = require('zlib');
http.createServer(function (req,res) {
    //获取客户端接受的压缩方式
    let rule = req.headers['Accept-Encoding'];
    // 创建原文件可读流
    let originStream=fs.createReadStream(path.join(__dirname, '1.html'));
    if(rule){
        // 啊啊啊!正则是个坎,我怕我是跨不过去了。
        if(rule.match(/\bgzip\b/)){
            //如果支持压缩!一定要设置头部!
            res.setHeader('Content-Encoding','gzip');
            originStream=originStream.pipe(zlib.createGzip())
        } else if (rule.match(/\bdeflate\b/)){
            res.setHeader('Content-Encoding', 'deflate');
            originStream=originStream.pipe(zlib.createDeflate())
        }
    }
    // 输出处理后的可读流
    originStream.pipe(res)
}).listen(3000);

Промежуточные операции

Большинство первичных операций должны полагаться только на конфигурацию HEADER для достижения ***, конечно, нам немного сложнее для промежуточного уровня, большинство из которых требует сотрудничества клиента и сервера.

  • Клиент отправляет содержимое на сервер (Content-Type,Content-Length)
  • Клиент получает содержимое с сервера (Range,Content-Range)
  • клиентский сканер, сканирование веб-страниц

клиент отправляет контент на сервер

server client
Я дал вам кучу данных, вы их обрабатываете
Мозгов нет, кто знает, что вы собираетесь делать, пожалуйста, настройте ЗАГОЛОВОК
хорошо, позвольте мне сказать вамContent-TypeиContent-Length
Да, необходимо, чтобы тип содержимого данных был length
Я передал вам данные, посмотрите
Получение ~ мониторинг полученных данных представляет собой набор буферов
После принятия объедините Buffer
в соответствии сContent-Typeобрабатывать данные
форматировать данные, конец

Код сервера

let http = require('http');
let server = http.createServer();
let arr=[]
server.on('request', (req, res)=>{
  req.on('data',function (data) {
    //把获取到的Buffer数据都放入熟组
    arr.push(data);
  });
  req.on('end',function() {
    // 请求结束了,好了可以开始处理断断续续收到的Buffer了
    // 合并buffer
    let r = Buffer.concat(arr).toString();
    if (req.headers['content-type'] === 'x-www-form-urlencoded'){
        let querystring = require('querystring');
        r = querystring.parse(r); // a=1&b=2然后格式化
        console.log("querystring",r);
      } else if (req.headers['content-type'] === 'application/json'){
        //听说是JSON格式的
        console.log("json",JSON.parse(r));
      } else{
        //没有格式?那原来是啥就是啥吧。
        console.log("no type",r);
      }
      arr=[]
      res.end('结束了!');
  });
})
server.listen(3000,()=>{
  console.log(`server start`);
});

Код клиента

// 设置请求地址的配置
let opts = {
  host:'localhost',
  port:3000,
  path:'/',
  // 头部设置很重要,头部设置很重要,头部设置很重要
  headers:{
    'Content-Type':'x-www-form-urlencoded',
    //长度超过3就没有人理你了
    "Content-Length":7
  }
}
let http = require('http');
let client = http.request(opts,function (res) {
  res.on('data',function (data) {
      console.log(data);
  })
});
client.end("a=1&b=2");

клиент получает частичный контент с сервера

server client
Я хочу часть ресурса
Хорошо, скажи мне диапазон
Я вставил ЗАГОЛОВОКRangeв настоящее время,bytes=0-3
Content-Range:bytes 0-3/7, пожалуйста, примите, этот файл имеет в общей сложности 8 байт, первые 3 байта были предоставлены вам Хорошо, тогда дай мне следующий,bytes=4-7
дать это тебе end

Все находили, такой диапазон для получения данных — это совсем простой вариант продолжения по брейкпойнту! Однако есть момент, в котором легко ошибиться, это вычисление размера файла, потому что позиция байтов файла считается от 0, поэтому полный диапазон диапазона0~size-1/size-1, всем внимание.

серверная часть

let http = require('http');
let fs = require('fs');
let path = require('path');
// 当前要下载的文件的大小
let size = fs.statSync(path.join(__dirname, 'my.txt')).size;
let server = http.createServer(function (req, res) {
  let range = req.headers['range']; //获取client请求访问的部分内容
  if (range) {
    let [, start, end] = range.match(/(\d*)-(\d*)/);
    start = start ? Number(start) : 0;
    end = end ? Number(end) : size - 1; // 10个字节 size 10  (0-9)
    console.log(`bytes ${start}-${end}/${size - 1}`)
    res.setHeader('Content-Range', `bytes ${start}-${end}/${size - 1}`);
    fs.createReadStream(path.join(__dirname, 'my.txt'), { start, end }).pipe(res);
  } else {
    // 会把文件的内容写给客户端
    fs.createReadStream(path.join(__dirname, 'my.txt')).pipe(res);
  }
});
server.listen(3000);

сторона клиента

let opts = {
    host:'localhost',
    port:3000,
    headers:{}
  }
let http = require('http');
let start = 0;
let fs = require('fs');
function download() {
    //分流下载,部分下载
    opts.headers.Range = `bytes=${start}-${start+3}`;
    start+=4;
    let client = http.request(opts,function (res) {
        let total = res.headers['content-range'].split('/')[1];
        res.on('data',function (data) {
          fs.appendFileSync('./download.txt',data);
        });
        res.on('end',function () {
            //结束之后,1s之后再下载
          setTimeout(() => {
              console.log(start,total)
            if (start <= total)
              download();
          }, 1000);
        })
    });
    client.end();
}
download()

Клиент сканирует веб-контент, простой сканер

Работа этой части на самом деле очень проста, если вы создаете запрос на получение веб-страницы. Трудность заключается в том, как удалить эффективную информацию с веб-страниц и отфильтровать бесполезную информацию. Взял тут развлекательную версию Байду.Байду еще добросовестный,это utf8,иначе будет косяк.

let http = require('http');
let opts = {
  host:'news.baidu.com',
  path:'/ent'
}
//创建一个请求,获取网站内容
let client = http.request(opts,function (r) {
    let arr= [];
    //资源不可能一次下载完成,因此每次获取到数据都要push到arr中
    r.on('data',function (data) {
        arr.push(data);
    });
    r.on('end',function() {
        //合并资源
        let result = Buffer.concat(arr).toString();
        //对资源进行处理,可以是变成我这样的对象,之后不管做什么处理都很方便
        let content = result.match(/<ul class="ulist mix-ulist">(?:[\s\S]*?)<\/ul>/img).toString().match(/<li>(?:[\s\S]*?)<\/li>/img);
        content=content.map((c)=>{
            let href=/<a href="(?:[\S]*?)"/img.exec(c)
            let title=/">(?:[\s\S]*?)<\/a>/img.exec(c)
            return {
                href:href[0].replace(/"/img,"").replace("<a href=",""),
                title:title[0].replace(/">/img,"").replace("</a>","")
            }
        })
        console.log(JSON.stringify(content))
        arr= [];
    })
});
client.end();