【make it easy】Rest API サービスを作りましょう

Rest APIを言われると大抵どう接続するかどう利用するかの話です。今回サービスを作ってみましょう。

# Restイベント
efwのrestイベントはwebイベントと同じくtomcat/WEB-INF/efw/eventフォルダに格納します。相違点は書き方が違うことです。
https://github.com/efwGrp/efw4.X/blob/master/help/api_restevent.md

# テストのイメージ
jspからサーバに送信します。jsイベントからRestAPIのイベントに転送して、RestAPIをDB処理して結果を戻します。token正しいかどうかの軽いセキュリティチェックを設けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/8429a5b1-0bbf-c6cd-a613-bf84dae6e967.png)

# JSP
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="efw" uri="efw" %>
<!DOCTYPE HTML>
<HTML>
<HEAD>
	<title>Rest API Server and Client Test</title>
	<efw:Client lang="jp"/>
</HEAD>
<BODY>
	<button onclick="Efw('helloRestAPI_submit',{mode:'0'})">初期化（テーブル作成）</button><br><br>
	ID：<input type="text" id="customerId">
	Name：<input type="text" id="customerName"><br>
	token:<input type="text" id="token" value="1234567890">※httpヘッダからセキュリティ情報送信のテスト<br>
	<button onclick="Efw('helloRestAPI_submit',{mode:'1'})">顧客追加</button>
	<button onclick="Efw('helloRestAPI_submit',{mode:'2'})">顧客変更</button>
	<button onclick="Efw('helloRestAPI_submit',{mode:'3'})">顧客削除</button>
	<button onclick="Efw('helloRestAPI_submit',{mode:'4'})">顧客取得</button><br><br>
	<textarea style="width:800px;height:300px;">
	</textarea><br><br>
	<button onclick="Efw('helloRestAPI_submit',{mode:'9'})">終了（テーブル削除）</button><br><br>
</BODY>
</HTML>
```

# jsイベント
```javascript
var helloRestAPI_submit={};
helloRestAPI_submit.paramsFormat={
	mode:null,
	"#customerId":null,
	"#customerName":null,
	"#token":null,
};
helloRestAPI_submit.fire=function(params){
	if (params.mode=="0"){
		db.change("helloRestAPI","createTbl",{});
		return new Result().alert("テーブルを作成しました。");
	}else if (params.mode=="9"){
		db.change("helloRestAPI","dropTbl",{});
		return new Result().alert("テーブルを削除しました。");
	}
	var url="http://localhost:8080/helloworld/efwRestAPI/customer";
	try{
		var ret=null;
		if (params.mode=="1"){//新規追加の場合
			ret=rest.post(url,
				{id:params["#customerId"],nm:params["#customerName"]},
				{token:params["#token"]});
		}else if (params.mode=="2"){//更新の場合
			ret=rest.put(url+"/"+params["#customerId"],
				{nm:params["#customerName"]},
				{token:params["#token"]});
		}else if (params.mode=="3"){//削除の場合
			ret=rest.delete(url+"/"+params["#customerId"],{token:params["#token"]});
		}else if (params.mode=="4"){//取得の場合
			ret=rest.get(url+"/"+params["#customerId"],{token:params["#token"]});
		}
		return new Result()
		.runat("body")
		.withdata({
			"textarea":rest.getStatus()+"\n"+JSON.stringify(ret),
		});
	}catch(e){//失敗の場合
		return new Result()
		.runat("body")
		.withdata({
			"textarea":rest.getStatus()+"\n"+e.getMessage(),
		});
	}
}
```

# restAPIイベント
```javascript
var customer={};
customer.POST=function(keys,params){//新規作成
	var token=Packages.efw.framework.getRequest().getHeader("token");
	if("1234567890"!=token){throw new Error("セキュリティエラー");}
	db.change("helloRestAPI","insertRow",{"id":params.id,"name":params.nm});
	return {url:"efwRestAPI/customer/"+params.id}
};
customer.PUT=function(keys,params){//更新
	var token=Packages.efw.framework.getRequest().getHeader("token");
	if("1234567890"!=token){throw new Error("セキュリティエラー");}
	var rt=db.change("helloRestAPI","updateRow",{"id":keys[0],"name":params.nm});
	if (rt==0) throw new Error("更新対象のデータは存在しません。");
	return null;
};
customer.DELETE=function(keys){//削除
	var token=Packages.efw.framework.getRequest().getHeader("token");
	if("1234567890"!=token){throw new Error("セキュリティエラー");}
	var rt=db.change("helloRestAPI","deleteRow",{"id":keys[0]});
	if (rt==0) throw new Error("削除対象のデータは存在しません。");
	return null;
};
customer.GET=function(keys){//取得
	var token=Packages.efw.framework.getRequest().getHeader("token");
	if("1234567890"!=token){throw new Error("セキュリティエラー");}
	return db.select("helloRestAPI","selectRow",{"id":keys[0]}).getSingle();
};
```

# sql
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqls>
<sqls>
<sql id="createTbl">
	CREATE TABLE tbl_customer(
		id character varying(10) NOT NULL,
		name character varying(20),
		CONSTRAINT tbl_customer_pkey PRIMARY KEY (id)
	)
</sql>
<sql id="dropTbl">
	DROP TABLE tbl_customer;
</sql>
<sql id="selectRow">
	SELECT
		id,
		name
	FROM tbl_customer
	WHERE
		id=:id;
</sql>
<sql id="insertRow">
	INSERT INTO tbl_customer(
		id,
		name
	)VALUES (
		:id,
		:name
	);
</sql>
<sql id="updateRow">
	UPDATE tbl_customer
	SET
		name=:name
	WHERE id=:id;
</sql>
<sql id="deleteRow">
	DELETE FROM tbl_customer
	WHERE id=:id;
</sql>
</sqls>
```

