|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- {
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Your first Python REST API server\n",
- "\n",
- "In this tutorial you will learn how to make a simple HTTP/REST API in Python 2.7 (or 3.5). When you're done you will know about:\n",
- "\n",
- "* how to install/verify your Python installation \n",
- "* how to do a basic package installation with `pip`, [Python's package manager](https://en.wikipedia.org/wiki/Pip_(Python))\n",
- "* a few common Python data structures (notably Python objects, dictionaries, tuples, lists)\n",
- "* how to use the [`json` module](https://docs.python.org/2/library/json.html) in Python\n",
- "* how to install the [Flask microframework](http://flask.pocoo.org/) for Python\n",
- "* how to build and run a basic API in Flask\n",
- "\n",
- "#### ADDITIONAL RESOURCES\n",
- "\n",
- "\n",
- "|What | Where | Why |\n",
- "|:-------------------:|:----------------------:|:------------------|\n",
- "|A nice primer on Python decorators | [https://realpython.com/blog/python/primer-on-python-decorators/](https://realpython.com/blog/python/primer-on-python-decorators/) | You might want to get a little under the hood on how the routes (and other) decorators work. |\n",
- "|Resource modeling - the RESTful way | [https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling](https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling) | Can't get enough of REST? |\n",
- "|A thorough intro to RESTful APIs | [https://codeplanet.io/principles-good-restful-api-design/](https://codeplanet.io/principles-good-restful-api-design/) | This really nails the full spectrum of concerns in a nice way, but get some coffee before you start. |\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## STEP 1: Environment check\n",
- "\n",
- "You will minimally need Python 2.7.1x for this tutorial. Most if not all of the code will work in 3.5.x as well, so feel free to use that moving forward.\n",
- "\n",
- "### CHECK YOUR VERSION OF PYTHON\n",
- "1. open terminal and type `python -V`\n",
- "```\n",
- " $ python -V\n",
- " Python 2.7.12 :: Anaconda 4.2.0 (64-bit)\n",
- "```\n",
- "\n",
- "2. execute `pip install flask`\n",
- " * if you need to know more about `pip`, please [read more on it](http://www.pythonforbeginners.com/basics/python-pip-usage/). \n",
- " \n",
- "3. check to see if you have the `json` library installed \n",
- " \n",
- "```\n",
- " $ python\n",
- " Python 2.7.12 |Anaconda 4.2.0 (64-bit)| (default, Jun 29 2016, 11:07:13) [MSC v.1500 64 bit (AMD64)] on win32\n",
- " Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n",
- " Anaconda is brought to you by Continuum Analytics.\n",
- " Please check out: http://continuum.io/thanks and https://anaconda.org\n",
- " >>> import json\n",
- " >>> exit()\n",
- "```\n",
- "* **If this returns an error please execute `pip install json`.**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 2: Build Your First Example App\n",
- "\n",
- "We're now going to build our first functional app. It isn't going to do anything yet -- we just want to make sure that we can see that everything is looking good in our environment.\n",
- "\n",
- "1. Open your favorite editor and **create the file `app.py`**.\n",
- "\n",
- "2. Write the following code in your file and **save it**:\n",
- "```python\n",
- " # -*- coding: utf-8 -*-\n",
- " from flask import Flask\n",
- " \n",
- " app = Flask(__name__)\n",
- "\n",
- " if __name__ == \"__main__\":\n",
- " app.run()\n",
- "```\n",
- "\n",
- "3. Go to your command line and type:\n",
- "```bash\n",
- " $ python app.py\n",
- "```\n",
- "You should see the following:\n",
- "```\n",
- " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n",
- "``` \n",
- "4. Hooray! You have your first Flask app, test it by opening your browser and pointing it to [localhost:5000](http://localhost:5000) (**except if** you don't see that last line `* Running ...`, then something is really wrong!)\n",
- "\n",
- " 1. **NOTE:** You haven't implemented any endpoints/routes, so you should get HTTP/404 when you hit [localhost:5000](http://localhost:5000). This is the correct behavior.\n",
- " 2. The default port is `5000`, which can be changed later.\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 3: Let's dissect what is going on ...\n",
- "\n",
- "line by line now ..."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "collapsed": true
- },
- "source": [
- "```python\n",
- "from flask import Flask\n",
- "```\n",
- "This is a required import to get any basic Flask app running ... don't forget it, and don't linger on it."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "```python\n",
- "app = Flask(__name__)\n",
- "```\n",
- "This variable is necessary to initialize the Flask app -- it is the [Application Object](http://flask.pocoo.org/docs/0.11/api/#application-object). We're giving the name `__name__` because we are running this application as a standalone in a single file (`app.py`). You can read more about how Python handles application and module name spaces, but if a file you create is run from the command line, its application `__name__` attribute will be set to `__main__` (unless overridden by the developer, but that is not a typical move in Python, so don't go playing footsy with it until you know why you might want to do so!)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "```python\n",
- "if __name__ == \"__main__\":\n",
- " app.run()\n",
- "```\n",
- "\n",
- "This last bit of code tells Python that if this file is run from the command line (as opposed to being imported as a module), then execute the Flask application method [`run()`](http://flask.pocoo.org/docs/0.11/api/#flask.Flask.run), which starts the HTTP server (read [the docs carefully if you're deploying](http://flask.pocoo.org/docs/0.11/deploying/#deployment) in a production environment)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "\n",
- "## Step 3a: Let's make a few quick changes\n",
- "\n",
- "\n",
- "### Change the port\n",
- "\n",
- "Let's change the port to something like `5200` using the `port` parameter of the `Flask.run()` method like this:\n",
- "```python\n",
- "if __name__ == \"__main__\":\n",
- " app.run(port=5200)\n",
- "```\n",
- "### Change the host ipaddress\n",
- "The default parameter for the `run()` method is indeed the IP of your running server (which defaults to `127.0.0.1`). You can set it to your fixed IP or use `0.0.0.0` to make it externally available.\n",
- "\n",
- "Let's put your fixed/assigned/DHCP address in first (remember this _may_ change depending on what network you are on):\n",
- "\n",
- "* Get the IP of your machine with `ifconfig` (on \\*X) or `ipconfig` on (Win\\*):\n",
- "```bash\n",
- " $ ifconfig \n",
- "```\n",
- "\n",
- "and make the change accordingly:\n",
- "```python\n",
- "if __name__ == \"__main__\":\n",
- " app.run(_your_ip_address_, port=5200)\n",
- "```\n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Set DEBUG mode ON\n",
- "\n",
- "Finally, let's make sure we run in debug mode so if anything crazy happens, we can find out what ... using the `debug` parameter makes that a snap ...\n",
- "\n",
- "And now update your `app.run()` accordingly:\n",
- "```python \n",
- " app.run(_your_ip_address_, port=5200, debug=True)\n",
- "```\n",
- "With this, others will be able to see your server (on the same network) and you will be able to make changes to your code and **dynamically update your code without stopping and starting the server**."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Step 4: Let's make a route \n",
- "\n",
- "In our final step, we're going to implement something interesting (well sort of anyway). Let's say we have an application which will respond to `/status` by returning the following json:\n",
- "\n",
- "\n",
- "### INGREDIENT A: Some JSON response spec\n",
- "\n",
- "```json\n",
- "{\n",
- "\t\"appname\": \"My awesome app\",\n",
- "\t\"version\": \"0.1\",\n",
- "\t\"creator\": \"BigBird007\"\n",
- "}\n",
- "```\n",
- "\n",
- "Because Python dictionaries are *very* similar to JSON objects, we can specify the above JSON like this in Python:\n",
- "```python\n",
- "data = {\n",
- "\t\"appname\": \"My awesome app\",\n",
- "\t\"version\": \"0.1\",\n",
- "\t\"creator\": \"BigBird007\"\n",
- "}\n",
- "```\n",
- "\n",
- "To convert this Python dictionary to JSON so we can return it over the wire, we use the `json` library. The two methods you should just remember are `json.dumps(a_python_object)` and `json.loads(a_json_string)` ... these two methods allow you to go back and forth from Python to a JSON string (`dumps`) and from a JSON object (as a string) back to a Python object (`loads`). \n",
- "\n",
- "### INGREDIENT B: A `/status` route and handler \n",
- "\n",
- "The syntax for specifying a route is\n",
- "```python\n",
- "@app.route(\"your/route/here\")\n",
- "def the_handler_for_the_route():\n",
- " # your awesome implementation here\n",
- " \n",
- " # return some cool JSON usually ... \n",
- " return\n",
- "```\n",
- "\n",
- "This pattern will be used over and over in your code, study it and remember it. To learn how to use this pattern to specify things like your `GET` or `POST` handlers, read the docs further here: [http://flask.pocoo.org/docs/0.11/api/#flask.Flask.route](http://flask.pocoo.org/docs/0.11/api/#flask.Flask.route) and here: [http://flask.pocoo.org/docs/0.11/api/#url-route-registrations](http://flask.pocoo.org/docs/0.11/api/#url-route-registrations). Everything you'd want to know will be there (including handling variable parts of your route, defaults, method handling, etc.).\n",
- "\n",
- "Let's finish off our `/status` route ... make sure you place the route somewhere before the `if __name__ ...` line of code:\n",
- "\n",
- "\n",
- "```python\n",
- "@app.route(\"/status\")\n",
- "def status_endpoint():\n",
- " data = {\n",
- " \"appname\": \"My awesome app\",\n",
- " \"version\": \"0.1\",\n",
- " \"creator\": \"BigBird007\"\n",
- " }\n",
- " return json.dumps(data)\n",
- "```"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Your final code should look like something like the sample file provided in [app.py](./app.py)."
- ]
- }
- ],
- "metadata": {
- "anaconda-cloud": {},
- "kernelspec": {
- "display_name": "Python [conda root]",
- "language": "python",
- "name": "conda-root-py"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 2
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.12"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 1
- }
|