maaya ishida
I'm system engineer in Tokyo. I join ... - Java Women Group Tokyo - PyLadies Tokyo
今回のシステムフローを確認しよう
Tornado is 何?
(おまけ) WebSocket is 何?
これからお話しする内容は
なるべくわかりやすいように簡単に簡単に書いています。
デキるエンジニアさんから見ると
「そこ違うだろ!」みたいなお叱りを受けるかもしれません。
が、今回はそこはお口にチャックでお願いします。
ソースをダウンロードしよう!
出来る人はローカルへクローンしてみよう!
何言ってるのかわかんないよ!ってひとはzipダウンロードしてみよう!
Javascript
Websocket
Tornado
html
http
http
Cookie
Websocket
Tornado
html
http
http
入力フォームでデータを受け取る
データをget送信
Tornado
html
Javascript
Websocketとhttpを通じて
チャット文字列の送受を行う。
表示はJavascriptがhtmlを作成して実現
Cookie
Javascript
何かしら
の
処理
が
いっぱい
html
http
http
Cookie
httpという通信システムを使って、
ブラウザに画像や文字を
世界中に届けられるようにするもの
今回の
ファイル構成
はこれ!
1. 入力ページを表示し
2. 入力文字列をキャッチして
3. 遷移先ページ情報をクレクレ
4. 前のページの情報を表示する!
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", MainHandler), #--------①
(r"/send/message", SendMessageHandler)
]
tornado.web.Application.__init__(self, handlers)
#--------②
①handlers : 画面に遷移する時のパス名の決定と処理の指定
(r"[URL]", [Handlerの名前])
②Tornadoに「この流れ(台本)でよろしく!」とお知らせ
まずはどんなページを用意したのか、
遷移前にどんな処理をさせたいのかを設定する!
class MainHandler(tornado.web.RequestHandler):
def get(self): #----③
self.render("templates/index.html")
③httpのgetリクエストを受け取り、
templatesディレクトリ内の
index.htmlを
ユーザーに返却するという指示
最初の接続でやりたいことを設定する!
class SendMessageHandler(tornado.web.RequestHandler):
def post(self):
send_message = self.get_argument('text_message')
#-----④
self.render(
"templates/result.html",
message=send_message
)
④httpのpostリクエストを受け取り、
前のページで打った文字列を
result.htmlと共にmessageという名前で
ユーザーに返却するという指示
決定ボタンを押した後にやりたいことを設定する!
def main():
app = Application()
app.listen(8888)
print("接続可能だよ!")
tornado.ioloop.IOLoop.instance().start()
#------⑤
⑤httpの接続を受け入れる
回線番号(ポート番号)を指定し、
TornadoのWebサーバを起動します。
今まで作った全部の処理を起動するスイッチを作ります
<form action="/send/message" method="post">
{% module xsrf_form_html() %} #--------★
<p>文字列を次のページへ文字列を送付する簡単なページ遷移デモをします</p>
<input type="text" name="text_message"/>
<input type="submit" value="go!"/>
</form>
★handlersに指定した処理の中の
どの処理を実行させたいのかを
form actionに記述。httpの送信方法は
form methodへ。
module xsrf_form_html()は
Tornadoでデータを受け取る呪文
表示画面を作ります(index.html抜粋)
<div id="output">
<p>入力した文字列は</p>
<p>{{ message }}</p>
<p>です!</p>
</div>
{% from datetime import datetime %}
<div id="date">
{{ datetime.now() }}
</div>
{{変数名もしくは関数名}}
と記述することで、Tornadoから送付されたデータやPython関数を表示利用することができます
表示画面を作ります(result.html抜粋)
では、
Girls Tech Fes で使った
PyLadies Tokyo Chat のソースを見てみましょう
def __init__(self):
# URLの指定
handlers = [
(ur"/", MainHandler), #--- Sign In ページ表示
(ur"/name/inputname", InputNameHandler), #--- 表示名確定処理
(ur"/name/signup", SignUpHandler), #--- チャットページ表示
(ur"/name/signout", SignOutHandler), #--- サインアウト処理
(ur"/chatsocket", ChatSocketHandler), #--- チャット文字列授受ができるようにする処理
]
settings = dict(
cookie_secret=u"__pyladies_girls_tech_fes_demo_cookie__", #--- Cookie名
template_path=os.path.join(os.path.dirname(__file__), u"templates"),
static_path=os.path.join(os.path.dirname(__file__), u"static"),
login_url=u"/name/inputname", #--- Cookieにデータが存在しなかった場合の処理
autoescape=u"xhtml_escape",
xsrf_cookies=True,
debug=options.debug,
)
tornado.web.Application.__init__(self, handlers, **settings)
class MainHandler(BaseHandler):
# Sign in ページ遷移
@tornado.web.authenticated
def get(self):
self.render(u"index.html", messages=ChatSocketHandler.cache)
class BaseHandler(tornado.web.RequestHandler):
# ページ遷移処理共通。
def get_current_user(self):
username = self.get_secure_cookie(u"chatdemo_user")
# cookieが値を返すとパスできる
# 何もないとlogin_urlへリダイレクトされる
# →login_urlの指定がなければloginなしのシステムとなる
if not username:
return None
return tornado.escape.utf8(username)
class MainHandler(BaseHandler):
# Sign in ページ遷移
@tornado.web.authenticated
def get(self):
self.render(u"index.html", messages=ChatSocketHandler.cache)
class BaseHandler(tornado.web.RequestHandler):
# ページ遷移処理共通。
def get_current_user(self):
username = self.get_secure_cookie(u"chatdemo_user")
# cookieが値を返すとパスできる
# 何もないとlogin_urlへリダイレクトされる
# →login_urlの指定がなければloginなしのシステムとなる
if not username:
return None
return tornado.escape.utf8(username)
# ログインの代わりに名前の入力してもらう
class InputNameHandler(BaseHandler):
def get(self):
self.render(u"inputname.html")
# Cookie削除処理
class SignOutHandler(BaseHandler):
def get(self):
self.clear_cookie(u"chatdemo_user")
self.write(u"登録していたユーザ名を消去しました。
もう一度はじめからやり直しをお願いします。")
# 入力した名前をCookieにセット。チャットページで使えるようにする。
class SignUpHandler(BaseHandler):
def get(self):
self.render(u"login.html")
# <form action="/name/setname" method="post"> の記述により
# inputname.htmlから最初に呼ばれるdefはこちら。
def post(self):
logging.debug(u"xsrf_cookie:" + self.get_argument(u"_xsrf", None))
self.check_xsrf_cookie() #--- Cookieデータの確認
username = self.get_argument(u"username")
logging.debug(u'サインアップメソッドで {0} の名前を受け取りました'.format(username))
if username:
self.set_secure_cookie(u"chatdemo_user", tornado.escape.utf8(username))
self.redirect(u"/")
else:
self.set_status = 403
self.set_header('Content-Type', 'text/html; charset="utf-8"')
self.finish(
'名前が入力されていません。最初からやり直して下さい。')
最後はChatSocketHandler
その前に!
WebSocketについて
すごくざっくり知識を!
Web
サーバ
での
処理
http
http
都度通信通路
の取り直し!
Web
サーバ
での
処理
http
http
WebSocket
#WebSocketを使ってチャット通信を行う
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
waiters = set()
cache = []
cache_size = 200
def open(self): #---接続オープン
ChatSocketHandler.waiters.add(self)
def on_close(self): #----接続クローズ
ChatSocketHandler.waiters.remove(self)
@classmethod
def update_cache(cls, chat):
#---新しい文字列を今まで溜めていたデータにマージする
cls.cache.append(chat)
if len(cls.cache) > cls.cache_size:
#---保存上限値内に情報量が収まっているかチェック
cls.cache = cls.cache[-cls.cache_size:]
@classmethod
def send_updates(cls, chat):
#---新しい文字列をWebSocket接続されているユーザ全員に送付!
logging.info(u"sending message to %d waiters", len(cls.waiters))
print chat
for waiter in cls.waiters:
try:
waiter.write_message(chat)
except:
logging.error(u"Error sending message", exc_info=True)
#--- 異常があった場合の処理
ChatSocketHandler つづき
# 表示メッセージの整形
def on_message(self, message):
logging.info(u"got message %r", message)
parsed = tornado.escape.json_decode(message) #----文字コードの整形
#----ユーザ名をCookieから取得
username = self.get_secure_cookie(u"chatdemo_user")
# htmlに表示させたい文字列をそれぞれ用意する
chat = {
u"id": unicode(uuid.uuid4()),
u"from": unicode(username, encoding=u'utf-8'),
u"body": parsed[u"body"],
}
# htmlセット
chat[u"html"] = tornado.escape.to_basestring(
self.render_string(u"message.html", message=chat))
# 新しい文字列が追加されたらマージして
# WebSocket接続している全ユーザへ送付
ChatSocketHandler.update_cache(chat)
ChatSocketHandler.send_updates(chat)
ChatSocketHandler つづき
By maaya ishida
What's that PyLadies Tokyo Chat System?
I'm system engineer in Tokyo. I join ... - Java Women Group Tokyo - PyLadies Tokyo