SlideShare a Scribd company logo
⽤用Tornado开发RESTful
                  API运⽤用
                 ⻜飞⻰龙⾮非⻰龙 (http://feilong.me)
                        2012/10/20




12年10月14⽇日星期⽇日
议程

                 • RESTful API简介
                 • ⽤用Tornado开发RESTful API应⽤用
                 • D3status demo APP


12年10月14⽇日星期⽇日
12年10月14⽇日星期⽇日
Service

         Resource   Service   API   Clients




12年10月14⽇日星期⽇日
RESTful API

                           OAuth




                       HTTPS GET/POST

                 App                    Platform
                         JSON/JSONP




12年10月14⽇日星期⽇日
RESTful and HTTP Verbs


            Level 0   GET   POST   PUT   DELETE   PATCH

            Level 1   GET   POST   PUT   DELETE   PATCH

            Level 2   GET   POST   PUT   DELETE   PATCH




12年10月14⽇日星期⽇日
RESTful in Tornado
                 class RequestHandler(object):
                     """Subclass this class and define get() or post() to make a handler.

                     If you want to support more methods than the standard GET/HEAD/POST, you
                     should override the class variable SUPPORTED_METHODS in your
                     RequestHandler class.
                     """
                     SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT",
                                           "OPTIONS")

                    def head(self, *args, **kwargs):
                        raise HTTPError(405)

                    def get(self, *args, **kwargs):
                        raise HTTPError(405)

                    def post(self, *args, **kwargs):
                        raise HTTPError(405)

                    def delete(self, *args, **kwargs):
                        raise HTTPError(405)

                    def patch(self, *args, **kwargs):
                        raise HTTPError(405)

                    def put(self, *args, **kwargs):
                        raise HTTPError(405)

                    def options(self, *args, **kwargs):
                        raise HTTPError(405)

12年10月14⽇日星期⽇日
JSON & JSONP
                 class APIHandler(BaseHandler):

                     def finish(self, chunk=None, notification=None):
                         if chunk is None:
                             chunk = {}

                         if isinstance(chunk, dict):
                             chunk = {"meta": {"code": 200}, "response": chunk}

                             if notification:
                                 chunk["notification"] = {"message": notification}

                         callback = escape.utf8(self.get_argument("callback", None))
                         if callback:
                             self.set_header("Content-Type", "application/x-javascript")

                             if isinstance(chunk, dict):
                                 chunk = escape.json_encode(chunk)

                             self._write_buffer = [callback, "(", chunk, ")"] if chunk else []
                             super(APIHandler, self).finish()
                         else:
                             self.set_header("Content-Type", "application/json; charset=UTF-8")
                             super(APIHandler, self).finish(chunk)




12年10月14⽇日星期⽇日
Exception
                 def write_error(self, status_code, **kwargs):
                     """Override to implement custom error pages."""
                     debug = self.settings.get("debug", False)
                     try:
                          exc_info = kwargs.pop('exc_info')
                          e = exc_info[1]

                        if isinstance(e, exceptions.HTTPAPIError):
                            pass
                        elif isinstance(e, HTTPError):
                            e = exceptions.HTTPAPIError(e.status_code)
                        else:
                            e = exceptions.HTTPAPIError(500)

                         exception = "".join([ln for ln in traceback.format_exception(*exc_info)])

                        if status_code == 500 and not debug:
                            self._send_error_email(exception)

                        if debug:
                            e.response["exception"] = exception

                        self.clear()
                        self.set_status(200) # always return 200 OK for API errors
                        self.set_header("Content-Type", "application/json; charset=UTF-8")
                        self.finish(str(e))
                    except Exception:
                        logging.error(traceback.format_exc())
                        return super(APIHandler, self).write_error(status_code, **kwargs)
12年10月14⽇日星期⽇日
Exception
                 class HTTPAPIError(HTTPError):
                     """API error handling exception

                     API server always returns formatted JSON to client even there is
                     an internal server error.
                     """
                     def __init__(self, status_code=400, error_detail="", error_type="",
                                  notification="", response="", log_message=None, *args):

                         super(HTTPAPIError, self).__init__(int(status_code), log_message, *args)

                         self.error_type = error_type if error_type else 
                             _error_types.get(self.status_code, "unknow_error")
                         self.error_detail = error_detail
                         self.notification = {"message": notification} if notification else {}
                         self.response = response if response else {}

                     def __str__(self):
                         err = {"meta": {"code": self.status_code, "errorType": self.error_type}}
                         self._set_err(err, ["notification", "response"])

                         if self.error_detail:
                             err["meta"]["errorDetail"] = self.error_detail

                         return escape.json_encode(err)



