Làm cách nào để bạn theo dõi Chuyển hướng HTTP trong Node.js?


86

Tôi muốn mở một trang trong nút và xử lý nội dung trong ứng dụng của mình. Một cái gì đó như thế này có vẻ hoạt động tốt:

var opts = {host: host, path:pathname, port: 80};
http.get(opts, function(res) {
  var page = '';
  res.on('data', function (chunk) {
    page += chunk;
  });
  res.on('end', function() {
     // process page
  });

Tuy nhiên, điều này không hoạt động nếu trang trả về chuyển hướng 301/302. Làm cách nào để làm điều đó theo cách có thể sử dụng lại trong trường hợp có nhiều chuyển hướng? Có một mô-đun trình bao bọc trên đầu http để dễ dàng xử lý các phản hồi http xử lý từ một ứng dụng nút không?

Câu trả lời:


46

Có một mô-đun trình bao bọc trên đầu http để dễ dàng xử lý các phản hồi http xử lý từ một ứng dụng nút không?

request

Logic chuyển hướng theo yêu cầu


20
Tại sao b'jesus sống lại không phải là một phần của mô-đun http tích hợp sẵn ?!
aaaidan

1
Nó là. Nó được gọi là http.requestAPI khá đơn giản.
Raynos

3
Có thể có một cuộc gọi lại cho mỗi chuyển hướng bằng cách nào đó không? Tôi muốn lưu trữ mọi URL mà yêu cầu đi qua. Không thể tìm thấy nó trong tài liệu.
Ignas

13
@Raynos, phương thức request () của httpmô-đun tích hợp không tuân theo các chuyển hướng, do đó, đây không phải là một phần của httpmô-đun tích hợp sẵn.
gilad mayani

4
requestđã không được dùng nữa.
Ivan Rubinson

111

Nếu tất cả những gì bạn muốn làm là theo dõi chuyển hướng nhưng vẫn muốn sử dụng các mô-đun HTTP và HTTPS được tích hợp sẵn, tôi khuyên bạn nên sử dụng https://github.com/follow-redirects/follow-redirects .

yarn add follow-redirects
npm install follow-redirects

Tất cả những gì bạn cần làm là thay thế:

var http = require('http');

với

var http = require('follow-redirects').http;

... và tất cả các yêu cầu của bạn sẽ tự động theo chuyển hướng.

Với TypeScript, bạn cũng có thể cài đặt các loại

npm install @types/follow-redirects

và sau đó sử dụng

import { http, https } from 'follow-redirects';

Tiết lộ: Tôi đã viết mô-đun này.


Đây là bây giờ ở đây: github.com/request/request/blob/…
Adrian Lynch

1
Đây là cách tốt hơn câu trả lời được chấp nhận có tính năng requestsẽ thêm hơn 20 phụ thuộc mới vào mô-đun của bạn cho một nhiệm vụ đơn giản như vậy. Cảm ơn bạn đã giữ cho các mô-đun npm nhẹ, Oliver! :)
Sainan

Không hoạt động khi tôi sử dụng nó với âm thanh được lưu trữ an toàn trên s3.
thedreamsaver

Sử dụng TypeScript, hãy thêm cái này vào npm install của bạn: npm install @ type / follow-redirects để bạn có thể sử dụng nhập {https} từ 'follow-redirects'; Đây là một mô-đun tuyệt vời, đơn giản, rất hiệu quả. Merci Olivier!
Louis-Eric Simard

26

Cập nhật:

Bây giờ bạn có thể theo dõi tất cả các chuyển hướng bằng var request = require('request');cách sử dụng followAllRedirectstham số.

request({
  followAllRedirects: true,
  url: url
}, function (error, response, body) {
  if (!error) {
    console.log(response);
  }
});

đây là mã hoàn chỉnh? var của bạn là httpnhưng sau đó bạn sử dụng một chức năng gọi làrequest
jcollum

7
bạn có thể không chỉ đơn giản yêu cầu ( 'yêu cầu'), đây là một mô-đun và nhu cầu bên ngoài để được tải về & cài đặt đầu tiên - npmjs.com/package/request (NPM cài đặt theo yêu cầu)
Gilad mayani

requestđã không được dùng nữa
Ross MacArthur

18

Đưa ra một yêu cầu khác dựa trên response.headers.location:

      const request = function(url) {
        lib.get(url, (response) => {
          var body = [];
          if (response.statusCode == 302) {
            body = [];
            request(response.headers.location);
          } else {
            response.on("data", /*...*/);
            response.on("end", /*...*/);
          };
        } ).on("error", /*...*/);
      };
      request(url);

Đây là câu trả lời nếu bạn muốn sử dụng được xây dựng trong http lib, hãy làm theoresponse.headers.location
Vidar

6

Đây là hàm tôi sử dụng để tìm nạp url có chuyển hướng:

const http = require('http');
const url = require('url');

function get({path, host}, callback) {
    http.get({
        path,
        host
    }, function(response) {
        if (response.headers.location) {    
            var loc = response.headers.location;
            if (loc.match(/^http/)) {
                loc = new Url(loc);
                host = loc.host;
                path = loc.path;
            } else {
                path = loc;
            }
            get({host, path}, callback);
        } else {
            callback(response);
        }
    });
}

nó hoạt động giống như http.get nhưng theo chuyển hướng.


2

Trong trường hợp Yêu cầu PUT hoặc POST. nếu bạn nhận được mã trạng thái 405 hoặc phương thức không được phép. Hãy thử triển khai này với thư viện " yêu cầu " và thêm các thuộc tính được đề cập.
followAllRedirects: true,
followOriginalHttpMethod: true

       const options = {
           headers: {
               Authorization: TOKEN,
               'Content-Type': 'application/json',
               'Accept': 'application/json'
           },
           url: `https://${url}`,
           json: true,
           body: payload,
           followAllRedirects: true,
           followOriginalHttpMethod: true
       }

       console.log('DEBUG: API call', JSON.stringify(options));
       request(options, function (error, response, body) {
       if (!error) {
        console.log(response);
        }
     });
}

