2012-07-04 13 views
11

chciałbym osiągnąć coś takiego:Jak wykonać wykonywanie oprogramowania pośredniego NodeJS/connect po wywołaniu funkcji responsee.end()?

var c = require('connect'); 
var app = c(); 

app.use("/api", function(req, res, next){ 
    console.log("request filter 1"); 
    next(); 
}); 

app.use("/api", function(req, res, next){ 
    console.log("request filter 2"); 
    next(); 
}); 

app.use("/api", function(req, res, next){ 
    console.log("request handler"); 
    res.end("hello"); 
    next(); 
}); 

app.use("/api", function(req, res, next){ 
    console.log("response post processor"); 
    next(); 
}); 
app.listen(3000); 

Kiedy zwijają na adres, otrzymuję wyjątek do konsoli narzekających nagłówków nie może być jedno po wysłaniu który jest wystarczająco fair. Tyle tylko, że nie dotykam obiektu odpowiedzi.

/usr/bin/node app2.js 
request filter 1 
request filter 2 
request handler 
Error: Can't set headers after they are sent. 
    at ServerResponse.OutgoingMessage.setHeader (http.js:644:11) 
    at ServerResponse.res.setHeader (/home/zpace/node_modules/connect/lib/patch.js:59:22) 
    at next (/home/zpace/node_modules/connect/lib/proto.js:153:13) 
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:25:5) 
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15) 
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:19:5) 
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15) 
    at Object.handle (/home/zpace/WebstormProjects/untitled1/app2.js:14:5) 
    at next (/home/zpace/node_modules/connect/lib/proto.js:190:15) 
    at Function.app.handle (/home/zpace/node_modules/connect/lib/proto.js:198:3) 

Debugowanie warstwę NodeJS/connect mam do części, która w jakiś sposób sugeruje, że jeśli nagłówki już wysłane następnie wykonanie obsługi trasy musi zainicjować nagłówki odpowiedzi.

