以前作成していたURL短縮サービスを、大幅改造しました。改良したのは以下の3点です。
- 独自ドメインの設定
ドメインが[APPLICATION_NAME].appspot.comでは短縮後のURLが長すぎるので、独自ドメインを設定しました。 - 例外処理の追加
正しいURLが入力されていない場合にサーバの投げる例外ではなく、適切なメッセージを表示するようにしました。 - デザインの適用
簡単なデザインをつけました。前回と比べればはるかに使いやすくなったはずです。
スクリーンショット
こんな感じになりました。スクリーンショットをクリックすると、サービスのURLに移動します。
GAE/Pythonで作るメリット
GAEで運用すれば、サーバのメンテナンスをしなくて良い。個人で提供するサービスで、正直、サーバの面倒までみている暇はありません。またPythonを使うと、メンテナンス性の高い短いソースコードでサービスを開発できます。大規模プロジェクトであれば、きっちり設計して、Javaなどのコンパイル言語をつかったほうが高速で安定したサービスを開発できるとは思いますが、個人でサービスを作成するなら、開発効率の高いPythonが楽だとおもいます。
ソースコード
いつものお約束。ソースコード全文公開です。今回は、メインのpyファイルのほかに、yamlファイルとCSSファイルも掲載します。メインのプログラムは、デザイン部分やらGoogleAnalyticsのロギングコードまで含めて90行を切っています。短いです。
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #!/usr/bin/env python # -*- coding: utf-8 -*- from google.appengine.ext import webapp from google.appengine.ext.webapp import util from google.appengine.ext import db class MyHandler(webapp.RequestHandler): def write(self, str): self.response.out.write(str) class MainHandler(MyHandler): def get(self, pageStr): if pageStr == '' or pageStr[0] == '_': printHeader(self) if pageStr == "_urlerror": self.write(u"""<div class="err">正しいURLを入力してください。</div>""") self.write(u""" <form method="post" action="/"><input type="hidden" name="phpMyAdmin" value="cfc2644bd9c947213a0141747c2608b0" /> <input type="text" name="url"/> <input type="submit" value="短縮"> </form>""") printFooter(self) else: url = URL.all().filter("num = ", int(pageStr, base = 16)) for u in url: self.redirect(u.url) def post(self, dummy): try: url = URL(url = self.request.get('url')) num = getNextNum() url.put() self.redirect("/thanks/%x" % num) except Exception: self.redirect('_urlerror') def printHeader(self): self.response.out.write(u""" <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <head><title>URL短縮君</title> <link rel="stylesheet" href="/css/style.css" type="text/css"/> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-20245912-3']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </head> <body><a href="/?phpMyAdmin=cfc2644bd9c947213a0141747c2608b0"><img src="/img/logo.png" border="0"/></a>""") def printFooter(self): self.response.out.write(u"""<br><br>Powered By <a href="http://php6.jp/python">python練習帳</a></body></html>""") class ThanksHandler(MyHandler): def get(self, code): printHeader(self) self.write(u"""<br/> <span class="msg">短縮URL:</span> <input type="text" size="20" value="http://a.php6.jp/%(code)s"/> """ % {'code':code}) printFooter(self) def getNextNum(): def procedure(): num = MAXNUM.get_by_key_name('URL') if num is None: num = MAXNUM(key_name = 'URL') num.max_num = num.max_num + 1 num.put() return num.max_num return db.run_in_transaction(procedure) class URL(db.Model): url = db.URLProperty() num = db.IntegerProperty() class MAXNUM(db.Model): max_num = db.IntegerProperty(default = 0) def main(): application = webapp.WSGIApplication([ ('/thanks/(.*)', ThanksHandler), ('/(.*)', MainHandler) ], debug=True) util.run_wsgi_app(application) if __name__ == '__main__': main() |
app.yaml
画像ファイル、CSSファイルは静的なファイルとして提供しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | application: pyshortener version: 1 runtime: python api_version: 1 handlers: - url: /css/(.*\.css) static_files: css/\1 upload: css/(.*\.css) - url: /img/(.*\.png) static_files: img/\1 upload: img/(.*\.png) - url: .* script: main.py |
style.css
1 2 3 | input, .msg, .err{font-size:30px} .err{color:red} body{text-align: center;} |
まとめ
- GAE/Pythonを使うと、個人でも、高負荷に耐えうるサービスを提供できる。
- サーバの面倒をみなくていいので、運用が楽。基本的に無料。
- ソースコード短い。