サンプルAPIへリクエストする時にリクエストヘッダー1件を追加するだけの、簡易APIゲートウェイを自作してみる。
環境
- macOS Ventura
- Rancher Desktop 1.7.0 (Container Engine設定はdockerd)
- Python3, Flask 2.2
サンプルAPIを作る
ディレクトリ
.
├── docker-compose.yml
└── helloapp
├── Dockerfile
├── app.py
└── requirements.txt
ファイル
version: "3.0"
services:
helloapp:
build:
context: helloapp
target: builder
stop_signal: SIGINT
volumes:
- ./helloapp:/app
ports:
- '10600:5000'
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.11-alpine AS builder
WORKDIR /app
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt
COPY . /app
CMD ["flask", "--app", "app.py", "--debug", "run", "--host", "0.0.0.0", "--port", "5000"]
# 調査用
# CMD ["tail", "-f"]
# docker exec -it <container id or name> /bin/sh
# flask --version
flask==2.2.3
from flask import Flask, request, Response
import json
import random
app = Flask(__name__)
@app.route('/<path:path>', methods=['GET', 'POST'])
def hello(path):
params = {}
if request.method == 'GET':
params = request.args
elif request.method == 'POST':
params = request.form
j = json.dumps({
'message': 'Hello world',
'method': request.method,
'path': path,
'params': params,
'headers': request.headers.to_wsgi_list(),
})
s = random.randrange(400, 405, 1)
return Response(response=j, status=s)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
__pycache__
動作確認(1)
- 上記のファイルを配置して
docker-compose up --build
する - ホストOSのブラウザなどで http://localhost:10600/foo/bar/hoge?param1=111¶m2=222 を開く。レスポンスの例は下記。
{"message": "Hello world", "method": "GET", "path": "foo/bar/hoge", "params": {"param1": "111", "param2": "222"}, "headers": [["Host", "localhost:10600"], ["Upgrade-Insecure-Requests", "1"], ["Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"], ["User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15"], ["Accept-Language", "ja"], ["Accept-Encoding", "gzip, deflate"], ["Connection", "keep-alive"]]}
簡易APIゲートウェイを作る
- Flaskでgatewayを作る
- gatewayはリクエストヘッダー
Additional-Header
を追加して、helloapp のAPIにリクエストする - gatewayはhelloappのレスポンスを転記して、レスポンスする
ディレクトリ
.
├── README.txt
├── docker-compose.yml
+ ├── gateway
+ │ ├── Dockerfile
+ │ ├── app.py
+ │ └── requirements.txt
└── helloapp
├── Dockerfile
├── app.py
└── requirements.txt
ファイル
docker-compose.yml を修正して services.gateway
を追加。
version: "3.0"
services:
helloapp:
build:
context: helloapp
target: builder
stop_signal: SIGINT
volumes:
- ./helloapp:/app
ports:
- '10600:5000'
# ホストの http://localhost:10600/any-path
gateway:
build:
context: gateway
target: builder
stop_signal: SIGINT
volumes:
- ./gateway:/app
ports:
- '10601:5000'
# ホストの http://localhost:10601/any-path
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.11-alpine AS builder
WORKDIR /app
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
pip3 install -r requirements.txt
COPY . /app
CMD ["flask", "--app", "app.py", "--debug", "run", "--host", "0.0.0.0", "--port", "5000"]
# 調査用
# CMD ["tail", "-f"]
# docker exec -it <container id or name> /bin/sh
# flask --version
flask==2.2.3
requests
from flask import Flask, request, Response
import requests
app = Flask(__name__)
@app.route('/<path:path>', methods=['GET', 'POST'])
def hello(path):
# url
url = 'http://host.docker.internal:10600/' + path
# headers: copy
reqHeaders = {}
for k in request.headers.keys():
reqHeaders[k] = request.headers[k]
# headers: add
reqHeaders["additional-header"] = "additional-header-value"
# params/data
params = {}
if request.method == 'GET':
params = request.args
res = requests.get(url, params=params, headers=reqHeaders)
elif request.method == 'POST':
data = request.form
res = requests.post(url, data=data, headers=reqHeaders)
resHeaders = {}
for k in res.headers.keys():
resHeaders[k] = res.headers[k]
return Response(response=res.text, headers=resHeaders, status=res.status_code)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
__pycache__
動作確認(2)
- 上記のファイルを配置して
docker-compose up --build
する - ホストOSのブラウザなどで http://localhost:10601/foo/bar/hoge?param1=111¶m2=222 を開く。
- ホストOSのzshなどで、コマンドを実行する
curl -X POST --data-urlencode 'name=太郎' -d 'age=30' http://localhost:10601/ababab/beef
{"message": "Hello world", "method": "GET", "path": "foo/bar/hoge", "params": {"param1": "111", "param2": "222"}, "headers": [["User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15"], ["Accept-Encoding", "gzip, deflate"], ["Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"], ["Connection", "keep-alive"], ["Host", "localhost:10601"], ["Upgrade-Insecure-Requests", "1"], ["Accept-Language", "ja"], ["Additional-Header", "additional-header-value"]]}
{"message": "Hello world", "method": "POST", "path": "ababab/beef", "params": {"name": "\u592a\u90ce", "age": "30"}, "headers": [["User-Agent", "curl/7.86.0"], ["Accept-Encoding", "gzip, deflate"], ["Accept", "*/*"], ["Connection", "keep-alive"], ["Host", "localhost:10601"], ["Content-Length", "30"], ["Content-Type", "application/x-www-form-urlencoded"], ["Additional-Header", "additional-header-value"]]}%
参考リンク
docker/awesome-compose/flask
https://github.com/docker/awesome-compose/tree/master/flask
how to call another webservice api from flask
https://stackoverflow.com/questions/25149493/how-to-call-another-webservice-api-from-flask
(問題) webアプリをdockerで立ててアクセスしたらERR_EMPTY_RESPONSEエラーになった
(原因) containerの外からリクエストが来るのにアプリがlocalhostでLISTENしている
(解決) アプリの設定を0.0.0.0でLISTENするよう変更する
https://qiita.com/amuyikam/items/01a8c16e3ddbcc734a46
Auto reloading python Flask app upon code changes
https://stackoverflow.com/a/40150705
PythonのFlaskで階層自由なURLをルーティングする方法
https://lightgauge.net/language/python/flask-free-route-path
Docker コンテナ内からホストのポートにアクセスする方法まとめ
host.docker.internal
https://gotohayato.com/content/561/
python requests post リクエスト送信時に、header を設定する
https://www.monotalk.xyz/blog/python-requests-post-リクエスト送信時にheader-を設定する/
FlaskでシンプルにContent-Typeをチェックする(@content_type)
https://qiita.com/5zm/items/c3f004291a87cdbce0b9
How do I set response headers in Flask?
https://stackoverflow.com/questions/25860304/how-do-i-set-response-headers-in-flask
flask Response
https://flask.palletsprojects.com/en/2.2.x/api/#flask.Response
flaskでhttpステータスを返却する方法
https://qiita.com/mink0212/items/52e0ebd66bd94e1303c1
The Request Object
https://flask.palletsprojects.com/en/2.2.x/quickstart/#the-request-object
curlコマンドでPOSTする, 様々な形式別メモ
https://weblabo.oscasierra.net/curl-post/
request.get()
https://www.w3schools.com/python/ref_requests_get.asp
requests.Response Object
https://www.w3schools.com/python/ref_requests_response.asp