12年10月14⽇日星期⽇日
12年10月14⽇日星期⽇日
12年10月14⽇日星期⽇日
12年10月14⽇日星期⽇日
12年10月14⽇日星期⽇日
⺴⽹网⻚页抓取

                 def update_server_status():
                     url = options.d3_server_status_url
                     req = HTTPRequest(url=url)

                     client = HTTPClient()
                     response = client.fetch(req)
                     if response.code == 200:
                         status = _parse_server_status(response.body)




12年10月14⽇日星期⽇日
⺴⽹网⻚页解析
                 def _parse_server_status(body):
                     status = {}

                     q = pq(etree.fromstring(body))
                     boxes = q(".box") # category box
                     for box in boxes:
                         box_q = pq(etree.fromstring(etree.tostring(box)))
                         category = box_q(".category")[0].text.strip()
                         status[category] = {}
                         servers = box_q(".server")
                         for server in servers:
                             server_q = pq(etree.fromstring(etree.tostring(server)))
                             server_name = server_q(".server-name")[0].text.strip().replace(" ", "")
                             if server_name:
                                 status_icon = server_q(".status-icon")[0]
                                 class_ = status_icon.get("class")
                                 if class_:
                                     st = 0
                                     if "up" in class_:
                                         st = 1
                                     status[category][server_name] = st

                     return status




12年10月14⽇日星期⽇日
@task
                                        任务队列
        def status_notification_task(changed_status):
            status_notifciation(changed_status)



        def status_notifciation(changed_status):
            notifications = {}
            for category, services in changed_status.iteritems():
                for name, st in services.iteritems():
                    # just push notification about game server now
                    if name == "GameServer":
                        notifications[category] = st

            for category, st in notifications.iteritems():
                status = "Available" if st else "Unavailable"

                 offset = 0
                 limit = 200
                 while True:
                     subscribers = load_model("subscribers").get_subscribers(limit, offset)
                     if not subscribers:
                         break

                     for subscribe in subscribers:
                         if category in subscribe.categorys:
                             alert = _trans_alert("Diablo3 %s server status has changed to %s",
                                                   category, status, subscribe.locale)
                             apns_tasks.apns_push_task.delay(subscribe.token, {},
                                                              alert=alert, badge=1,
                                                              sound="default")
                     offset += len(subscribers)

12年10月14⽇日星期⽇日
其它

                 • Apple push notification
                 • i18n
                 • crontab


12年10月14⽇日星期⽇日
相关资源
                 • https://github.com/felinx/d3status
                 • http://www.tornadoweb.org
                 • http://www.tornadoweb.cn
                 • http://tornado.poweredsites.org
                 • http://tornadogists.org
                 • http://en.wikipedia.org/wiki/
                   Representational_state_transfer
12年10月14⽇日星期⽇日
Q&A

                    @⻜飞⻰龙⾮非⻰龙

                 http://feilong.me/




12年10月14⽇日星期⽇日

More Related Content

PPT
Real time server
PDF
Rack Middleware
PDF
8 Minutes On Rack
PDF
Advanced symfony Techniques
PPT
Dance for the puppet master: G6 Tech Talk
PPTX
New in php 7
PDF
Introducing Assetic (NYPHP)
KEY
Phpne august-2012-symfony-components-friends
Real time server
Rack Middleware
8 Minutes On Rack
Advanced symfony Techniques
Dance for the puppet master: G6 Tech Talk
New in php 7
Introducing Assetic (NYPHP)
Phpne august-2012-symfony-components-friends

What's hot (20)

PPTX
Webrtc mojo
PDF
Symfony 2.0 on PHP 5.3
PPTX
Tornado web
PPTX
Tornado - different Web programming
PDF
Perl web frameworks
PPTX
Python, async web frameworks, and MongoDB
PDF
Doctrine MongoDB ODM (PDXPHP)
ODP
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
KEY
Mojo as a_client
ZIP
Nginx + Tornado = 17k req/s
PDF
An Introduction to Tornado
PDF
News of the Symfony2 World
PPTX
Building Web Apps with Express
PDF
Tornadoweb
PDF
Introducing Assetic: Asset Management for PHP 5.3
KEY
Keeping it small: Getting to know the Slim micro framework
PDF
Tornado Web Server Internals
PPTX
Phl mongo-philly-tornado-2011
PDF
Trading with opensource tools, two years later
PDF
HTTP Caching and PHP
Webrtc mojo
Symfony 2.0 on PHP 5.3
Tornado web
Tornado - different Web programming
Perl web frameworks
Python, async web frameworks, and MongoDB
Doctrine MongoDB ODM (PDXPHP)
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
Mojo as a_client
Nginx + Tornado = 17k req/s
An Introduction to Tornado
News of the Symfony2 World
Building Web Apps with Express
Tornadoweb
Introducing Assetic: Asset Management for PHP 5.3
Keeping it small: Getting to know the Slim micro framework
Tornado Web Server Internals
Phl mongo-philly-tornado-2011
Trading with opensource tools, two years later
HTTP Caching and PHP
Ad

