原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app
作為一個iPhone/iPad開發人員,能夠自己寫一個簡單的web伺服器將是很有用的。
例如,你可能希望在軟體啟動時顯示一些來自伺服器的更新,或者在伺服器端儲存一些使用者資料。除了你的想象力,沒有什麼能限制你了。
在第一篇中,我們將會一步一步的建立一個web伺服器,基於promo code system(促銷碼系統),我在我的第一個軟體中使用的,Wild Fables.在第二篇中,我們將會寫一個iOS App來和它進行互動。
為了完成這個教程,你將需要一個web伺服器,並裝有MySQL和PHP。如果你沒有,那麼你有以下幾種選擇:
- 如果你想在你的Mac(free)上運行Apache/MySQL/PHP,有很多教程可以幫你。這裡有一個教程。
- 如果你想租一個伺服器(需要花錢),這裡有一個教程。
- 或者你很懶,以上兩種你都不想做,那麼你可以使用我在本教程PART2做的伺服器。
你不需要有PHP和MySQL的經驗(當然有更好)因為這個教程包含了所有你需要的代碼。
你將做什麼
id: app的唯一標示.
app_id: app 的唯一字串標示.
- id:唯一表示.
- rw_app_id: 對應的App.
- code: 使用者輸入的促銷碼字元.
- unlock_code: 返回給App的促銷碼字元.
- uses_remaining:促銷碼剩餘可使用次數.
rw_promo_code_redeemed:儲存促銷碼兌取後的資訊。為了防止一個裝置用一個促銷碼兌取多次。
id: 唯一標示.
rw_promo_code_id: 已經兌取的促銷碼ID (from rw_promo_code).
device_id: 已經兌取的裝置ID.
redeemed_time: 兌取的時間.
DROP TABLE AUTO_INCREMENT PRIMARY ,,255) NOT ,255) NOT , AUTO_INCREMENT PRIMARY ,255) NOT AUTO_INCREMENT PRIMARY ,,255) NOT ,
在你的web伺服器上,你需要建立一個MySQL資料庫並建立這三張表。這裡是完成的命令:
儲存上面的代碼到一個名為create.sql的檔案,然後:
rwenderlich@kermit:~$ -u root -: monitor. Commands with ; or \g. connection id is 1286: 5.1.37-1ubuntu5.1-'help;' or '\h' help. Type '\c' to clear the input statement.>, 1 row affected (0.00> > grant all privileges on promos.* to 'username'@'localhost' identified by 'password', 0 rows affected (0.00> :~$ -u username -p promos < create.::~$ -u root -: monitor. Commands with ; or \g. connection id is 1417: 5.1.37-1ubuntu5.1-'help;' or '\h' help. Type '\c' to clear the input statement.> >+------------------------+| Tables_in_promos |+------------------------+| rw_app | | rw_promo_code | | rw_promo_code_redeemed | +------------------------+3 rows in set (0.00 sec)
現在已有了三張空表。下一步,建立一個測試的app:
INSERT INTO rw_app VALUES(1, 'com.razeware.test'1, 1, 'test', 'com.razeware.test.unlock.cake', 10000);
好的。現在資料庫已經串連,可以寫PHP伺服器了。
驗證PHP/MySQL
在開始實現PHP伺服器之前,首先檢查PHP是否在你的伺服器上運行正常。在你的伺服器上建立一個叫promos的檔案夾,在裡面建立一個叫index.php的檔案:
<? "Hello, PHP!" = ->?>
你可以用你的伺服器的URL測試,也可以像下面這樣在命令列測試:
Ray-Wenderlichs-Mac-mini-2:~ rwenderlich$ curl http:Hello, PHP!
下一步,擴充這個類,確保你的伺服器可以串連到資料庫:
->db = mysqli('localhost', 'username', 'password', 'promos'->db->autocommit( ->db-> = ->db->prepare('SELECT id, code, unlock_code, uses_remaining FROM rw_promo_code'->->bind_result(, , , (-> " has uses remaining!"->
這裡添加了一個建構函式來串連給定使用者名稱和密碼的資料庫,一個 解構函式來關閉資料庫。現在你可以測試一下了:
Ray-Wenderlichs-Mac-mini-2:~ rwenderlich$ curl http:test has 10000 uses remaining!
伺服器策略:GET還是POST:
好的,現在是時候來實現完成的功能了。但首先,讓我們來談談web伺服器的策略。
我們知道我們需要向伺服器發送一些資料,包括app的ID,兌換嗎,要兌換的裝置ID。
如何發送呢?有兩種方法:GET(普通方法)和POST(用於發送表單)
- 如果你選擇GET,那麼參數是URL的一部分,就是把參數發到URL裡,然後向伺服器發送請求。
- 如果你選擇POST,參數被放到request body中
每個都能滿足你的需求,但是當你要嘗試做些什麼的時候,比如兌換促銷碼,還是用POST比較好。這也是我將要做的。
這是什麼意思呢?如果我們想在PHP中訪問這些參數,我們可以通過內建的$_POST 數組:
["rw_app_id"]
我們將會用ASIHTTPRequest來串連伺服器,用ASIFormDataRequest類發送一個POST請求:
ASIFormDataRequest *request = forKey:];
更多GET和POST的資訊,請看Wikipedia entry。
更新:請看@smpdawg’s的精彩評論forum topic
web伺服器策略:演算法
下一步,我們要看看將要使用的web伺服器的演算法:
getStatusCodeMessage( = 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => '(Unused)', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' (([])) ? [] : '' sendResponse( = 200, = '', = 'text/html' = 'HTTP/1.1 ' . . ' ' . getStatusCodeMessage((('Content-type: ' .
如果你不理解為什麼我們不要這個,那是因為這是一個遵守HTTP協議的web伺服器,當你發送一個相應你可以制定一個包含錯誤碼和詳細描述的頭。有標準錯誤碼可以用,這些方法不過是用起來更方便。
正如你看到的,我防線了一個可以把狀態嗎轉換成HTML資訊的教程。
下一步,就是真正的實現了!
((["rw_app_id"]) && (["code"]) && (["device_id" = ["rw_app_id" = ["code" = ["device_id" = 0 = ->db->prepare('SELECT id, unlock_code, uses_remaining FROM rw_promo_code WHERE rw_app_id=? AND code=?'->bind_param("is", , ->->bind_result(, , (->-> ( <= 0400, 'Invalid code' ( <= 0403, 'Code already used' = ->db->prepare('SELECT id FROM rw_promo_code_redeemed WHERE device_id=? AND rw_promo_code_id=?'->bind_param("si", , ->->bind_result( (->-> ( > 0403, 'Code already used' = ->db->prepare("INSERT INTO rw_promo_code_redeemed (rw_promo_code_id, device_id) VALUES (?, ?)"->bind_param("is", , ->-> ->db->query("UPDATE rw_promo_code SET uses_remaining=uses_remaining-1 WHERE id="->db-> = "unlock_code" => ,200, json_encode( 400, 'Invalid request'
你應該能夠讀懂這段代碼,否則的話查看以下這個教程Mysqli reference。這裡有一些事情我需要指出:
- isset是一個用於檢測變數是否已經設定了的PHP函數。我們這裡用它來確保所有需要的POST參數都發送了。
- 注意我們沒有自己把傳進來的變數放到SQL語句中,而是使用bind_param方法。這是更安全的方法,否則你可能使自己易受SQL injection的攻擊。
- 注意 unlock_code 用JSON返回。我們當然可以直接用字串返回因為我們只返回一個資訊,但是用JSON便於以後擴充。
現在,你的web伺服器就已經可以工作了。你可以用下面命令來測試:
curl -F "rw_app_id=1" -F "code=test" -F "device_id=test" http:{"unlock_code":"com.razeware.wildfables.unlock.test"}
注意,如果你在我的伺服器上測試,如果你得到“code already used”的錯誤,你應該更改你的device_id。
你可能希望進入你的資料庫看看那裡是否有一個rw_promo_code_redeemed的入口,uses_remaining是否減一等等。
下一步?
敬請期待PART2.