# 説明


POST：挿入、url http://localhost:8080/helloworld/efwRestAPI/customer
戻り値は挿入データのURL、httpステータスは200
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/c3f4565c-4627-1982-c647-04ff71dcd266.png)
PUT：更新、url http://localhost:8080/helloworld/efwRestAPI/customer/[customerID]
戻り値なし、httpステータスは204
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/1fc29609-f85e-7efd-2142-0fcc0d158eaf.png)

GET：取得
http://localhost:8080/helloworld/efwRestAPI/customer/[customerID]
戻り値は取得したデータ、httpステータスは200
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/fae71c61-261e-be52-fa93-53048a95d107.png)

DELETE：削除、http://localhost:8080/helloworld/efwRestAPI/customer/[customerID]
戻り値なし、httpステータスは204
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/378b7656-5770-51e5-2e0b-8ae47cacbf1e.png)

エラー時、
例えば挿入キー重複、更新・削除データ存在なし、などの場合、
戻り値はエラー情報、httpステータスは500
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/3ca21048-f73c-d7b8-3e7e-ef8631f7c7ce.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/c311f9b6-d2f6-b7e3-75b6-a7d3b0231fc1.png)

# テストツールの接続
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/e0c31519-6c91-c2fb-dfea-1f00c1789f86.png)

https://chromewebstore.google.com/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ja

# ブラウザーからのテスト
ブラウザーからテストする場合、cors対応の実装がいけるかどうかの目的です。
```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="efw" uri="efw" %>
<!DOCTYPE HTML>
<HTML>
<HEAD>
	<title>Rest API Server and Client Test</title>
	<efw:Client lang="jp"/>
</HEAD>
<BODY>
	ID：<input type="text" id="customerId">
	Name：<input type="text" id="customerName"><br>
	token:<input type="text" id="token" value="1234567890">※httpヘッダからセキュリティ情報送信のテスト<br>

<script>
	function browserTest(samedomain){
		var domain=samedomain?"localhost":"127.0.0.1";
		var url="http://"+domain+":8080/helloworld/efwRestAPI/customer/"+$("#customerId").val();
		$.ajax({
			url:url,
			xhrFields: { withCredentials: true },
			headers:{ token:$("#token").val()},
			type: "GET",
			cache: false,
			async: true,
			dataType: "json",// send or get data by json type
			contentType: "application/json;charset=UTF-8",
			success:function(result){
				window.alert(JSON.stringify(result));
			},
			error:function(errorResponse){
				window.alert(JSON.stringify(errorResponse));
			}
		})
	}
</script>
<button onclick="browserTest(true)">ブラウザーテスト</button>
<button onclick="browserTest(false)">別ドメインテスト</button>
</BODY>
</HTML>
```
2つのボタンは、それぞれlocalhostと127.0.0.1から送信します。そして、refererと比較すると必ず１本はcorsと判断されます。
１回目はoptions送信です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/bc8466e7-89db-5432-e720-75529f32c8dc.png)
２回目はget送信です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3476221/c2c8bf7e-42c7-cc0b-8e9d-d1f55c8ef6ab.png)

corsに関わる説明のリンクです。
https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34

# 環境準備
DBも利用するから以下の記事を参考してください。
https://qiita.com/changkejun/items/b273b3ae64c76e5b016a

今回のサンプルは以下のリンクからダウンロードできます。

https://github.com/efwGrp/qittaSamples