やりたいこと
筆者はさくらレンタルサーバのスタンダードプランを利用しているのですが、
以前、ローカル環境にて遊びでPythonのGUIアプリを作っている際
「既存のデータベース(さくらレンタルサーバ)からデータを取得したい…!」
という場面があり、いざドメインを指定して接続してみたところ、うんともすんとも言わず。
(いや、何かエラーメッセージが出たような気もしますが忘れました。)
調べてみると、セキュリティの観点からさくらのWebサーバ以外からのアクセスはできない仕様らしい。
解決方法
既に同じ悩みを抱えている方々がコミュニティに質問を投げており、回答者の方々が口を揃えて言うのが
「WebサーバにAPIを設置して、API経由でデータベースにクエリを流せばいいじゃん」
とのこと。
実装方法
今回はとにかく手っ取り早く作る為、
APIがGETでパラメータを受け取り、それを元にAPI内部でクエリを作成する、という作りにしました。
ただそれだとあまりにもセキュリティ面がガバガバなので、
申し訳程度にパラメータのエスケープ処理とBasic認証を設けます。
まずローカル側のpythonソース
※対話型で検索条件を入力する形にしていますが、テーブルのカラム名その他構成が分かっていることが前提です。
import urllib.request
import json
import base64
# DBデータ抽出処理
def db_fetch(param):
user = '(Basic認証ユーザー名)'
password = '(Basic認証パスワード)'
basic_auth = base64.b64encode('{}:{}'.format(user, password).encode('utf-8'))
url = "http://(ドメイン名)/WebAPI.php?"+param
# APIへリクエストを送り、結果をjsonで取得
request = urllib.request.Request(url,
headers={'Content-Type': 'application/json',
"Authorization": "Basic " + basic_auth.decode('utf-8')})
try:
with urllib.request.urlopen(request) as response:
body = json.loads(response.read())
except urllib.error.URLError as e:
print(e.reason)
return body
if __name__ == "__main__":
print('検索条件を入力してください。')
print('入力フォーマットは 「カラム名=値」です。')
print('終了する場合は「end」を入力してください。\n')
param = ""
while(True):
str = input('検索条件を入力してください : ')
if(str == 'end'):
print('\n')
param = param[0:-1]
break
param += (str + '&')
result = db_fetch(param)
print(result)
Webサーバー側
<?php
require_once("DB.php");
// 文字コード設定
header('Content-Type: application/json; charset=UTF-8');
// genreが存在するかつgenreが英数字のみで構成されているか
if(isset($_GET["genre"]) && !preg_match('/[^a-z]/', $_GET["genre"])) {
// GETパラメータをエスケープ(xss対策)
$param = htmlspecialchars($_GET["genre"]);
try{
$pdo = new PDO($dsn, $dbuser, $password);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 用途に合わせて書き換えてください。
$query = "($paramを検索条件として使用したSELECT文)"
// クエリ実行
$st = $pdo->prepare($query);
$st->execute();
$results = $st->fetchAll(PDO::FETCH_ASSOC);
}catch (Exception $e) {
exit('エラーがありました。:' . $e->getMessage());
}
}else {
echo "GETパラメータを設定してください";
}
// 配列をjson形式にエンコードして出力。 オプションはご自由に。
$results = json_encode($results);
// 出力
print_r($results);
接続情報
<?php
$host = (DBサーバーのドメイン名);
$dbName = (DB名);
$dbuser = (DBユーザー名);
$password = (DBユーザーパスワード);
$dsn = "mysql:host={$host};dbname={$dbName};charser=utf8";
?>
最後に
つよつよエンジニアの方々からすると、「そんなん猿でもできるわ」レベルかもしれませんが、
ひよっこWebエンジニアの私としては目から鱗の発想だったため「ほぇ~」と思い記事にしました。
今はBasic認証を使っていますが、いずれはトークンによる認証や送信データの暗号化機能など実装してみたいですね。
この記事が同じスキル感のカードキャプターの役に立つことを願っています。