|
|
@@ -0,0 +1,187 @@ |
|
|
|
|
|
|
|
# Your first Python API server |
|
|
|
|
|
|
|
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: |
|
|
|
|
|
|
|
* how to install/verify your Python installation |
|
|
|
* how to do a basic package installation with `pip`, [Python's package manager](https://en.wikipedia.org/wiki/Pip_(Python)) |
|
|
|
* a few common Python data structures (notably Python objects, dictionaries, tuples, lists) |
|
|
|
* how to use the [`json` module](https://docs.python.org/2/library/json.html) in Python |
|
|
|
* how to install the [Flask microframework](http://flask.pocoo.org/) for Python |
|
|
|
* how to build and run a basic API in Flask |
|
|
|
|
|
|
|
#### ADDITIONAL RESOURCES |
|
|
|
|
|
|
|
|
|
|
|
## STEP 1: Environment check |
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
### CHECK YOUR VERSION OF PYTHON |
|
|
|
1. open terminal and type `python -V` |
|
|
|
``` |
|
|
|
$ python -V |
|
|
|
Python 2.7.12 :: Anaconda 4.2.0 (64-bit) |
|
|
|
``` |
|
|
|
|
|
|
|
2. execute `pip install flask` |
|
|
|
* if you need to know more about `pip`, please [read more on it](http://www.pythonforbeginners.com/basics/python-pip-usage/) |
|
|
|
|
|
|
|
3. check to see if you have json installed |
|
|
|
|
|
|
|
``` |
|
|
|
$ python |
|
|
|
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 |
|
|
|
Type "help", "copyright", "credits" or "license" for more information. |
|
|
|
Anaconda is brought to you by Continuum Analytics. |
|
|
|
Please check out: http://continuum.io/thanks and https://anaconda.org |
|
|
|
>>> import json |
|
|
|
>>> exit() |
|
|
|
``` |
|
|
|
* **If this returns an error please execute `pip install json`.** |
|
|
|
|
|
|
|
## Step 2: Build Your First Example App |
|
|
|
|
|
|
|
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 out environment. |
|
|
|
|
|
|
|
1. Open your favorite editor and **create the file `app.py`**. |
|
|
|
|
|
|
|
2. Write the following code in your file and **save it**: |
|
|
|
```python |
|
|
|
# -*- coding: utf-8 -*- |
|
|
|
from flask import Flask |
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
app.run() |
|
|
|
``` |
|
|
|
|
|
|
|
3. Go to your command line and type: |
|
|
|
```bash |
|
|
|
$ python app.py |
|
|
|
``` |
|
|
|
You should see the following: |
|
|
|
``` |
|
|
|
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) |
|
|
|
``` |
|
|
|
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!) |
|
|
|
|
|
|
|
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. |
|
|
|
2. The default port is `5000`, which can be changed later |
|
|
|
|
|
|
|
|
|
|
|
## Step 3: Let's dissect what is going on ... |
|
|
|
|
|
|
|
line by line now ... |
|
|
|
|
|
|
|
```python |
|
|
|
from flask import Flask |
|
|
|
``` |
|
|
|
This is a required import to get any basic Flask app running ... don't forget it, and don't linger on it. |
|
|
|
|
|
|
|
```python |
|
|
|
app = Flask(__name__) |
|
|
|
``` |
|
|
|
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, you it's 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!) |
|
|
|
|
|
|
|
```python |
|
|
|
if __name__ == "__main__": |
|
|
|
app.run() |
|
|
|
``` |
|
|
|
|
|
|
|
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). |
|
|
|
|
|
|
|
|
|
|
|
## Step 3a: Let's make a few quick changes |
|
|
|
|
|
|
|
|
|
|
|
### Change the port |
|
|
|
|
|
|
|
Let's change the port to something like `5200` using the `port` parameter if the `Flask.run()` method like this: |
|
|
|
```python |
|
|
|
if __name__ == "__main__": |
|
|
|
app.run(port=5200) |
|
|
|
``` |
|
|
|
### Change the host ipaddress |
|
|
|
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. |
|
|
|
|
|
|
|
Let's put your fixed/assigned/DHCP address in first (remember this _may_ change depending on what network you are on): |
|
|
|
|
|
|
|
* Get the IP of your machine with `ifconfig` (on \*X) or `ipconfig` on (Win\*): |
|
|
|
```bash |
|
|
|
$ ifconfig |
|
|
|
``` |
|
|
|
|
|
|
|
and make the change accordingly: |
|
|
|
```python |
|
|
|
if __name__ == "__main__": |
|
|
|
app.run(_your_ip_address_, port=5200) |
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
### Set DEBUG mode ON |
|
|
|
|
|
|
|
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 ... |
|
|
|
|
|
|
|
And now update your `app.run()` accordingly: |
|
|
|
```python |
|
|
|
app.run(_your_ip_address_, port=5200, debug=True) |
|
|
|
``` |
|
|
|
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**. |
|
|
|
|
|
|
|
## Step 4: Let's make a route |
|
|
|
|
|
|
|
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: |
|
|
|
|
|
|
|
|
|
|
|
### INGREDIENT A: Some JSON response spec |
|
|
|
|
|
|
|
```json |
|
|
|
{ |
|
|
|
"appname": "My awesome app", |
|
|
|
"version": "0.1", |
|
|
|
"creator": "BigBird007" |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
Because Python dictionaries are *very* similar to JSON objects, we can specify the above JSON like this in Python: |
|
|
|
```python |
|
|
|
data = { |
|
|
|
"appname": "My awesome app", |
|
|
|
"version": "0.1", |
|
|
|
"creator": "BigBird007" |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
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 a JSON string (`dumps`) and from a JSON object to a Python object (`loads`) so |
|
|
|
|
|
|
|
### INGREDIENT B: A `/status` route and handler |
|
|
|
|
|
|
|
The syntax for specifying a route is |
|
|
|
```python |
|
|
|
@app.route("your/route/here") |
|
|
|
def the_handler_for_the_route(): |
|
|
|
# your awesome implementation here |
|
|
|
|
|
|
|
# return some cool JSON usually ... |
|
|
|
return |
|
|
|
``` |
|
|
|
|
|
|
|
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 and here: 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.). |
|
|
|
|
|
|
|
Let's finish off our `/status` route ... make sure you place the route somewhere before the `if __name__ ...` line of code: |
|
|
|
|
|
|
|
|
|
|
|
```python |
|
|
|
@app.route("/status") |
|
|
|
def status_endpoint(): |
|
|
|
data = { |
|
|
|
"appname": "My awesome app", |
|
|
|
"version": "0.1", |
|
|
|
"creator": "BigBird007" |
|
|
|
} |
|
|
|
return json.dumps(data) |
|
|
|
``` |
|
|
|
|
|
|
|
Your final code should look like something like the sample file provided in [app.py](./app.py). |