1

Đây là cách tiếp cận của tôi để tải xuống JSON với nút đơn giản, không cần gói.

import https from "https";

function get(url, resolve, reject) {
  https.get(url, (res) => {
    if(res.statusCode === 301 || res.statusCode === 302) {
      return get(res.headers.location, resolve, reject)
    }

    let body = [];

    res.on("data", (chunk) => {
      body.push(chunk);
    });

    res.on("end", () => {
      try {
        // remove JSON.parse(...) for plain data
        resolve(JSON.parse(Buffer.concat(body).toString()));
      } catch (err) {
        reject(err);
      }
    });
  });
}

async function getData(url) {
  return new Promise((resolve, reject) => get(url, resolve, reject));
}

// call
getData("some-url-with-redirect").then((r) => console.log(r));


0

Nếu bạn có httpsmáy chủ, hãy thay đổi url của bạn để sử dụng https://giao thức.

Tôi gặp vấn đề tương tự với vấn đề này. Url của tôi có http://giao thức và tôi muốn thực hiện POSTyêu cầu, nhưng máy chủ muốn chuyển hướng nó đến https. Điều đó xảy ra là gì, hóa ra là hành vi nút http gửi yêu cầu chuyển hướng (tiếp theo) trongGET phương thức không đúng như vậy.

Những gì tôi đã làm là thay đổi url của mình thành https://giao thức và nó hoạt động.


Đây là một ví dụ rất cụ thể về chuyển hướng và sẽ không làm bất cứ điều gì đối với các liên kết tinyurls hoặc bit.ly.
Dan Dascalescu

-1

Có thể có một chút về một bài đăng chiêu thức ở đây, nhưng ...

đây là một chức năng theo sau tối đa 10 chuyển hướng và phát hiện các vòng chuyển hướng vô hạn. cũng phân tích kết quả thành JSON

Lưu ý - sử dụng trình trợ giúp gọi lại (hiển thị ở cuối bài đăng này)

(TLDR; bản demo hoạt động đầy đủ trong ngữ cảnh tại đây hoặc phiên bản phối lại tại đây )

function getJSON(url,cb){

    var callback=errBack(cb);
    //var callback=errBack(cb,undefined,false);//replace previous line with this to turn off logging

    if (typeof url!=='string') {
        return callback.error("getJSON:expecting url as string");
    }

    if (typeof cb!=='function') {
        return callback.error("getJSON:expecting cb as function");
    }

    var redirs = [url],
    fetch = function(u){
        callback.info("hitting:"+u);
        https.get(u, function(res){
            var body = [];
            callback.info({statusCode:res.statusCode});
            if ([301,302].indexOf(res.statusCode)>=0) {
                if (redirs.length>10) {
                    return callback.error("excessive 301/302 redirects detected");
                } else {
                    if (redirs.indexOf(res.headers.location)<0) {
                        redirs.push(res.headers.location);
                        return fetch(res.headers.location);
                    } else {
                        return callback.error("301/302 redirect loop detected");
                    }
                }
            } else {
              res.on('data', function(chunk){
                  body.push(chunk);
                  callback.info({onData:{chunkSize:chunk.length,chunks:body.length}});
              });
              res.on('end', function(){
                  try {
                      // convert to a single buffer
                      var json = Buffer.concat(body);
                      console.info({onEnd:{chunks:body.length,bodyLength:body.length}});

                      // parse the buffer as json
                      return callback.result(JSON.parse(json),json);
                  } catch (err) {

                      console.error("exception in getJSON.fetch:",err.message||err);

                      if (json.length>32) {
                        console.error("json==>|"+json.toString('utf-8').substr(0,32)+"|<=== ... (+"+(json.length-32)+" more bytes of json)");
                      } else {
                          console.error("json==>|"+json.toString('utf-8')+"|<=== json");
                      }

                      return callback.error(err,undefined,json);
                  }
              });
            }
        });
    };
    fetch(url);   
}

