はじめに
当記事では、C#アプリケーションとデータをやり取りするAPIを作成して実際に動かしてみます。
C#アプリケーションから、ライブラリを使用して直接データベースに接続することも可能ですが、
C#等の高級言語は「リバースエンジニアリングしやすい」といった特徴や、そもそも外部からの接続を許してしまうデータベースはセキュアではないので、基本的にはAPIを通す必要があります。
環境
Visual Studio 2019 Professional
Windows 10 1903
.NET Framework: 4.7.2
XAMPP: 7.3.7
PHP: 7.3.7
Apache: 2.4.39
MariaDB: 10.3.16
データベース
テーブルを新規作成します。
今回は例として以下の構造で作成しました。
PHP側
今回作成するのは以下3つのファイルです。
class.php
database.php
index.php
PHPのフルソースコードは以下の通りです。
今回はあくまで一例として行いますので、複数のデータが見つかった場合や例外エラーの処理は一切していません。
ソースコード
<?phpclassdatabase{public$db;private$db_host="localhost";private$db_user="root";private$db_password="";private$db_name="testdb";publicfunctionconnect(){$this->db=newPDO("mysql:host=".$this->db_host.";dbname=".$this->db_name,$this->db_user,$this->db_password);if(!$this->db){die("Failed to connect");}}}?>
<?phpinclude"database.php";class_main_extendsdatabase{publicfunction__construct(){$this->connect();}publicfunctionInsertData($data){if(empty($data)){die('{"result":"failed"}');}else{$query=$this->db->prepare("INSERT INTO data_table (data) VALUES ('$data')");$query->execute();die('{"result":"success"}');}}publicfunctionGetData(){$query=$this->db->prepare("SELECT * FROM data_table LIMIT 1");$query->execute();$result=$query->fetch(PDO::FETCH_ASSOC);if(isset($result)){$data=$result["data"];die('{"result":"success", "data":"'.$data.'"}');}else{die('{"result":"failed"}');}}}?>
<?php//ini_set( 'display_errors', 1 );//error_reporting(E_ALL);include"class.php";$main=new_main_;if(isset($_POST['type']))$Type=$_POST['type'];if(isset($_POST['data']))$Data=$_POST['data'];if(isset($Type)){switch(strip_tags($Type)){case"t_insert_data":$main->InsertData($Data);break;case"t_get_data":$main->GetData();break;}}?>
C#側
.NET Frameworkを使用します。
Jsonを扱うので、Newtonsoft Jsonを予めNuGetでインストールして下さい。
ソースコード
using(WebClient wc = new WebClient()) { }
としているのは、必ずリソースが破棄されるからです。NameValueCollection
に値を格納し、それを渡す感じです。Encoding.Default.GetString(wc.UploadValues(URL, Values));
でPOSTし、返ってきた値を関数の返り値としています。
一応、WebException
はキャッチします。
usingNewtonsoft.Json;usingSystem;usingSystem.Collections.Generic;usingSystem.Collections.Specialized;usingSystem.Linq;usingSystem.Net;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespacedatatest{classHandler{//ポスト先のURLpublicstaticstringURL{get;set;}//コンストラクタpublicHandler(){}publicstaticstringDoPost(NameValueCollectionValues){try{using(WebClientwc=newWebClient()){returnEncoding.Default.GetString(wc.UploadValues(URL,Values));}}catch(WebExceptione){MessageBox.Show(e.Message);Dictionary<string,object>ERROR=newDictionary<string,object>();HttpWebResponseresponse=(HttpWebResponse)e.Response;switch(response.StatusCode){caseHttpStatusCode.NotFound:ERROR.Add("result","net_error_not_found");break;caseHttpStatusCode.RequestEntityTooLarge:ERROR.Add("result","net_error_request_entry_too_large");break;caseHttpStatusCode.ServiceUnavailable:ERROR.Add("result","net_error_service_unavailable");break;caseHttpStatusCode.Forbidden:ERROR.Add("result","net_error_forbidden");break;default:ERROR.Add("result","net_error_unknown"+Environment.NewLine+e.Message);break;}returnJsonConvert.SerializeObject(ERROR);}}}}
フォームがロードされたタイミングでハンドラーのURLをセットし、NameValueCollection
に各値を格納してHandler.DoPost()
するだけです。
Stringとして帰ってくるので、JObject.Parse()
でパースする必要があります。
usingNewtonsoft.Json.Linq;usingSystem;usingSystem.Collections.Generic;usingSystem.Collections.Specialized;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespacedatatest{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}privatevoidForm1_Load(objectsender,EventArgse){Handler.URL="http://localhost/api/index.php";}privatestaticvoidInsertData(stringdata){varvalues=newNameValueCollection();values["type"]="t_insert_data";values["data"]=data;stringresult=Handler.DoPost(values);if(result!=""){varjobj=JObject.Parse(result);switch((string)jobj["result"]){case"success":MessageBox.Show("Success!");break;case"failed":MessageBox.Show("Failed!");break;default:MessageBox.Show("Unknown error!");break;}}}privatestaticvoidGetData(){varvalues=newNameValueCollection();values["type"]="t_get_data";stringresult=Handler.DoPost(values);MessageBox.Show(result);if(result!=""){varjobj=JObject.Parse(result);switch((string)jobj["result"]){case"success":MessageBox.Show("Success!"+Environment.NewLine+"Data: "+(string)jobj["data"]);break;case"failed":MessageBox.Show("Failed!");break;default:MessageBox.Show("Unknown error!");break;}}}privatevoidBtnInsert_Click(objectsender,EventArgse){InsertData("SETO_KOUJI");}privatevoidBtnGet_Click(objectsender,EventArgse){GetData();}}}
実行結果
無事データのやり取りを行うことができました。
このとき、データベースの中身は以下のようになっています。
正しい値を取得できていますね。
最後に
今回は、C#アプリケーションとデータベースでのやり取りにAPIを使用してみました。
APIを使用しても、肝心の通信の中身が暗号化されていなければセキュアであるとは言えないですし、中間者攻撃も容易にできてしまいます。
実際に実装して運用する際は十分な注意を払う必要がありますね。
通信内容の暗号化に関しては、セッションを利用して共通鍵方式で暗号化するのも手段の一つだと思います。