Powstaje pytanie, czy powyższe działanie jest zamierzone (czyli, że wykonanie dowolnego kodu po obsługi trasy zakończy wysyłanie odpowiedzią jest coś zupełnie niewyobrażalnego lub jest to po prostu błąd w connect?

+0

Wykonujesz 'res.end (" hello ")' w kodzie –

+0

tak. przetwarzanie odpowiedzi jest zakończone, odpowiedź jest gotowa do przesłania. a teraz chciałbym na przykład umieścić dziennik lub coś posprzątać. –

+0

Czy znalazłeś sposób, aby to zrobić, ponieważ zadałeś to pytanie? Wygląda na to, że nie ma odpowiedzi na ten wątek i próbuję zrozumieć, dlaczego zespół Connect zaimplementował rzeczy tak, jak oni. – conradkdotcom

Odpowiedz

24

Nie jesteś pewien, czy znalazłeś rozwiązanie.

Jeśli chcesz zaprojektować postprocesor dla cyklu żądania, możesz użyć oprogramowania pośredniego, które nasłuchuje zdarzenia "zakończ" w obiekcie odpowiedzi. Tak:

app.use(function(req, res, next){ 
    res.on('finish', function(){ 
    console.log("Finished " + res.headersSent); // for example 
    console.log("Finished " + res.statusCode); // for example 
    // Do whatever you want 
    }); 
    next(); 
}); 

Funkcja dołączony do „Finish” zdarzenie zostanie wykonane po odpowiedź jest rozpisane (co oznacza, że ​​NodeJS został przekazany nagłówek odpowiedzi i ciało do systemu operacyjnego dla transmisji sieciowej).

Domyślam się, że to musi być to, czego chcesz.

+0

to jest to ... dzięki! –

+2

Zdarzenie 'finish' nie jest uruchamiane w przypadku, gdy obiekt odpowiedzi już uruchomił zdarzenie' close' - zdarza się, gdy gniazdo jest zamknięte, zanim można wysłać odpowiedź. Jeśli chcesz złapać moment, w którym kończy się cykl żądanie/odpowiedź (nawet gdy middleware obsługi błędów wysyła odpowiedź) może lepiej zastąpić 'end' funkcję odpowiedzi: ' var _END = res .koniec; res.end = function() { console.log ("sam koniec odpowiedzi"); _end.apply (to, argumenty); } ' – godness

+0

@godness dobry pomysł. dzięki. – r3wt

2

myślę jest to zły problem z planowaniem.Możesz rozwiązać ten problem w lepszy sposób.Nie wiem, dlaczego masz obsługi żądania i oddzielne procesor żądania, ale pozwala dowiedzieć się, co możemy zrobić.

Tak tak, po odpowiedzi skończyło się, że nie możesz ponownie przeczytać nagłówków:

Więc nie kończ odpowiedzi do momentu wywołania postprocesora.

var isEnd; 

app.use("/*", function(req, res, next){ 
    isEnd = false; 
}) 

app.use("/api", function(req, res, next){ 
    console.log("request handler"); 
    res.write("hello"); 
    isEnd = true; 
    next(); 
}); 

app.use("/api", function(req, res, next){ 
    console.log("response post processor"); 
    if(isEnd) { 
     res.end(); 
    } 
    else next(); 
}); 

Jest to rodzaj rozwiązania, ale może nie być najlepszym rozwiązaniem problemu.

Moim zdaniem jest naprawdę źle, że zadzwonię next() po zakończeniu reakcji. Jeśli potrzebujesz post procesora, dlaczego robisz to w filtrze żądania (lub co to jest). Wywołania funkcji, ale nie next()

Może to:

app.use("/api", function(req, res, next){ 
    console.log("request handler"); 
    res.end("hello"); 
    setTimeout(function(){(postProcessor(req)},0); 
}); 

function postProcessor(req) { 
//doing post process stuff. 
//response not needed because already ended. 
} 

albo to:

app.use("/api", function(req, res, next){ 
    console.log("request handler"); 
    res.writed("hello"); 
    setTimeout(function(){(postProcessor(req)},0); 
    // u cant res.end here because setTimeout. 
    //If you dont use setTimeout you can use res.end here, but not bot function. 
}); 

function postProcessor(req, res) { 
//doing post process stuff. 
res.end(); 
} 

next() nie jest do tego użycia, czego używa.

Mam nadzieję, że moja odpowiedź pomoże, ale wiem, że nie obejmuje wszystkiego, ale twoja odpowiedź też nie jest konkretna.

+0

Należy pamiętać, że warstwa nodejs/connect zostaje wypełniona mieszaną logiką z różnych źródeł: A) kod reprezentujący logikę biznesową i B) kod reprezentujący infrastrukturę. Jeśli więc dwie różne osoby tworzą część biznesową i infrastrukturalną, nie ma tu możliwości rozwoju funkcji współpracy/silnie sprzężonej. Facet logiki biznesowej musi radzić sobie z tym, że działa sam (nieświadomy infrastruktury), kod infrastruktury potrzebuje tego samego: bycie agnostycznym w odniesieniu do kodu warstwy biznesowej. –

+0

I znowu: w ostatniej funkcji ("procesor post") nie dotykam nagłówków, ciał, które zostały zamrożone. –

1

Co za świetne pytanie, aby wypróbować swoją poranną kawę!

Tak więc patrząc przez , jeśli spojrzeć w dół do linii 102, która jest app.handle, który jest kod obsługi stosu oprogramowania pośredniego, zobaczysz, jak działa next().

Wywołanie funkcji next() pozwala sprawdzić, czy res.headerSent ma wartość true, a jeśli tak, zgłasza błąd.

Jeśli Zmienić linia 14 do:

app.use("/api", function(req, res, next){ 
    console.log("request handler"); 
    res.end("hello"); 
    console.log(res); 
    next(); 
}); 

Przekonasz się, że to rzeczywiście ustawia "headersSent" true. Po zakończeniu żądania możemy zobaczyć z kodu next(), że zgłasza błąd z powodu omówionych warunków.

+0

tak, ale wysłanie wniosku niekoniecznie oznacza, że ​​zakończyliśmy cykl żądania. IMHO to tylko jedno możliwe podejście. –

Powiązane problemy