Lưu ý - sử dụng trình trợ giúp gọi lại (hiển thị bên dưới)

bạn có thể dán nó vào bảng điều khiển nút và nó sẽ chạy như cũ.

(hoặc để biết bản demo hoạt động đầy đủ trong ngữ cảnh, hãy xem tại đây )

var 

fs      = require('fs'),
https   = require('https');

function errBack (cb,THIS,logger) {

   var 
   self,
   EB=function(fn,r,e){
       if (logger===false) {
           fn.log=fn.info=fn.warn=fn.errlog=function(){};       
       } else {
           fn.log        = logger?logger.log   : console.log.bind(console);
           fn.info       = logger?logger.info  : console.info.bind(console);
           fn.warn       = logger?logger.warn  : console.warn.bind(console);
           fn.errlog     = logger?logger.error : console.error.bind(console);
       }
       fn.result=r;
       fn.error=e;
       return (self=fn);
   };


   if (typeof cb==='function') {
       return EB(

            logger===false // optimization when not logging - don't log errors
            ?   function(err){
                   if (err) {
                      cb (err);
                     return true;
                   }
                   return false;
               }

            :  function(err){
                   if (err) {
                      self.errlog(err);
                      cb (err);
                     return true;
                   }
                   return false;
               },

           function () {
               return cb.apply (THIS,Array.prototype.concat.apply([undefined],arguments));
           },
           function (err) {
               return cb.apply (THIS,Array.prototype.concat.apply([typeof err==='string'?new Error(err):err],arguments));
           }
       );
   } else {

       return EB(

           function(err){
               if (err) {
                   if (typeof err ==='object' && err instanceof Error) {
                       throw err;
                   } else {
                       throw new Error(err);
                   }
                   return true;//redundant due to throw, but anyway.
               }
               return false;
           },

           logger===false
              ? self.log //optimization :resolves to noop when logger==false
              : function () {
                   self.info("ignoring returned arguments:",Array.prototype.concat.apply([],arguments));
           },

           function (err) {
               throw typeof err==='string'?new Error(err):err;
           }
       );
   }
}

function getJSON(url,cb){

    var callback=errBack(cb);

    if (typeof url!=='string') {
        return callback.error("getJSON:expecting url as string");
    }

    if (typeof cb!=='function') {
        return callback.error("getJSON:expecting cb as function");
    }

    var redirs = [url],
    fetch = function(u){
        callback.info("hitting:"+u);
        https.get(u, function(res){
            var body = [];
            callback.info({statusCode:res.statusCode});
            if ([301,302].indexOf(res.statusCode)>=0) {
                if (redirs.length>10) {
                    return callback.error("excessive 302 redirects detected");
                } else {
                    if (redirs.indexOf(res.headers.location)<0) {
                        redirs.push(res.headers.location);
                        return fetch(res.headers.location);
                    } else {
                        return callback.error("302 redirect loop detected");
                    }
                }
            } else {
              res.on('data', function(chunk){
                  body.push(chunk);
                  console.info({onData:{chunkSize:chunk.length,chunks:body.length}});
              });
              res.on('end', function(){
                  try {
                      // convert to a single buffer
                      var json = Buffer.concat(body);
                      callback.info({onEnd:{chunks:body.length,bodyLength:body.length}});

                      // parse the buffer as json
                      return callback.result(JSON.parse(json),json);
                  } catch (err) {
                      // read with "bypass refetch" option
                      console.error("exception in getJSON.fetch:",err.message||err);

                      if (json.length>32) {
                        console.error("json==>|"+json.toString('utf-8').substr(0,32)+"|<=== ... (+"+(json.length-32)+" more bytes of json)");
                      } else {
                          console.error("json==>|"+json.toString('utf-8')+"|<=== json");
                      }

                      return callback.error(err,undefined,json);
                  }
              });
            }
        });
    };
    fetch(url);   
}

