From cffb74f78f27c9a7ae523c9fe61bf42d5c06661d Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Tue, 17 Nov 2020 15:54:55 -0800 Subject: [PATCH 1/3] Improve request payload docs --- docs/deployments/realtime-api/predictors.md | 188 +++++++++++++++++--- 1 file changed, 164 insertions(+), 24 deletions(-) diff --git a/docs/deployments/realtime-api/predictors.md b/docs/deployments/realtime-api/predictors.md index fa51024845..b28888eb36 100644 --- a/docs/deployments/realtime-api/predictors.md +++ b/docs/deployments/realtime-api/predictors.md @@ -492,58 +492,198 @@ If your application requires additional dependencies, you can install additional ## API requests -The type of the `payload` parameter in `predict(self, payload)` can vary based on the content type of the request. The `payload` parameter is parsed according to the `Content-Type` header in the request: +The type of the `payload` parameter in `predict(self, payload)` can vary based on the content type of the request. The `payload` parameter is parsed according to the `Content-Type` header in the request. Here are the parsing rules (see below for examples): 1. For `Content-Type: application/json`, `payload` will be the parsed JSON body. -1. For `Content-Type: multipart/form-data` / `Content-Type: application/x-www-form-urlencoded`, `payload` will be `starlette.datastructures.FormData` (key-value pairs where the value is a `string` for form data, or `starlette.datastructures.UploadFile` for file uploads, see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). +1. For `Content-Type: multipart/form-data` / `Content-Type: application/x-www-form-urlencoded`, `payload` will be `starlette.datastructures.FormData` (key-value pairs where the values are strings for text data, or `starlette.datastructures.UploadFile` for file uploads; see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). 1. For all other `Content-Type` values, `payload` will be the raw `bytes` of the request body. -The `payload` parameter type will be a Python object (*lists*, *dicts*, *numbers*) if a request with a JSON payload is made: +Here are some examples: + +### JSON data + +#### Python request + +```python +import requests + +url = "https://***.amazonaws.com/my-api" +requests.post(url, json={"key": "value"}) +``` + +Or if you have a json string: + +```python +import requests +import json + +url = "https://***.amazonaws.com/my-api" +jsonStr = json.dumps({"key": "value"}) +requests.post(url, data=jsonStr, headers={"Content-Type": "application/json"}) +``` + +#### CURL request ```bash -$ curl http://***.amazonaws.com/my-api \ +$ curl https://***.amazonaws.com/my-api \ -X POST -H "Content-Type: application/json" \ -d '{"key": "value"}' ``` -The `payload` parameter type will be a `bytes` object if a request with a `Content-Type: application/octet-stream` is made: +Or if you have a json file: ```bash -$ curl http://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: application/octet-stream" \ - --data-binary @file.bin +$ curl https://***.amazonaws.com/my-api \ + -X POST -H "Content-Type: application/json" \ + -d @file.json ``` -The `payload` parameter type will be a `bytes` object if a request doesn't have the `Content-Type` set: +#### Reading the payload -```bash -$ curl http://***.amazonaws.com/my-api \ - -X POST -H "Content-Type:" \ - -d @sample.txt +When sending a JSON payload, the `payload` parameter will be a Python object: + +```python +class PythonPredictor: + def __init__(self, config): + pass + + def predict(self, payload): + print(payload["key"]) # prints "value" +``` + +### Binary data + +#### Python request + +```python +import requests +import pickle + +url = "https://***.amazonaws.com/my-api" +pklBytes = pickle.dumps({"key": "value"}) +requests.post(url, data=pklBytes, headers={"Content-Type": "application/octet-stream"}) ``` -The `payload` parameter type will be a `starlette.datastructures.FormData` object if a request with a `Content-Type: multipart/form-data` is made: +#### CURL request ```bash -$ curl http://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: multipart/form-data" \ - -F "fieldName=@file.txt" +$ curl https://***.amazonaws.com/my-api \ + -X POST -H "Content-Type: application/octet-stream" \ + --data-binary @object.pkl ``` -The `payload` parameter type will be a `starlette.datastructures.FormData` object if a request with a `Content-Type: application/x-www-form-urlencoded` is made: +#### Reading the payload + +Since the `Content-Type: application/octet-stream` header is used, the `payload` parameter will be a `bytes` object: + +```python +import pickle + +class PythonPredictor: + def __init__(self, config): + pass + + def predict(self, payload): + obj = pickle.loads(payload) + print(obj["key"]) # prints "value" +``` + +Here's an example if the binary data is an image: + +```python +from PIL import Image +import io + +class PythonPredictor: + def __init__(self, config): + pass + + def predict(self, payload, headers): + img = Image.open(io.BytesIO(payload)) + print(img.size) +``` + +### Form data (files) + +#### Python request + +```python +import requests +import pickle + +url = "https://***.amazonaws.com/my-api" +files = { + "text": open("text.txt", "rb"), + "object": open("object.pkl", "rb"), + "image": open("image.png", "rb"), +} + +requests.post(url, files=files) +``` + +#### CURL request ```bash -$ curl http://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: application/x-www-form-urlencoded" \ - -d @file.txt +$ curl https://***.amazonaws.com/my-api \ + -X POST \ + -F "text=@text.txt" \ + -F "object=@object.pkl" \ + -F "image=@image.png" +``` + +#### Reading the payload + +When sending files via form data, the `payload` parameter will be `starlette.datastructures.FormData` (key-value pairs where the values are `starlette.datastructures.UploadFile`, see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). Either `Content-Type: multipart/form-data` or `Content-Type: application/x-www-form-urlencoded` can be used (typically `Content-Type: multipart/form-data` is used for files, and is the default in the examples above). + +```python +from PIL import Image +import pickle + +class PythonPredictor: + def __init__(self, config): + pass + + def predict(self, payload): + text = payload["text"].file.read() + print(text.decode("utf-8")) # prints the contents of text.txt + + obj = pickle.load(payload["object"].file) + print(obj["key"]) # prints "value" assuming `object.pkl` is a pickled dictionary {"key": "value"} + + img = Image.open(payload["image"].file) + print(img.size) # prints the dimensions of image.png ``` -The `payload` parameter type will be a `starlette.datastructures.FormData` object if no headers are added to the request: +### Form data (text) + +#### Python request + +```python +import requests + +url = "https://***.amazonaws.com/my-api" +requests.post(url, data={"key": "value"}) +``` + +#### CURL request ```bash -$ curl http://***.amazonaws.com/my-api \ +$ curl https://***.amazonaws.com/my-api \ -X POST \ - -d @file.txt + -d "key=value" +``` + +#### Reading the payload + +When sending text via form data, the `payload` parameter will be `starlette.datastructures.FormData` (key-value pairs where the values are strings, see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). Either `Content-Type: multipart/form-data` or `Content-Type: application/x-www-form-urlencoded` can be used (typically `Content-Type: application/x-www-form-urlencoded` is used for text, and is the default in the examples above). + +```python +class PythonPredictor: + def __init__(self, config): + pass + + def predict(self, payload): + print(payload["key"]) # will print "value" ``` ## API responses From 58da7062ef42d2d425bf126996bf9e07df172c67 Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Tue, 17 Nov 2020 16:00:00 -0800 Subject: [PATCH 2/3] Update predictors.md --- docs/deployments/realtime-api/predictors.md | 24 ++++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/deployments/realtime-api/predictors.md b/docs/deployments/realtime-api/predictors.md index b28888eb36..05fa5d65e0 100644 --- a/docs/deployments/realtime-api/predictors.md +++ b/docs/deployments/realtime-api/predictors.md @@ -502,7 +502,9 @@ Here are some examples: ### JSON data -#### Python request +#### Making the request + +##### Python ```python import requests @@ -522,7 +524,7 @@ jsonStr = json.dumps({"key": "value"}) requests.post(url, data=jsonStr, headers={"Content-Type": "application/json"}) ``` -#### CURL request +##### CURL ```bash $ curl https://***.amazonaws.com/my-api \ @@ -553,7 +555,9 @@ class PythonPredictor: ### Binary data -#### Python request +#### Making the request + +##### Python ```python import requests @@ -564,7 +568,7 @@ pklBytes = pickle.dumps({"key": "value"}) requests.post(url, data=pklBytes, headers={"Content-Type": "application/octet-stream"}) ``` -#### CURL request +##### CURL ```bash $ curl https://***.amazonaws.com/my-api \ @@ -605,7 +609,9 @@ class PythonPredictor: ### Form data (files) -#### Python request +#### Making the request + +##### Python ```python import requests @@ -621,7 +627,7 @@ files = { requests.post(url, files=files) ``` -#### CURL request +##### CURL ```bash $ curl https://***.amazonaws.com/my-api \ @@ -656,7 +662,9 @@ class PythonPredictor: ### Form data (text) -#### Python request +#### Making the request + +##### Python ```python import requests @@ -665,7 +673,7 @@ url = "https://***.amazonaws.com/my-api" requests.post(url, data={"key": "value"}) ``` -#### CURL request +##### CURL ```bash $ curl https://***.amazonaws.com/my-api \ From 4950b0327a264b0da3b58b8a7e370054b4816295 Mon Sep 17 00:00:00 2001 From: David Eliahu Date: Wed, 18 Nov 2020 18:08:44 -0800 Subject: [PATCH 3/3] Address comments --- docs/deployments/realtime-api/predictors.md | 86 ++++++++++----------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/deployments/realtime-api/predictors.md b/docs/deployments/realtime-api/predictors.md index 05fa5d65e0..9bc05823b5 100644 --- a/docs/deployments/realtime-api/predictors.md +++ b/docs/deployments/realtime-api/predictors.md @@ -504,6 +504,22 @@ Here are some examples: #### Making the request +##### Curl + +```bash +$ curl https://***.amazonaws.com/my-api \ + -X POST -H "Content-Type: application/json" \ + -d '{"key": "value"}' +``` + +Or if you have a json file: + +```bash +$ curl https://***.amazonaws.com/my-api \ + -X POST -H "Content-Type: application/json" \ + -d @file.json +``` + ##### Python ```python @@ -524,22 +540,6 @@ jsonStr = json.dumps({"key": "value"}) requests.post(url, data=jsonStr, headers={"Content-Type": "application/json"}) ``` -##### CURL - -```bash -$ curl https://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: application/json" \ - -d '{"key": "value"}' -``` - -Or if you have a json file: - -```bash -$ curl https://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: application/json" \ - -d @file.json -``` - #### Reading the payload When sending a JSON payload, the `payload` parameter will be a Python object: @@ -557,6 +557,14 @@ class PythonPredictor: #### Making the request +##### Curl + +```bash +$ curl https://***.amazonaws.com/my-api \ + -X POST -H "Content-Type: application/octet-stream" \ + --data-binary @object.pkl +``` + ##### Python ```python @@ -568,14 +576,6 @@ pklBytes = pickle.dumps({"key": "value"}) requests.post(url, data=pklBytes, headers={"Content-Type": "application/octet-stream"}) ``` -##### CURL - -```bash -$ curl https://***.amazonaws.com/my-api \ - -X POST -H "Content-Type: application/octet-stream" \ - --data-binary @object.pkl -``` - #### Reading the payload Since the `Content-Type: application/octet-stream` header is used, the `payload` parameter will be a `bytes` object: @@ -603,7 +603,7 @@ class PythonPredictor: pass def predict(self, payload, headers): - img = Image.open(io.BytesIO(payload)) + img = Image.open(io.BytesIO(payload)) # read the payload bytes as an image print(img.size) ``` @@ -611,6 +611,16 @@ class PythonPredictor: #### Making the request +##### Curl + +```bash +$ curl https://***.amazonaws.com/my-api \ + -X POST \ + -F "text=@text.txt" \ + -F "object=@object.pkl" \ + -F "image=@image.png" +``` + ##### Python ```python @@ -627,16 +637,6 @@ files = { requests.post(url, files=files) ``` -##### CURL - -```bash -$ curl https://***.amazonaws.com/my-api \ - -X POST \ - -F "text=@text.txt" \ - -F "object=@object.pkl" \ - -F "image=@image.png" -``` - #### Reading the payload When sending files via form data, the `payload` parameter will be `starlette.datastructures.FormData` (key-value pairs where the values are `starlette.datastructures.UploadFile`, see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). Either `Content-Type: multipart/form-data` or `Content-Type: application/x-www-form-urlencoded` can be used (typically `Content-Type: multipart/form-data` is used for files, and is the default in the examples above). @@ -664,6 +664,14 @@ class PythonPredictor: #### Making the request +##### Curl + +```bash +$ curl https://***.amazonaws.com/my-api \ + -X POST \ + -d "key=value" +``` + ##### Python ```python @@ -673,14 +681,6 @@ url = "https://***.amazonaws.com/my-api" requests.post(url, data={"key": "value"}) ``` -##### CURL - -```bash -$ curl https://***.amazonaws.com/my-api \ - -X POST \ - -d "key=value" -``` - #### Reading the payload When sending text via form data, the `payload` parameter will be `starlette.datastructures.FormData` (key-value pairs where the values are strings, see [Starlette's documentation](https://www.starlette.io/requests/#request-files)). Either `Content-Type: multipart/form-data` or `Content-Type: application/x-www-form-urlencoded` can be used (typically `Content-Type: application/x-www-form-urlencoded` is used for text, and is the default in the examples above).