从Flask传递JSON对象到JavaScript

29

我在获取Flask/Python传递到JavaScript的变量方面遇到了麻烦。

基本上,我从MySQL导入并尝试以三种不同的方式呈现返回结果。

  1. (43.8934276, -103.3690243), (47.052060, -91.639868), (45.1118, -95.0396) 当应用以下代码后,这是我的字典项的输出结果。

new_list = [tuple(d.values()) for d in MySQL_Dict] output = ', '.join('(' + ', '.join(i) + ')' for i in new_list)

这种方法不好,但我添加了它以供详细参考,它完全不符合正确格式。

  1. 我直接将Python字典传递到模板中,其格式如下:

({'lat': '43.8934276', 'lng': '-103.3690243'}, {'lat': '47.052060', 'lng': '-91.639868'}, {'lat': '45.1118', 'lng': '-95.0396'})

然后在模板侧,我尝试了以下JavaScript行:

 var other_coords = {{ MySQL_Dict|tojson }}; 
 var other_coords = {{ MySQL_Dict|tojson|safe }};
 var still_more = JSON.parse(other_coords);

无论是一起使用还是分开使用,都没能奏效。

  1. 我还尝试过使用json_out = json.dumps(My_Dict)从视图中发送字典,但也没有成功。

所有这些努力的目的,是为了从MySQL数据库获取lat、lng坐标到Google Maps API脚本。令我困惑的是,如果我将视图中的json.dump结果直接粘贴到Google Maps脚本中,它可以完美地工作(在引号被移除后),但如果我使用一个变量则会出现问题。有人有建议吗?


渲染后的HTML中期望的输出是什么? - Matt Healy
1
谷歌地图上的标记。 - BrettJ
抱歉,我的意思是你想要什么输出格式(坐标)? - Matt Healy
哦,对不起,它需要看起来像这样,就是一个JavaScript对象,对吗?我以为你可以通过使用JSON.parse(json_object)从JSON中获取它呢。[{lat: 43.8934276, lng: -103.3690243}, {lat: 47.052060, lng: -91.639868}, {lat: 45.1118, lng: -95.0396}] - BrettJ
2个回答

49

看起来目前被接受的答案(由@BrettJ提供)存在潜在的安全漏洞:如果我们传递给JavaScript的对象包含某些带有单引号的字符串,那么这个单引号将不会被json.dumps转义,因此允许注入任意代码到JavaScript中。更好的做法是使用Flask的tojson()模板过滤器,请参见文档,因为它可以正确地转义所有这样的字符(用Unicode代码替换它们)。

这是我的解决方案:

view.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def hello_world():
    user = {'firstname': "Mr.", 'lastname': "My Father's Son"}
    return render_template("index.html", user=user)

if __name__ == '__main__':
    app.run()

索引文件.html

<p>Hello, <span id="username"></span></p>
<script>
    var user = JSON.parse('{{ user | tojson | safe}}');
    document.getElementById('username').innerHTML = user.firstname + " " +
            user.lastname;
</script>

生成的JS代码如下:

var user = JSON.parse('{"firstname": "Mr.", "lastname": "My Father\u0027s Son"}');

这是完全安全的。例如,如果我们使用 json.dumps 驱动的解决方案,则会得到

var user = JSON.parse('{"firstname": "Mr.", "lastname": "My Father's Son"}');

这句话在语法上至少有错误。


5
你的回答对我有用。我改了我的采纳答案,更好的安全性总是等于更好的代码。 - BrettJ
1
{btsdaf} - Teemo
这行代码 JSON.parse('{{ user | tojson | safe}}') 正是我所需要的。单引号也很有帮助! - Elliptica
不需要将 tojson 的输出放入引号中,然后再解析为 JSON。JSON 已经是有效的 Javascript 字面量。 只需使用 var user = {{ user | tojson | safe }}; 即可。 - deceze
@deceze 是的,我没有考虑到这一点。使用您的解决方案,我们是否仍然会遇到使用 json.dumps 方法而不是 | tojson 的漏洞呢?看起来双引号将被 json.dumps 正确转义,因此不可能进行任意代码注入。我是正确的吗?无论如何,似乎我们需要一个新的答案来投票和接受 :) - Ilya V. Schurov
显示剩余2条评论

4
以下简单的示例应该展示了如何从您的字典中获取一个Javascript对象:
views.py
@app.route('/', methods=['GET','POST'])                                         
def index():                                                                    

    points = [{"lat": 43.8934276, "lng": -103.3690243},                         
              {"lat": 47.052060, "lng": -91.639868},                            
              {"lat": 45.1118, "lng": -95.0396}]                                

    return render_template("index.html", points=json.dumps(points)) 

index.html(为简洁起见省略了一些代码)

  function initMap() {                                                      

    var map = new google.maps.Map(document.getElementById('map'), {         
      center: new google.maps.LatLng(43.8934276, -103.3690243),             
      zoom: 4                                                               
    });                                                                     

    var points = JSON.parse('{{ points|safe }}');                           
    var marker;                                                             

    for (var i = 0; i < points.length; i++) {                               

        marker = new google.maps.Marker({                                   
          position: new google.maps.LatLng(points[i].lat, points[i].lng),   
          map: map                                                          
        });                                                                 

    }                                                                       
  }   

谢谢你的回答!好的,那么为什么会发生这种情况呢?如果我按照你说的做,标记不会出现在地图上。在Google Maps中,我只是设置var locations = points;。然而,如果我复制并粘贴你定义的points变量的输出,并直接将var locations = [{lat: 43.8934276,lng: -103.3690243 },{lat: 47.05206,lng: -91.639868}, {lat:45.1118,lng: -95.0396 }]放入地图脚本中,它就完美地工作了!...我很困惑。 - BrettJ
我已经添加了一些用于在地图上渲染标记的Javascript示例。在本地测试过,可以为我渲染标记。希望这能帮到你! - Matt Healy
您真是太棒了!非常感谢。我所有的问题都在于实际的地图脚本本身... 真是令人失望。非常感谢!! - BrettJ
另外,提醒自己和/或未来遇到此问题的人。我的变量中有一个下划线,JS不太欣赏。一旦我改了它,事情就好多了。我从来没有写过JS,所以我不确定这是规则还是语法问题。无论如何,再次感谢Matt。 - BrettJ
这个解决方案存在安全问题(可能会注入任意JavaScript代码),因为缺少对单引号字符的转义。最好使用tojson Flask模板过滤器。有关详细信息,请参见我的答案。 - Ilya V. Schurov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接