var TLDs,TLDs_fallback = "com.org.tech.net.biz.info.code.ac.ad.ae.af.ag.ai.al.am.ao.aq.ar.as.at.au.aw.ax.az.ba.bb.bd.be.bf.bg.bh.bi.bj.bm.bn.bo.br.bs.bt.bv.bw.by.bz.ca.cc.cd.cf.cg.ch.ci.ck.cl.cm.cn.co.cr.cu.cv.cw.cx.cy.cz.de.dj.dk.dm.do.dz.ec.ee.eg.er.es.et.eu.fi.fj.fk.fm.fo.fr.ga.gb.gd.ge.gf.gg.gh.gi.gl.gm.gn.gp.gq.gr.gs.gt.gu.gw.gy.hk.hm.hn.hr.ht.hu.id.ie.il.im.in.io.iq.ir.is.it.je.jm.jo.jp.ke.kg.kh.ki.km.kn.kp.kr.kw.ky.kz.la.lb.lc.li.lk.lr.ls.lt.lu.lv.ly.ma.mc.md.me.mg.mh.mk.ml.mm.mn.mo.mp.mq.mr.ms.mt.mu.mv.mw.mx.my.mz.na.nc.ne.nf.ng.ni.nl.no.np.nr.nu.nz.om.pa.pe.pf.pg.ph.pk.pl.pm.pn.pr.ps.pt.pw.py.qa.re.ro.rs.ru.rw.sa.sb.sc.sd.se.sg.sh.si.sj.sk.sl.sm.sn.so.sr.st.su.sv.sx.sy.sz.tc.td.tf.tg.th.tj.tk.tl.tm.tn.to.tr.tt.tv.tw.tz.ua.ug.uk.us.uy.uz.va.vc.ve.vg.vi.vn.vu.wf.ws.ye.yt.za.zm.zw".split(".");
var TLD_url = "https://gitcdn.xyz/repo/umpirsky/tld-list/master/data/en/tld.json";
var TLD_cache = "./tld.json";
var TLD_refresh_msec = 15 * 24 * 60 * 60 * 1000;
var TLD_last_msec;
var TLD_default_filter=function(dom){return dom.substr(0,3)!="xn-"};


function getTLDs(cb,filter_func){

    if (typeof cb!=='function') return TLDs;

    var 
    read,fetch,
    CB_WRAP=function(tlds){
        return cb(
            filter_func===false
            ? cb(tlds)
            : tlds.filter(
                typeof filter_func==='function'
                 ? filter_func
                 : TLD_default_filter)
            );
    },
    check_mtime = function(mtime) {
       if (Date.now()-mtime > TLD_refresh_msec) {
           return fetch();
       } 
       if (TLDs) return CB_WRAP (TLDs);
       return read();
    };

    fetch = function(){

        getJSON(TLD_url,function(err,data){
            if (err) {
                console.log("exception in getTLDs.fetch:",err.message||err);
                return read(true);      
            } else {
                TLDs=Object.keys(data);

                fs.writeFile(TLD_cache,JSON.stringify(TLDs),function(err){
                    if (err) {
                        // ignore save error, we have the data
                        CB_WRAP(TLDs);
                    } else {
                        // get mmtime for the file we just made
                        fs.stat(TLD_cache,function(err,stats){
                            if (!err && stats) {
                               TLD_last_msec = stats.mtimeMs; 
                            }
                            CB_WRAP(TLDs);    
                        });
                    }
                });
            }
        });
    };

    read=function(bypassFetch) {
        fs.readFile(TLD_cache,'utf-8',function(err,json){

            try {
                if (err) {

                    if (bypassFetch) {
                        // after a http errror, we fallback to hardcoded basic list of tlds
                        // if the disk file is not readable
                        console.log("exception in getTLDs.read.bypassFetch:",err.messsage||err);    

                        throw err;
                    }
                    // if the disk read failed, get the data from the CDN server instead
                    return fetch();
                }

                TLDs=JSON.parse(json);
                if (bypassFetch) {
                    // we need to update stats here as fetch called us directly
                    // instead of being called by check_mtime
                    return fs.stat(TLD_cache,function(err,stats){
                        if (err) return fetch();
                        TLD_last_msec =stats.mtimeMs;
                        return CB_WRAP(TLDs);
                    });
                }

            } catch (e){
                // after JSON error, if we aren't in an http fail situation, refetch from cdn server
                if (!bypassFetch) {
                    return fetch();
                }

                // after a http,disk,or json parse error, we fallback to hardcoded basic list of tlds

                console.log("exception in getTLDs.read:",err.messsage||err);    
                TLDs=TLDs_fallback;
            }

            return CB_WRAP(TLDs);
        });
    };

    if (TLD_last_msec) {
        return check_mtime(TLD_last_msec);
    } else {
        fs.stat(TLD_cache,function(err,stats){
            if (err) return fetch();
            TLD_last_msec =stats.mtimeMs;
            return check_mtime(TLD_last_msec);
        });
    }
}

getTLDs(console.log.bind(console));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.