Similar to 用Tornado开发RESTful API运用 (20)

PDF
Python magicmethods
PDF
Rest in flask
KEY
Python在豆瓣的应用
PDF
오픈소스 라이브러리 개발기
PDF
Lego: A brick system build by scala
PDF
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development
PDF
PyFoursquare: Python Library for Foursquare
PDF
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
PDF
AOP in Python API design
PDF
Let's read code: python-requests library
PDF
Python client api
PDF
Python tools for testing web services over HTTP
PDF
Python RESTful webservices with Python: Flask and Django solutions
PPTX
REST with Eve and Python
PDF
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
PDF
Django at Scale
PDF
Fabric Python Lib
PDF
Brubeck
PPTX
Automate that
PPTX
Automate That! Scripting Atlassian applications in Python
Python magicmethods
Rest in flask
Python在豆瓣的应用
오픈소스 라이브러리 개발기
Lego: A brick system build by scala
Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development
PyFoursquare: Python Library for Foursquare
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
AOP in Python API design
Let's read code: python-requests library
Python client api
Python tools for testing web services over HTTP
Python RESTful webservices with Python: Flask and Django solutions
REST with Eve and Python
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
Django at Scale
Fabric Python Lib
Brubeck
Automate that
Automate That! Scripting Atlassian applications in Python
Ad

Recently uploaded (20)

PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Electronic commerce courselecture one. Pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
Big Data Technologies - Introduction.pptx
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPT
Teaching material agriculture food technology
PDF
Approach and Philosophy of On baking technology
PDF
Encapsulation theory and applications.pdf
Chapter 3 Spatial Domain Image Processing.pdf
MYSQL Presentation for SQL database connectivity
Understanding_Digital_Forensics_Presentation.pptx
20250228 LYD VKU AI Blended-Learning.pptx
Electronic commerce courselecture one. Pdf
Advanced methodologies resolving dimensionality complications for autism neur...
NewMind AI Weekly Chronicles - August'25 Week I
Big Data Technologies - Introduction.pptx
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Mobile App Security Testing_ A Comprehensive Guide.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Per capita expenditure prediction using model stacking based on satellite ima...
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Building Integrated photovoltaic BIPV_UPV.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Teaching material agriculture food technology
Approach and Philosophy of On baking technology
Encapsulation theory and applications.pdf

