Express - 在单个请求中向浏览器发送页面和自定义数据?

Posted

技术标签:

【中文标题】Express - 在单个请求中向浏览器发送页面和自定义数据?【英文标题】:Express - Send a page AND custom data to the browser in a single request? 【发布时间】:2014-02-06 00:22:22 【问题描述】:

如何同时呈现页面并将我的自定义数据传输到浏览器。据我了解,它需要发送两层:第一层是模板,第二层是 JSON 数据。我想通过主干处理这些数据。

据我从教程expressbb app 了解到,交互如下:

    res.render 向浏览器发送页面 当document.ready触发jQuery.get到app.get('/post')app.get('/post', post.allPosts)发送数据到页面

这是三个步骤,如何一步完成?

var visitCard = 
  name: 'John Smit',
  phone: '+78503569987'
;

exports.index = function(req, res, next)
  res.render('index');
  res.send(data: visitCard); 
;

我应该如何在页面上捕获这个变量-document.card

【问题讨论】:

AJAX 是您在不执行整页请求的情况下从现有页面与服务器通信的方式。它代表异步 javascript 和 XML,尽管您很少再看到 XML,因为 JSON 几乎赢得了这场比赛。您可能还会看到称为 XHR(XML HTTP 请求)的 AJAX 请求,例如在 Chrome's dev tools window 中。 @AlexFord 我玩过 JSON,但不知道 Chrome 开发工具中的 XHR 是 XML HTTP 请求。我以前没用过,因为 JSON 赢了 @khaljava 我是说 XHR 只是 AJAX 的另一个术语。它之所以被称为 XML Http Request,是因为它是在 JSON 真正出现之前创建的。 XHR 也适用于 JSON,我们只是从未将其重命名为 JHR:P 【参考方案1】:

使用res.send 代替res.render。它接受任何形式的原始数据:字符串、数组、普通旧对象等。如果是对象或对象数组,它会为您将其序列化为 JSON。

var visitCard = 
  name: 'John Smit',
  phone: '+78503569987'
;

exports.index = function(req, res, next)
  res.send(visitCard; 
;

【讨论】:

res.json。但我想与渲染页面并行发送数据。此解决方案仅适用于数据 我回答后你改变了你的问题。目前尚不清楚您的要求是什么。【参考方案2】:

我创建了自己的小中间件函数,它向 res 对象添加了一个名为 renderWithData 的辅助方法。

app.use(function (req, res, next) 
    res.renderWithData = function (view, model, data) 
        res.render(view, model, function (err, viewString) 
            data.view = viewString;
            res.json(data);
        ); 
    ;
    next();
);

它包含视图名称、视图模型以及您要发送到浏览器的自定义数据。它调用res.render,但传入一个回调函数。这指示 express 将编译后的视图标记作为字符串传递给回调,而不是立即将其传递到响应中。获得视图字符串后,我将其作为data.view 添加到数据对象中。然后我使用res.json 将数据对象发送到浏览器并完成编译视图:)

编辑:

上面的一个警告是,请求需要使用 javascript 进行,因此它不能是整页请求。您需要一个初始请求来拉下包含将发出 ajax 请求的 javascript 的主页。

这对于当用户通过 AJAX 导航到新页面时尝试更改浏览器 URL 和标题的情况非常有用。您可以将新页面的部分视图连同页面标题的一些数据一起发送回浏览器。然后,您的客户端脚本可以将部分视图放在页面上它所属的位置,更新页面标题栏,并在需要时更新 URL。

如果您想将一个完整的 html 文档连同一些初始 JavaScript 数据一起发送到浏览器,那么您需要将该 JavaScript 代码编译到视图本身中。这样做绝对有可能,但我从来没有找到不涉及一些字符串魔法的方法。

例如:

// controller.js
var someData =  message: 'hi' ;
res.render('someView',  data: JSON.stringify(someData) );

// someView.jade
script.
    var someData = !data;

注意:使用!data 代替#data,因为jade 默认会转义HTML,这会将所有引号转换为" 占位符。

一开始看起来很奇怪,但它确实有效。基本上,您在服务器上获取一个 JS 对象,将其转换为字符串,将该字符串呈现到编译后的视图中,然后将其发送到浏览器。当文档最终到达浏览器时,它应该如下所示:

// someSite.com/someView
<script type="text/javascript">
    var someData =  "message": "hi" ;
</script>

希望这是有道理的。如果我要重新创建我原来的辅助方法来减轻第二种情况的痛苦,那么它看起来像这样:

app.use(function (req, res, next) 
    res.renderWithData = function (view, model, data) 
        model.data = JSON.stringify(data);
        res.render(view, model);
    ;
    next();
);

所有这一切都是获取您的自定义数据对象,为您将其字符串化,将其添加到视图的模型中,然后正常渲染视图。现在您可以拨打res.renderWithData('someView', , message: 'hi' );;您只需确保在视图中的某个位置抓取该数据字符串并将其呈现为变量赋值语句。

html
    head
        title Some Page
        script.
            var data = !data;

不会撒谎,这整件事感觉有点恶心,但如果它为您节省了一次额外的服务器之旅,而这正是您所追求的,那么您就需要这样做。也许有人可以想出一些更聪明的方法,但我只是不明白如何才能让数据已经出现在第一次呈现的完整 HTML 文档中。

编辑2:

这是一个工作示例:https://c9.io/chevex/test

您需要拥有一个(免费)Cloud9 帐户才能运行该项目。登录,打开 app.js,然后点击顶部的绿色运行按钮。

【讨论】:

