Node.js에서 HTTP 리디렉션을 어떻게 따르나요?


90

노드에서 페이지를 열고 내 응용 프로그램의 내용을 처리하고 싶습니다. 이와 같은 것이 잘 작동하는 것 같습니다.

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
  });

그러나 페이지가 301/302 리디렉션을 반환하면 작동하지 않습니다. 여러 리디렉션이있는 경우 재사용 가능한 방법으로 어떻게해야합니까? 노드 응용 프로그램의 http 응답 처리를보다 쉽게 ​​처리 할 수 ​​있도록 http 위에 래퍼 모듈이 있습니까?

답변:


47

노드 응용 프로그램의 http 응답 처리를보다 쉽게 ​​처리 할 수 ​​있도록 http 위에 래퍼 모듈이 있습니까?

request

요청의 리디렉션 논리


22
살아있는 b'jesus가 내장 http 모듈의이 부분이 아닌 이유는 무엇입니까?!
aaaidan

1
그것은. http.requestAPI 라고 하는 것은 매우 간단합니다.
Raynos

3
어떻게 든 각 리디렉션에 대한 콜백을 가질 수 있습니까? 요청이 통과하는 모든 단일 URL을 저장하고 싶습니다. 문서에서 찾을 수 없습니다.
Ignas 2013-06-26

13
@Raynos, 내장 http모듈 의 request () 메소드는 리디렉션을 따르지 않으므로 내장 http모듈의 일부가 아닙니다 .
gilad mayani

5
request더 이상 사용되지 않습니다.
Ivan Rubinson

113

리디렉션을 따르고 싶지만 기본 제공 HTTP 및 HTTPS 모듈을 계속 사용하려면 https://github.com/follow-redirects/follow-redirects 를 사용하는 것이 좋습니다 .

yarn add follow-redirects
npm install follow-redirects

다음을 교체하기 만하면됩니다.

var http = require('http');

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

... 모든 요청은 자동으로 리디렉션을 따릅니다.

TypeScript를 사용하면 유형을 설치할 수도 있습니다.

npm install @types/follow-redirects

그런 다음

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

공개 :이 모듈을 작성했습니다.


이것은 지금 여기에 있습니다 : github.com/request/request/blob/...
아드리안 린치

1
이것은 request그러한 간단한 작업을 위해 모듈에 20 개 이상의 새로운 종속성을 추가 하는 허용 된 답변 기능보다 낫습니다 . npm 모듈을 가볍게 유지 해주셔서 감사합니다, Oliver! :)
Sainan

s3에서 안전하게 호스팅되는 오디오와 함께 사용할 때 작동하지 않습니다.
thedreamsaver

TypeScript를 사용하여 다음을 npm 설치에 추가합니다. npm install @ types / follow-redirects 그래서 'follow-redirects'에서 가져 오기 {https}를 사용할 수 있습니다. 이것은 환상적이고 간단하며 효율적인 모듈입니다. Merci Olivier!
Louis-Eric Simard

26

최신 정보:

이제 매개 변수 를 var request = require('request');사용하여 모든 리디렉션을 따를 수 있습니다 followAllRedirects.

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

이 완전한 코드입니까? 당신의 VAR는 http하지만, 당신은라는 함수 사용request
jcollum

7
단순히 요구 ( '요청') 할 수 없습니다. 이것은 외부 모듈이며 먼저 다운로드 및 설치해야합니다.- npmjs.com / package / request (npm 설치 요청)
gilad mayani

request더 이상 사용되지 않습니다
Ross MacArthur

18

다음에 따라 다른 요청을합니다 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);

이것은 당신이 HTTP lib에 내장 된 사용하려면, 대답은 따라response.headers.location
VIDAR

6

리디렉션이있는 URL을 가져 오는 데 사용하는 기능은 다음과 같습니다.

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);
        }
    });
}

http.get과 동일하게 작동하지만 리디렉션을 따릅니다.


2

PUT 또는 POST 요청의 경우. statusCode 405 또는 메서드가 허용되지 않는 경우. " request "라이브러리 로이 구현을 시도 하고 언급 된 속성을 추가합니다.
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);
        }
     });
}

2

다음은 일반 노드로 JSON을 다운로드하는 방법이며 패키지가 필요하지 않습니다.

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

당신이있는 경우 https서버를 사용하여 귀하의 URL을 변경 https://프로토콜입니다.

이 문제와 비슷한 문제가 발생했습니다. 내 URL에 http://프로토콜이 있고 POST요청을하고 싶지만 서버가 https. 무슨 일이 일어나고 있는지, 노드 http 동작은 GET경우가 아닌 방법으로 리디렉션 요청 (다음)을 보냅니다 .

내가 한 일은 내 URL을 https://프로토콜 로 변경하는 것이며 작동합니다.


이것은 리디렉션의 매우 특별한 예이며, tinyurls 또는 bit.ly 링크에 대해서는 아무 작업도하지 않습니다.
Dan Dascalescu

-1

아마도 여기에 약간의 강령술 게시물이 있지만 ...

다음은 최대 10 개의 리디렉션을 따르고 무한 리디렉션 루프를 감지하는 함수입니다. 또한 결과를 JSON으로 구문 분석합니다.

참고-콜백 도우미를 사용합니다 (이 게시물의 끝에 표시됨).

(TLDR, 전체 작동 데모 여기 또는 여기 리믹스 버전 )

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);   
}

참고-콜백 도우미를 사용합니다 (아래 참조).

이것을 노드 콘솔에 붙여 넣으면있는 그대로 실행됩니다.

(또는 전체 작업 데모는 여기를 참조하십시오 )

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));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.