|
|
@@ -0,0 +1,275 @@ |
|
|
|
{ |
|
|
|
"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 |
|
|
|
} |