RESTful Web Services with Python - Dynamic Languages conference


Published on

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

RESTful Web Services with Python - Dynamic Languages conference

  1. 1. ‎RESTful Web Services with Python<br />Juozas“Joe”Kaziukėnas<br /> / / @juokaz<br />
  2. 2. Who is this guy?<br />JuozasKaziukėnas, Lithuanian<br />You can call me Joe<br />3 years in Edinburgh, UK<br />CEO of Web Species<br />Occasional open source developer<br />Conferences speaker<br />More info in<br />Tweet me @juokaz<br />
  3. 3. What is a RESTful API?<br />
  4. 4. What is an API?<br />Web service is a type of web application<br />Integrate different apps together<br />Mashups<br />Content is in multiple formats<br />JSON<br />XML<br />HTML?<br />Used by other apps mainly<br />Not necessary<br />
  5. 5. REST<br />REpresentational State Transfer<br />99% HTTP, 1% conventions/ideas<br />Stateless<br />Resources<br />Name -> URI<br /><br />Hierarchy<br />Operations<br />Not in the URL, but via HTTP terms<br />Trivial to cache<br />Cache-control, Last-Modified, Expires, Etag<br />
  6. 6. Difference from other web services<br />XML-RPC<br />Single url as a front controller <br />Calling methods on a remote app<br />Most of the actions are via POST<br />SOAP<br />No comments…<br />
  7. 7. Difference from web apps<br />PUT and DELETE HTTP verbs<br />POST and GET only supported by browsers today<br />Create: POST<br />Update: PUT<br />Delete: DELETE<br />View: GET<br />Status codes<br />Not just 404 and 500<br />Meaning of the response without analyzing the returned data<br />And many other headers<br />Web page is not a resource, it’s a representation of a resource<br />
  8. 8. Accept header means the format<br />$ curl -H "Accept: application/html" localhost/products/iphone<html><body>Iphone 5</body></html>$ curl -H "Accept: application/xml" localhost/products/iphone<name>Iphone 5</name>$ curl -H "Accept: application/json“ localhost/products/iphone{‘name’:‘Iphone 5’} $ curl -H "Accept: text/plain" localhost/products/iphoneIphone 5<br />
  9. 9. Doing it wrong<br />
  10. 10. URLs<br />Before we had:<br /><br />Now we have:<br /><br />This is wrong<br />
  11. 11. REST + XML-RPC<br />What is new?<br /><br />Unclear hierarchy<br /><br />Filtering<br /><br />
  12. 12. Wrong<br />Everything is REST now<br />But it’s not<br />Twitter, Facebook, Google all inventing their own @$^&$s<br />“Users are stupid”, ditching standards<br />How to figure out the URIs?<br />To fix this you need…<br />
  13. 13. HATEOAS<br />Hypermedia as the Engine of Application State<br />
  14. 14. The steps to being REST<br />“The Swamp of POX.” You’re using HTTP to make RPC calls. HTTP is only really used as a tunnel.<br />Resources. Rather than making every call to a service endpoint, you have multiple endpoints that are used to represent resources, and you’re talking to them. This is the very beginnings of supporting REST.<br />HTTP Verbs. This is the level that something like Rails gives you out of the box: You interact with these Resources using HTTP verbs, rather than always using POST.<br />Hypermedia Controls. HATEOAS. You’re 100% REST compliant.<br />From Richardson Maturity Model<br />
  15. 15. HATEOAS<br />A requirement for REST<br />One url, everything else is discoverable<br />What to<br />Do next?<br />Do with the resource?<br />Reduces code errors<br />Invalid URLS<br />Invalid state transfers<br />Documentation is not needed<br />Evolution<br />
  16. 16. XML example<br /><appointment><br /> <slot id="1234" doctor="mjones" start="1400" end="1450“/><br /> <patient id="jsmith“/><br /> <link rel="cancel" uri="/slots/1234/appointment"/><br /> <link rel="addTest" uri="/slots/1234/appointment/tests"/><br /> <link rel="updateContactInfo" uri="/patients/jsmith/contactInfo"/><br /></appointment><br />
  17. 17. Accept header means the format<br />Media type<br />Versioning<br />$ curl -H "Accept: application/vnd.demo.v1+json“ localhost/products/iphone{‘name’:‘Iphone 5’}$ curl -H "Accept: application/vnd.demo.v2+json" localhost/products/iphone{‘product_name’:‘Iphone 5’}<br />
  18. 18. Solving the problems with Python<br />
  19. 19. How to create a REST protocol<br />What are the URIs?<br />What's the format?<br />What methods are supported at each URI?<br />What status codes could be returned?<br />By Joe Gregorio<br />
  20. 20. Why Python?<br />Fast, relatively <br />Robust to develop<br />New code live in seconds<br />Huge selection of web frameworks<br />Interfaces with databases, servers etc.<br />APIs do not need much else<br />
  21. 21. Python<br />As long as it’s WSGI it’s OK<br />Simple code<br />Different feature sets<br />Talking here about<br />Django<br />Bottle, similar to Flask<br /><br />Tornado (asynchronous)<br />Render content in the request format<br />
  22. 22. Sample API with Django<br />xml_poll_resource= Collection( <br />queryset= Poll.objects.all(), <br />permitted_methods= ('GET', 'POST', 'PUT', 'DELETE'), <br />expose_fields= ('id', 'question', 'pub_date'), <br /> responder = XMLResponder(paginate_by = 10) <br />) <br />xml_choice_resource= Collection( <br />queryset= Choice.objects.all(), <br />permitted_methods= ('GET',), <br />expose_fields= ('id', 'poll_id', 'choice'), <br /> responder = XMLResponder(paginate_by = 5) <br />)<br />urlpatterns= patterns('', <br />url(r'^xml/polls/(.*?)/?$', xml_poll_resource), <br />url(r'^xml/choices/(.*?)/?$', xml_choice_resource) <br />) <br />
  23. 23. Sample API with Bottle<br />importbottlefrombottleimport route, run <br />@route('/', method='GET') <br />defhomepage(): <br /> return'Hello world!' <br />@route('/events/:id', method='GET') <br />defget_event(id): <br /> returndict(name = 'Event ' + str(id)) <br />bottle.debug(True) <br />run()<br />
  24. 24. Sample API with Tornado<br />class PlaceHandler(tornado.web.RequestHandler):<br />    def get(self, id):<br />        self.write('GETting something')<br /> <br />    def post(self):<br />        self.write('POSTing something')<br />application = tornado.web.Application([<br />    (r"/place", PlaceHandler),<br />    (r"/place/([0-9]+)", PlaceHandler)<br />])<br /> <br />if __name__ == "__main__":<br />    http_server = tornado.httpserver.HTTPServer(application)<br />    tornado.ioloop.IOLoop.instance().start()<br />
  25. 25. MIME types<br />Detect which format to use from Accept header<br />Content negotiation<br />Mimeparse -<br />Use it, works great<br />> mimeparse:best_match(["application/xbel+xml", "text/xml"], "text/*;q=0.5,*/*; q=0.1").> "text/xml"<br />
  26. 26. Mimerender<br />render_xml= lambda message: '<message>%s</message>'%messagerender_json = lambda **args: json.dumps(args)render_html = lambda message: '<html><body>%s</body></html>'%messageurls = ('/(.*)', 'greet')app = web.application(urls, globals())<br />class greet:    @mimerender(default = 'html', html = render_html, xml = render_xml, json= render_json)    def GET(self, name):        if not name:             name = 'world'        return {'message': 'Hello, ' + name + '!'}if __name__ == "__main__":<br />
  27. 27. Return formats<br />Rendering XML takes a lot of code<br />doc =xml.dom.minidom.Document()# Something happensreturn doc.toxml()<br />JSON is as easy as<br />json_dumps(data, sort_keys=True)<br />Maybe allow human readable output<br />json_dumps(data, sort_keys=True, indent=4)<br />JSON is great for Ajax, if not XML brings huge advantages<br />
  28. 28. Which framework to choose<br />Django for existing apps<br />Different sub-frameworks for REST<br />Bottle or similar<br />Small<br />Effective<br />Build your own logic<br />Asynchronous Tornado <br />If API needs to be asynchronous<br />
  29. 29. What’s missing<br />Cache headers<br />Authentication<br />Different types<br />No real REST framework<br />
  30. 30. Applying it practically for Edinburgh Festivals<br />
  31. 31. Applying it practically for Edinburgh Festivals<br />Start at<br />7 summer festivals<br />Built in 100% Python<br />Fast<br />Very stable<br />Some bad decisions<br />Works awesome<br />More info in the blog<br />
  32. 32. Structure<br />
  33. 33. Inside<br />Whole API – 100 LoC of Python code<br />Mainly interacting with the ElasticSearch server<br />Scheduled data imports – main task<br />Nginx as a reverse proxy<br />Supervisor to manage the processes<br />
  34. 34. Conclusion<br />
  35. 35. Conclusion<br />REST is awesome<br />Support different formats<br />Follow HATEOAS<br />Try to create as little as possible custom behaviour<br />Go with light Python code<br />
  36. 36. Keep in touch: / / @juokaz<br />Thank you!<br />We can help you build them, talk to us!<br />