用Tornado开发RESTful API运用

  • 1. ⽤用Tornado开发RESTful API运⽤用 ⻜飞⻰龙⾮非⻰龙 (http://feilong.me) 2012/10/20 12年10月14⽇日星期⽇日
  • 2. 议程 • RESTful API简介 • ⽤用Tornado开发RESTful API应⽤用 • D3status demo APP 12年10月14⽇日星期⽇日
  • 4. Service Resource Service API Clients 12年10月14⽇日星期⽇日
  • 5. RESTful API OAuth HTTPS GET/POST App Platform JSON/JSONP 12年10月14⽇日星期⽇日
  • 6. RESTful and HTTP Verbs Level 0 GET POST PUT DELETE PATCH Level 1 GET POST PUT DELETE PATCH Level 2 GET POST PUT DELETE PATCH 12年10月14⽇日星期⽇日
  • 7. RESTful in Tornado class RequestHandler(object): """Subclass this class and define get() or post() to make a handler. If you want to support more methods than the standard GET/HEAD/POST, you should override the class variable SUPPORTED_METHODS in your RequestHandler class. """ SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS") def head(self, *args, **kwargs): raise HTTPError(405) def get(self, *args, **kwargs): raise HTTPError(405) def post(self, *args, **kwargs): raise HTTPError(405) def delete(self, *args, **kwargs): raise HTTPError(405) def patch(self, *args, **kwargs): raise HTTPError(405) def put(self, *args, **kwargs): raise HTTPError(405) def options(self, *args, **kwargs): raise HTTPError(405) 12年10月14⽇日星期⽇日
  • 8. JSON & JSONP class APIHandler(BaseHandler): def finish(self, chunk=None, notification=None): if chunk is None: chunk = {} if isinstance(chunk, dict): chunk = {"meta": {"code": 200}, "response": chunk} if notification: chunk["notification"] = {"message": notification} callback = escape.utf8(self.get_argument("callback", None)) if callback: self.set_header("Content-Type", "application/x-javascript") if isinstance(chunk, dict): chunk = escape.json_encode(chunk) self._write_buffer = [callback, "(", chunk, ")"] if chunk else [] super(APIHandler, self).finish() else: self.set_header("Content-Type", "application/json; charset=UTF-8") super(APIHandler, self).finish(chunk) 12年10月14⽇日星期⽇日
  • 9. Exception def write_error(self, status_code, **kwargs): """Override to implement custom error pages.""" debug = self.settings.get("debug", False) try: exc_info = kwargs.pop('exc_info') e = exc_info[1] if isinstance(e, exceptions.HTTPAPIError): pass elif isinstance(e, HTTPError): e = exceptions.HTTPAPIError(e.status_code) else: e = exceptions.HTTPAPIError(500) exception = "".join([ln for ln in traceback.format_exception(*exc_info)]) if status_code == 500 and not debug: self._send_error_email(exception) if debug: e.response["exception"] = exception self.clear() self.set_status(200) # always return 200 OK for API errors self.set_header("Content-Type", "application/json; charset=UTF-8") self.finish(str(e)) except Exception: logging.error(traceback.format_exc()) return super(APIHandler, self).write_error(status_code, **kwargs) 12年10月14⽇日星期⽇日
  • 10. Exception class HTTPAPIError(HTTPError): """API error handling exception API server always returns formatted JSON to client even there is an internal server error. """ def __init__(self, status_code=400, error_detail="", error_type="", notification="", response="", log_message=None, *args): super(HTTPAPIError, self).__init__(int(status_code), log_message, *args) self.error_type = error_type if error_type else _error_types.get(self.status_code, "unknow_error") self.error_detail = error_detail self.notification = {"message": notification} if notification else {} self.response = response if response else {} def __str__(self): err = {"meta": {"code": self.status_code, "errorType": self.error_type}} self._set_err(err, ["notification", "response"]) if self.error_detail: err["meta"]["errorDetail"] = self.error_detail return escape.json_encode(err) 12年10月14⽇日星期⽇日
  • 15. ⺴⽹网⻚页抓取 def update_server_status(): url = options.d3_server_status_url req = HTTPRequest(url=url) client = HTTPClient() response = client.fetch(req) if response.code == 200: status = _parse_server_status(response.body) 12年10月14⽇日星期⽇日
  • 16. ⺴⽹网⻚页解析 def _parse_server_status(body): status = {} q = pq(etree.fromstring(body)) boxes = q(".box") # category box for box in boxes: box_q = pq(etree.fromstring(etree.tostring(box))) category = box_q(".category")[0].text.strip() status[category] = {} servers = box_q(".server") for server in servers: server_q = pq(etree.fromstring(etree.tostring(server))) server_name = server_q(".server-name")[0].text.strip().replace(" ", "") if server_name: status_icon = server_q(".status-icon")[0] class_ = status_icon.get("class") if class_: st = 0 if "up" in class_: st = 1 status[category][server_name] = st return status 12年10月14⽇日星期⽇日
  • 17. @task 任务队列 def status_notification_task(changed_status): status_notifciation(changed_status) def status_notifciation(changed_status): notifications = {} for category, services in changed_status.iteritems(): for name, st in services.iteritems(): # just push notification about game server now if name == "GameServer": notifications[category] = st for category, st in notifications.iteritems(): status = "Available" if st else "Unavailable" offset = 0 limit = 200 while True: subscribers = load_model("subscribers").get_subscribers(limit, offset) if not subscribers: break for subscribe in subscribers: if category in subscribe.categorys: alert = _trans_alert("Diablo3 %s server status has changed to %s", category, status, subscribe.locale) apns_tasks.apns_push_task.delay(subscribe.token, {}, alert=alert, badge=1, sound="default") offset += len(subscribers) 12年10月14⽇日星期⽇日
  • 18. 其它 • Apple push notification • i18n • crontab 12年10月14⽇日星期⽇日
  • 19. 相关资源 • https://github.com/felinx/d3status • http://www.tornadoweb.org • http://www.tornadoweb.cn • http://tornado.poweredsites.org • http://tornadogists.org • http://en.wikipedia.org/wiki/ Representational_state_transfer 12年10月14⽇日星期⽇日
  • 20. Q&A @⻜飞⻰龙⾮非⻰龙 http://feilong.me/ 12年10月14⽇日星期⽇日