如何将数据推送到数据基于 SQL 语句的浏览器?

How can I push data to a browser where the data is based on a SQL statement?

我知道有关于此主题的主题,但似乎确实回答了我正在寻找的问题。我以前从未做过任何推送技术,所以这里的一些指导表示赞赏。我了解当某些事情发生变化时如何触发推送到正在侦听的任何浏览器,但我认为这不太适合我正在尝试做的场景。

我们正在重建我们的用户 Web 应用程序,他们可以在其中跟踪货运。我们将允许用户建立自己的搜索以匹配他们的工作方式。例如,有些人会查找计划在今天交付的任何货件,而其他人会查找今天要取货的货件,还有一些人会查找需要安排取货的货件。因此,当他们打开应用程序时,我可以为他们计算今天需要完成的每项工作任务。所以现在我想要的是计数将根据 SQL 重新 运行 而改变。但我不希望用户必须刷新页面才能看到新计数。

如何获得此 SQL 运行 并将当前计数推送到使用此 SQL 的任何浏览器。自动重新运行这个SQL的机制是什么?请记住,我将有 50 个或更多这些独特的 SQL 需要执行并推送计数。

感谢您的指导!

我认为这完全属于 AJAX 的角色。 AJAX 将允许您向服务器发出 GET 和 POST 请求,服务器将处理查询和 return 结果到 JS 函数。冒着 jQuery 传福音的风险,API 使这种事情变得极其简单和标准,你几乎可以在任何你想激活它的事件中进行。

这有一些问题,即客户端输入和 SQL 注入。如果您通过 POST 请求发送任何输入,则必须非常小心地清理所有内容。使用准备好的语句,不要执行查询字符串连接+执行,并且通常假设用户会尝试发送您不希望他们发送的文本。为哪些输入将被成功确认提供一些服务器端边界(例如,如果选项是 "Left" 或 "Right",并且它们给出 "Bottom",要么默认它,要么放弃它)。

  1. 客户端激活请求(定时或事件)
  2. JS 对服务器进行 AJAX 调用(可选参数)
  3. 服务器验证任何输入并处理查询
  4. 服务器发回结果
  5. JS使用结果修改DOM

AJAX 拉取是一种解决方案,尽管存在其他可能更适合并节省资源的解决方案*...

例如,拥有持久的 Websocket 连接将有助于最大限度地降低建立新连接和重复请求(大部分是冗余的)的成本。

因此,您的服务器应该具有较低的工作负载并且您的应用程序需要较少的带宽(如果这些对您很重要)。

即使使用 Websocket 连接只是为了告诉您的客户端何时发送 AJAX 请求有时也可以节省资源。

这是一个快速的 Websocket 推送演示

您可以使用许多不同的 Websocket 解决方案。我使用 the Plezi framework 编写了一个快速演示,因为它非常容易实现,但还有其他方法可以解决这个问题。

Plezi 框架是一个 Ruby 框架,运行 它有自己的 HTTP 和 Websocket 服务器,独立于 Rack。

示例代码包括模型控制器 (DemoController)、根索引页面控制器 (DemoIndex) 和 Websocket 连接控制器 (MyWSController)。

示例代码似乎更长,因为它全部在一个脚本中 - 即使 HTML 页面用作客户端...但它确实非常容易阅读。

搜索要求从客户端发送到web服务器(搜索要求模型的对象ID在0到50之间)。

每次创建(或更新)对象时,都会向所有连接的客户端发送警报,首先运行收集每个客户端的搜索,然后发送任何更新。

其余时间服务器处于休息状态(除了每 45 秒左右 ping 一次,以保持 websocket 连接处于活动状态)。

要查看实际演示,只需将以下代码复制并粘贴到您的 IRB 终端**,然后访问演示页面:

require 'plezi'

class MyWSController
    def on_open
        # save the user data / register them / whatever
        @searches = []
    end
    def on_message data
        # get data from the user
        data = JSON.parse data
        # sanitize data, create search parameters...
        raise "injection attempt: #{data}}" unless data['id'].match(/^\([\d]+\.\.[\d]+\)\z/)
        # save the search
        @searches << data
    end
    def _alert options
        # should check @searches here
        @searches.each do |search|
            if eval(search['id']).include? options[:info][:id]
                # update
                response << {event: 'alert'}.merge(options).to_json
            else
                response << "A message wouldn't be sent for id = #{options[:info][:id]}.\nSaved resources."
            end
        end
    end
end

class DemoController
    def index
        "use POST to post data here"
    end
    # called when a new object is created using POST
    def save
        # ... save data posted in params ... then:
        _send_alert
    end
    # called when an existing object is posted using POST or UPDATE
    def update
        # ... update data posted in params ... then:
        _send_alert
    end
    def demo_update
        _send_alert message: 'info has been entered', info: params.update(id: rand(100), test: 'true')
        "        This is a demo for what happens when a model is updated.\n
        Please Have a look at the Websocket log for the message sent."
    end
    # sends an alert to 
    def _send_alert alert_data
        MyWSController.broadcast :_alert, alert_data
    end
end

class DemoIndex
    def index search = '(0..50)'
        response['content-type'] = 'text/html'
        <<-FINISH
<html>
<head>
<style>
   html, body {height: 100%; width:100%;}
   #output {margin:0 5%; padding: 1em 2em; background-color:#ddd;}
   #output li {margin: 0.5em 0; color: #33f;}
</style>
</head><body>
<h1> Welcome to your Websocket Push Client </h1>
<p>Please open the following link in a <b>new</b> tab or browser, to simulate a model being updated: <a href='#{DemoController.url_for id: :demo_update, name: 'John Smith', email: 'john@gmail.com'}', target='_blank'>update simulation</a></p>
<p>Remember to keep this window open to view how a simulated update effects this page.</p>
<p>You can also open a new client (preferably in a new tab, window or browser) that will search only for id's between 50 and 100: <a href='#{DemoIndex.url_for :alt_search}'>alternative search</a></p>
<p>Websocket messages recieved from the server should appeare below:</p>
<ul id='output'>
</ul>
<script>
var search_1 = JSON.stringify({id: '#{search}'})
output = document.getElementById("output");
websocket = new WebSocket("#{request.base_url 'ws'}/ws"); 
websocket.onmessage = function(e) { output.innerHTML += "<li>" + e.data + "</li>" }
websocket.onopen = function(e) { websocket.send(search_1) }
</script>
</body></html>
FINISH
    end
    def alt_search
        index '(50..100)'
    end
end

listen

route '/model', DemoController
route '/ws', MyWSController
route '/', DemoIndex

exit

要查看此演示,请访问 localhost:3000 并按照屏幕上的说明进行操作。

该演示将指导您打开多个浏览器windows,模拟不同的人访问您的服务器并执行不同的操作。

如您所见,客户端 javascript 和服务器端处理都不是很难编写,而 Websockets 提供了非常高的灵活性并允许更好的资源管理(对于例如,不需要将搜索参数一次又一次地发送到服务器)。


* 适合您应用的最佳解决方案取决于您的具体设计。我只是提供另一种观点。

** ruby 的终端是 运行 使用 bashirb,确保首先使用 gem install plezi[= 安装 plezi 网络16=]