@Alex_Ford 第一个代码根本不起作用 - 我只是在窗口上收到 JSON,但第二个是非常有趣且有效的解决方案。我只是不明白一件事 - 这是一个有用的功能,但它仍然不在 ExpressJS API 中? 我觉得你不太明白。让我试着用清晰的英语来分解它,也许这会有所帮助。第一个示例仅返回 JSON 数据。所以是的,如果您将浏览器指向它,那么您将得到的只是 JSON 数据。第一个示例期望从一个已经运行 JavaScript 代码的现有网页发出请求,因此它可以发出 AJAX 请求。 您的浏览器向您的服务器发出一个请求,并期望返回一个 HTML 文档。如果 JavaScript 数据已经嵌入到浏览器接收的 HTML 文档中,那么您还可以将 JavaScript 数据与该文档一起发送到浏览器的唯一方法。 Express 所做的只是为您提供一种将数据与 HTML 标记相结合的方法。这称为“编译视图”,这一切都发生在服务器上。 Express 然后将其作为完全呈现的 HTML 文档发送回浏览器。如果不发出两次请求或数据已经在标记中,您的浏览器将无法获取 HTML 和 JavaScript 数据。 希望这是有道理的。在第一个示例中,我们将编译后的视图呈现为我们返回的 JSON 数据的字符串属性。在第二个示例中,我们做相反的事情,而是将一些 JSON 数据粘贴到 HTML 标记中。第一个示例是返回一些数据以及部分视图。其次是完整视图以及一些数据。 @Alex_Ford 都清楚了,谢谢。在您回答之前,我已经尝试过第二个示例,但它不起作用,因为我不认为将数据转换为 JSON。【参考方案3】:

查看 Steamer,这是一个专门为此目的而设计的微型模块。

https://github.com/rotundasoftware/steamer

【讨论】:

这很酷。和我从头开始做的事情一样。我得调查一下;也许我会开始使用它而不是我的助手。【参考方案4】:

我的方法是发送一个带有信息的 cookie,然后从客户端使用它。

server.js

const visitCard = 
  name: 'John Smit',
  phone: '+78503569987'
;

router.get('/route', (req, res) => 
  res.cookie('data', JSON.stringify(pollsObj));
  res.render('index');
);

client.js

const getCookie = (name) => 
  const value = "; " + document.cookie;
  const parts = value.split("; " + name + "=");
  if (parts.length === 2) return parts.pop().split(";").shift();
;

const deleteCookie = (name) => 
  document.cookie = name + '=; max-age=0;';
;

const parseObjectFromCookie = (cookie) => 
  const decodedCookie = decodeURIComponent(cookie);
  return JSON.parse(decodedCookie);
;

window.onload = () => 
  let dataCookie = getCookie('data');
  deleteCookie('data');

  if (dataCookie) 
    const data = parseObjectFromCookie(dataCookie);
    // work with data. `data` is equal to `visitCard` from the server

   else 
    // handle data not found
  

演练

从服务器,您在呈现页面之前发送 cookie,因此 cookie 在页面加载时可用。

然后,您从客户端获取包含我找到的解决方案 here 的 cookie 并将其删除。 cookie 的内容存储在我们的常量中。如果 cookie 存在,则将其解析为对象并使用它。请注意,在parseObjectFromCookie 中,您首先必须解码内容,然后将 JSON 解析为对象。

注意事项

如果您以异步方式获取数据,请小心呈现之前发送 cookie。否则,您将收到错误消息,因为res.render() 结束了响应。如果数据获取时间过长,您可以使用另一种解决方案,该解决方案不会将渲染保持那么长时间。另一种方法是从客户端打开一个套接字并发送您在服务器中保存的信息。 See here 表示该方法。

data 可能不是 cookie 的最佳名称,因为您可能会覆盖某些内容。使用对你的目的更有意义的东西。

我在其他任何地方都没有找到此解决方案。我不知道是否由于某种我不知道的原因不推荐使用 cookie。我只是认为它可以工作并检查了它,但我没有在生产中使用它。

【讨论】:

注意这个方法是up to 4Kbytes,所以它不能用于所有目的。 好点 - 我喜欢这个脚本,但有限制,所以使用时要小心:browsercookielimits.squawky.net 此外 4Kb 是 ~ 4000 个字符 extraconversion.com/data-storage-conversion-table/… 谢谢兄弟。我也可以使用这个速记 console.log(JSON.parse(decodeURIComponent(document.cookie).split('=')[1])); 直接访问 cookie;【参考方案5】:

最优雅和最简单的方法是使用渲染引擎(至少对于关注的页面)。例如使用 ejs 引擎

node install ejs -s

在 server.js 上:

let ejs = require('ejs');    
app.set('view engine', 'ejs');

然后将所需的 index.html 页面重命名为 index.ejs 并将其移动到 /views 目录。之后,您可以为该页面创建 API 端点(通过使用 mysql 模块):

app.get('/index/:id', function(req, res)  
    db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => 
    if (error) throw error;
        res.render('index',  title: results[0] ); 
    );
);  

在前端,您将需要发出 GET 请求,例如使用 Axios 或直接通过单击发送请求的模板 index.ejs 页面中的链接:

<a v-bind:href="'/index/' + co.id">Click</a>

其中 co.id 是您要与请求一起发送的 Vue 数据参数值“co”

【讨论】:

以上是关于Express - 在单个请求中向浏览器发送页面和自定义数据?的主要内容,如果未能解决你的问题,请参考以下文章

Ajax的基本使用(以express框架为例)

在单个 Node.js + Express 响应中发送图像和 JSON 数据

如何在 reactjs 中向 nodejs RESTful API 发出 POST 请求

如何从express中的PATCH请求获取请求有效负载

在 axios 发布请求后使用 express 进行重定向

Auth 标头未与 GET 请求一起发送