Jak zwrócić złożoną odpowiedź JSON za pomocą Node.js?

82

Używając nodejs i express, chciałbym zwrócić jeden lub wiele obiektów (tablic) za pomocą JSON. W poniższym kodzie wyświetlam po jednym obiekcie JSON na raz. Działa, ale to nie jest dokładnie to, czego chcę. Utworzona odpowiedź nie jest prawidłową odpowiedzią JSON, ponieważ mam wiele obiektów.

Zdaję sobie sprawę, że mógłbym po prostu dodać wszystkie obiekty do tablicy i zwrócić tę konkretną tablicę w res.end. Jednak obawiam się, że może to być trudne do przetworzenia i intensywnie korzystające z pamięci.

Jaki jest właściwy sposób na osiągnięcie tego z nodejs? Czy query.each jest właściwą metodą wywołania?

app.get('/users/:email/messages/unread', function(req, res, next) {
    var query = MessageInfo
        .find({ $and: [ { 'email': req.params.email }, { 'hasBeenRead': false } ] });

    res.writeHead(200, { 'Content-Type': 'application/json' });   
    query.each(function(err, msg) {
        if (msg) { 
            res.write(JSON.stringify({ msgId: msg.fileName }));
        } else {
            res.end();
        }
    });
});
Jaskółka oknówka
źródło

Odpowiedzi:

183

W Express 3 możesz użyć bezpośrednio res.json ({foo: bar})

res.json({ msgId: msg.fileName })

Zobacz dokumentację

zobi8225
źródło
9
jak to zrobić bez ekspresu?
Piotrek
@ Ludwik11 res.write(JSON.stringify(foo)). Jeśli foojest duży, być może będziesz musiał go pociąć (stringify, a następnie zapisuj porcje na raz). Prawdopodobnie chcesz również zobaczyć swój nagłówek "Content-Type":"application/json"lub podobny, stosownie do potrzeb .
OJFord
21

Nie wiem, czy to naprawdę wygląda inaczej, ale zamiast iterować po kursorze zapytania, możesz zrobić coś takiego:

query.exec(function (err, results){
  if (err) res.writeHead(500, err.message)
  else if (!results.length) res.writeHead(404);
  else {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.write(JSON.stringify(results.map(function (msg){ return {msgId: msg.fileName}; })));
  }
  res.end();
});
danmactough
źródło
12

[Edytuj] Po przejrzeniu dokumentacji Mongoose wygląda na to, że możesz wysłać wyniki każdego zapytania jako oddzielną porcję; serwer sieciowy używa domyślnie fragmentarycznego kodowania transferu, więc wszystko, co musisz zrobić, to owinąć tablicę wokół elementów, aby uczynić go prawidłowym obiektem JSON.

Z grubsza (niesprawdzone):

app.get('/users/:email/messages/unread', function(req, res, next) {
  var firstItem=true, query=MessageInfo.find(/*...*/);
  res.writeHead(200, {'Content-Type': 'application/json'});
  query.each(function(docs) {
    // Start the JSON array or separate the next element.
    res.write(firstItem ? (firstItem=false,'[') : ',');
    res.write(JSON.stringify({ msgId: msg.fileName }));
  });
  res.end(']'); // End the JSON array and response.
});

Alternatywnie, jak wspomniałeś, możesz po prostu wysłać zawartość tablicy bez zmian. W takim przypadku treść odpowiedzi zostanie zbuforowana i natychmiast wysłana, co może wymagać dużej ilości dodatkowej pamięci (ponad to, co jest wymagane do przechowywania samych wyników) w przypadku dużych zestawów wyników. Na przykład:

// ...
var query = MessageInfo.find(/*...*/);
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(query.map(function(x){ return x.fileName })));
maerics
źródło
To jest dobry pomysł. Jednak wydaje mi się to trochę zepsute. Liczyłem, że nodejs dostarczy coś bardziej eleganckiego.
Martin