如何在CherryPy

时间:2015-06-09 10:44:11

标签: python recaptcha cherrypy wtforms flask-wtforms

我想在我的WTForms中使用RecaptchaField(),例如:

class MyForm(Form):
    name = StringField('Your name', [InputRequired(message='Please enter your name!')])
    recaptcha = RecaptchaField() # RecaptchaField as provided by Flask-WTF
    submit = SubmitField('Send')

由于我使用的是CherryPy,我不确定是否应该使用Flask-WTF,因为Flask本身就是一个完整的框架。我想知道我是否可以在CherryPy解决方案中使用Flask-WTF的Recaptcha功能。我尝试了以下方法:

from wtforms import StringField
from wtforms.validators import InputReqired
from flask.ext.wtf import Form
from flask.ext.wtf.recaptcha import RecaptchaField

# ...

form = MyForm() # Somewhere in my code

this Example here。我得到以下例外:

RuntimeError: working outside of application context

这意味着我必须在考虑正确的背景下正确设置Flask应用程序......这就是我开始怀疑我是否采取了正确的方法。除了在我的CherryPy应用程序中设置一个单独的Flask应用程序之外没有其他办法吗?

1 个答案:

答案 0 :(得分:1)

我的回答主要与CherryPy和问题的reCaptcha部分有关。在我看来,使用WTForms和其他类似的库会导致设计问题,当谈到类似MVC的设计时,您将视图和控制器分散到您自己的代码和WTForms代码/配置中。当你在一个地方管理一件事情时,事情很简单。因此,我建议使用Jinja2之类的模板引擎作为视图(您可以为重复的表单元素创建一个宏)并在控制器中使用输入验证库,如voluptuous(您可以对表单使用相同的模式)和API验证)。

如果你无法避免使用WTForms,只需抓住validateRecaptcha并将其转为WTForms custom validator

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import urllib
import json

import cherrypy
import voluptuous as volu


config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  },
  '/' : {
    'recaptcha' : {
        # By default, all keys work on localhost
       'siteKey' : '6LeYIbsSAAAAACRPIllxA7wvXjIE411PfdB2gt2J',
       'secret'  : '6LeYIbsSAAAAAJezaIq3Ft_hSTo0YtyeFG-JgRtu'
     }
  }
}

def validateRecaptcha(value):
  '''https://developers.google.com/recaptcha/docs/verify'''

  if 'g-recaptcha-response' not in cherrypy.request.params:
    raise volu.Invalid('Recaptcha response is missing') 

  payload = urllib.urlencode({
    'secret'   : cherrypy.request.config['recaptcha']['secret'],
    'remoteip' : cherrypy.request.headers['remote-addr'],
    'response' : cherrypy.request.params['g-recaptcha-response']
  })

  url      = 'https://www.google.com/recaptcha/api/siteverify'
  response = json.load(urllib.urlopen(url, payload))
  if not response['success']:
    cherrypy.log(str(response))
    raise volu.Invalid(response['error-codes'])


class App:

  @cherrypy.expose
  def index(self, **kwargs):
    form = dict(form = {'value': ''}, errors = '')

    if cherrypy.request.method == 'POST':
      schema = volu.Schema({
        'value'                : volu.All(unicode, volu.Length(min = 8, max = 16)),
        'g-recaptcha-response' : validateRecaptcha,
      }, required = True, extra = True)
      try:
        kwargs = schema(kwargs)
      except volu.MultipleInvalid as ex:
        form = dict(form = kwargs, errors = {e.path[0] for e in ex.errors})
      else:
        raise cherrypy.HTTPRedirect('#success')

    return '''<!DOCTYPE html>
      <html>
        <head>
          <title>reCAPTCHA demo</title>
          <script src="https://www.google.com/recaptcha/api.js" type="text/javascript"></script>
        </head>
        <body>
          <form action="/" method="POST">
            <div style='border: 1px red solid'>{errors}</div>
            <div>Name</div>
            <input type="text" name="value" value="{form[value]}"/>
            <br/>
            <div class="g-recaptcha" data-sitekey="{0}"></div>
            <br/>
            <input type="submit" value="Submit"/>
          </form>
        </body>
      </html>
    '''.format(cherrypy.request.config['recaptcha']['siteKey'], **form)


if __name__ == '__main__':
  cherrypy.quickstart(App(), '/', config)