舊專案必學技術整理 - 開發環境設定 & 前後端溝通(RESTful API)

Posted by Tim Lin on 2018-11-26

開發環境設置

本案開發工具:
cultivator-20140508-1145-x86_64 (based on Eclipse)
jboss-eap-6.1

maven 是什麼?

  • maven
    • 透過 pom.xml 來定義和管理 java jar lib
  • nexus server
    • 取得 jar 的 repository server
  • JBoss
    • 本案中執行 war 的 ap server

把我準備好的 nta_m2 放在想放的位置
打開 eclipse
每次打開 都會要求選 workspace

每次選一個 “新” 的 workspace 後, 請做這些事

設定 maven 設定檔

Windows -> Preferences

左上搜尋 settings

指定 nta_m2 下面的 settings.xml

把 nexus 的 ip 換成本地的
把指定 repository 換成目前 nta_m2 的位置

為什麼開新 workspace 要做以上事情?

因為每次一匯入新專案或一進入 eclipse 就會做 build automatically, 如果沒有設定好,等等匯入專案後會一堆錯誤而且很慢, 而且還要重跑


匯入專案 (nta-app-sample)

匯入前, 觀察一下原本專案…, 很乾淨



正在匯入了

然後因為有開啟 build automatically, 所以會開始 build project


使用 maven update


在更新…

完成!

看專案多了什麼東西
匯入前:

匯入後:

Eclipse view 的新增

JBoss 設定

把 war 檔 加上 jboss server 中

按下啟動

啟動後網址:
http://localhost:8080/xyz/login

調整字體大小:

調整console顯示的行數:

顯示行數


eclipse 快捷鍵

quick text search (找檔案內文關鍵字)
ctrl + shift + l

open type (找 java 檔案 by 檔名)

open resource (找任何檔案 by 檔名)
ctrl + shift + r

整理 import
ctrl + shift + o

縮排排版
ctrl + i

刪掉一整行
ctrl + d

rename 所有這個變數
alt + shift + r

sysout 自動帶出完整一句
打 sysout + alt + / => System.out.println();

在方法上按 F3 可以進此方法看程式碼

在方法上按 ctrl, 可以選 implement

在方法上 右鍵 open call hierachy, 可以看誰呼叫這方法


環境疑難雜症

狀況 一

新增 db table 時, 請新增…

maven repository 會抓最新的 entity jar 下來到這, 如果有時候覺得抓到還是舊的, 關掉eclipse, 刪除掉整個 gov 資料夾, 在開啟 eclipse 就會整個重抓一次

狀況 二

做 maven update 時會重新產生 entity
有時候產生出來的 entity (有成功產出), eclipse 沒顯示, 要自己手動加

對 xxx-entity 按右鍵

把 entity 選起來

2. 一些範例

SPA 是什麼?

單頁面應用程式 Single Page Applications

過去: Multi-Page (多頁式)

以前製作網站大多是一個一個頁面切換,點選一個按鈕,重新載入另外一個頁面。

現在: Single-Page (單頁式)


前後端互動的方式

RESTful API

Representational State Transfer,簡稱REST,它是一種網路架構風格,他並不是一種標準。

而 RESTful 可以這樣子想像: 美麗 (Beauty) 的事物可以稱為 Beautiful; 設計為 REST 的系統就可以稱為 RESTful

以 API 而言,假設我們正在撰寫一組待辦事項的 API,
可能會有以下方式來作為 API 的 interface:

獲取使用者資料 /getAllUsers
獲取使用者資料 /getUser/1
新增使用者資料 /createUser
更新使用者資料 /updateUser/1
刪除使用者資料 /deleteUser/1

以 REST 風格來開發 RESTful API:

獲取使用者資料 /GET /users
獲取使用者資料 /GET /user/1
新增使用者資料 /POST /user
更新使用者資料 /PUT /user/1
刪除使用者資料 /DELETE /user/1

兩者差異是在於 RESTful API 充分地使用了 HTTP protocol (GET/POST/PUT/DELETE)


js 呼叫 rest api, 達到 crud (新增修改刪除) 的功能

Rest api: Eac108eResource.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* 若沒有傳 key 值,會 mapping 到此網址,查詢所有資料
*/
@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<Ads132fa> find(Alerter alerter) {
return find(Strings.emptyToNull(null), alerter);
}

/*
* 若有傳 key 值,會 mapping 到此網址,查詢單一筆資料
*/
@RequestMapping(value = "/{ped}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Ads132fa get(@PathVariable String ped, Alerter alerter) {
List<Ads132fa> asb103fas = find(ped, alerter);
if (asb103fas.isEmpty()) {
return null;
}
return asb103fas.get(0);
}

@RequestMapping(value = "/{ped}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public void create(@PathVariable String ped, @Valid @RequestBody Ads132fa asb103fa,
Alerter alerter) {
if (service.create(asb103fa) != null) {
alerter.success(Messages.success_create());
} else {
alerter.info(Messages.abort_create());
}
}

@RequestMapping(value = "/{ped}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void save(@PathVariable String ped, @RequestBody Ads132fa asb103fa, Alerter alerter) {
service.save(asb103fa);
alerter.success(Messages.success_update());
}

@RequestMapping(value = "/{ped}", method = RequestMethod.DELETE)
public void delete(@PathVariable String ped, Alerter alerter) {
service.delete(ped);
alerter.success(Messages.success_delete());
}

/*
* 使用 execute 的方式執行,傳入 entity 在判斷要查詢單筆或是多筆。
* 要注意網址不要與 key 值的網址重覆。
*/
@RequestMapping(value = "/find/all", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public List<Ads132fa> find(@RequestBody Ads132fa asb103fa, Alerter alerter) {
return find(asb103fa.getPed(), alerter);
}

eac108e.js

js 呼叫 rest api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$scope.query = function(ped, omitAlerts) {
ped = ped || ($scope.model ? $scope.model.ped : "");
// var result = eac108eService.find({ped:ped},omitAlerts||false);
var result = eac108eService.findAll('rest/find/all', {
ped: ped
}, omitAlerts || false);
result.then(function(response) {
if (response) {
state.query();
}

if (angular.isArray(response)) {
$scope.gridData = response;
} else {
$scope.gridData = [ response ];
}
});
};
});

brt306e.html & brt306e.js 解說

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!DOCTYPE html>
<html lang="zh-Hant-TW" xmlns:th="http://www.thymeleaf.org">

<head>
<title>代繳作業繳費期限維護 - BRT306E</title>
<meta th:substituteby="fragments/head_meta :: meta">
</meta>
<meta th:substituteby="fragments/resources :: head_resources">
</meta>
</head>

<body>
<div class="container">
<div th:substituteby="fragments/body_header :: body_header"></div>
<div class="row" data-ng-app="brt306eApp"> <!-- 宣告angularjs 作用域 -->
<div class="container" data-ng-controller="brt306eController"> <!-- 宣告 controller 範圍和對應的 name -->
<section>
<!-- 宣告一個 form (form-inline), 通常會給一個 name, 為了給驗證元件使用 -->

<!-- data-c-form-validation: 如果此 FORM 要加上驗證 必須加上 -->

<!-- data-c-enter-tab: 依照 tabindex 的順序按 enter 自動跳欄 -->
<!-- 按下 enter 時自動跳到下一個欄位包含所有的 input、select、button, 若 tabIndexThreshold 有設定時,-->
<!--則 tabIndexThreshold< tabindex 以下的欄位 ,按下enter時不會進入。 -->
<form class="form-inline" name="brt306eForm" data-c-form-validation="{displayTrigger:'none'}"
data-c-enter-tab="{tabIndexThreshold:1}">
<div class="panel panel-info">
<div class="panel-heading col-sm-12">
<h3 class="panel-title">
<!-- ${FUNCTION_ID} 固定由後端讀取而來, 固定寫即可 -->
<span th:text="${FUNCTION_ID}"></span> <span class="text-muted">代繳作業繳費期限維護</span>
</h3>
</div>
<div class="panel-body">
<div class="row-fluid">
<div class="row">
<div class="col-sm-12 text-right">
<button type="button" class="btn btn-primary" data-ng-click="create()"
tabindex="3">
<i class="glyphicon glyphicon-plus"></i> 新增
</button>
<button type="button" class="btn btn-primary" data-ng-click="reset()">
<i class="glyphicon glyphicon-refresh"></i> 清除
</button>
<button type="button" class="btn btn-primary" data-ng-click="remove()">
<i class="glyphicon glyphicon-remove"></i> 刪除
</button>
<button type="button" class="btn btn-primary" data-ng-click="query(true)">
<i class="glyphicon glyphicon-search"></i> 查詢
</button>
</div>
</div>
<br>
<div class="row">
<div class="panel panel-info">
<div class="panel-body">
<div class="col-sm-4"></div>
<div class="col-sm-7">
<div class="row">
<div class="col-sm-3 text-right">
<label>付款類別</label>
</div>
<div class="col-sm-5">
<!-- data-c-validate = {函式名稱 : 要帶入顯示訊息內的參數,若沒有則寫true。} -->
<select id="item" name="item" class="form-control"
data-ng-model="model.item" data-c-validate="{checkItem : true}">
<option value="">&nbsp;&nbsp;&nbsp;全部</option>
<option value="01">01-公保</option>
<option value="02">02-勞保</option>
<option value="03">03-勞工退休金</option>
<option value="04">04-健保</option>
<option value="05">05-退撫基金</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
<label>款項所屬年月</label>
</div>
<!-- dateValidation 此函式 在 customValidation.js -->
<div class="col-sm-9">
<input type="text" class="form-control" id="payYyymm" name="payYyymm"
data-ng-model="model.payYyymm" data-ui-mask="99999"
data-c-validate="{dateValidation : ['yyyMM','Minguo'],messages:{dateValidation:'年月錯誤 (YYYMM)'}}"
data-ng-keydown="findEndDateByEnter($event)" style="width: 35%;"
tabindex="1" required>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
<label>代繳期限</label>
</div>
<div class="col-sm-9">
<input type="text" class="form-control" id="endDate" name="endDate"
data-ng-model="model.endDate" style="width: 40%;"
data-c-validate="{dateValidation : ['yyyMMdd','Minguo'], checkEndDate:true, messages:{dateValidation:'代繳期限錯誤!!'}}"
tabindex="2">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row text-center">
<!-- 使用 ng-grid 元件 -->
<div class="grid-style" data-ng-grid="itemGrid" style="display: inline-block; height: 400px; width: 520px;"></div>
</div>
</div>
</div>
</div>
</form>
</section>
</div>
</div>
</div>
<div th:substituteby="fragments/body_footer :: footer"></div>
<div th:substituteby="fragments/resources :: body_resources"></div>
<script type="text/javascript" th:src="@{/scripts/brt306e.js}"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
(function() {
var app = angular.module("brt306eApp", [ 'ntaCommonModule']); // 都一定會注入 ntaCommonModule

app.factory('brt306eService',function(cResource){ //注入 cResource 來使用框架提供的 ajax 方法,

// cResource 第一個參數 url pattern, 參數物件
// 這裡只有 下面 remove 會用到, 為了對應後端 rest api url

/*@RequestMapping(value = "/{payYyymm}/{item}", method = RequestMethod.DELETE)
public @ResponseBody
void delete(@PathVariableModel Brt352faId id, Alerter alerter) {*/


var resource = cResource('rest/:payYyymm/:item', {payYyymm :"@payYyymm", item :"@item"});

//如果只要用execute, 宣告這樣即可 var resource = cResource('rest', {});


return {
remove : function(model){
return resource.remove(model); // delete
},
execute : function (url , param, showAlerts, clearAlerts){
// omitAlerts: 忽略這次 ajax 的噴錯或警告, clearAlerts: 是否清除 alert
var options = {'omitAlerts': !showAlerts, 'clearAlerts': clearAlerts};
return resource.execute(url , param, options);
}
};
});

// 注入所需的 物件 $scope, brt306eService, alerter, cValidation
app.controller('brt306eController', function($scope, brt306eService, alerter, cValidation) {

$scope.addSpaces = function(input){
var result = "";
if(input == '') {
for(var i = 0; i < 3; i++){
result += String.fromCharCode(160);
}
}
return result;
};

//初始
var init = function() {
$scope.model = {};
$scope.itemData = []; // or $scope.itemData.length = 0
};

init();
$scope.model.item = '';

//帶出EndDate
$scope.findEndDateByEnter = function($event) {
// 檢查 是否按下的案件是 enter (13) 並檢查 payYyymm 這欄位是不是有通過檢核
if($event.keyCode == 13 && $scope.validate($scope.brt306eForm.payYyymm)) {

//呼叫 後端 findEndDate rest api
var result = brt306eService.execute('rest/findEndDate', $scope.model);
result.then(function(response){//針對回傳做事
//沒有錯誤發生時執行以下
if(response) {
$scope.model.endDate = response.endDate;
}
});
}
};

//查詢 load grid
$scope.query = function(showAlerts) {
//呼叫 後端 findGrid rest api
var result = brt306eService.execute('rest/findGrid', $scope.model, showAlerts);
result.then(function(response){//針對回傳做事
//沒有錯誤發生時執行以下
if(response) {
$scope.itemData = response;
}
});
};

//新增
$scope.create = function() {
// 在 js 檢查這三個欄位是否通過, 記得 要檢核的欄位要有 name
// 如果是針對整個 form 的欄位都要檢查 $scope.validate($scope.brt306eForm)
if($scope.validate($scope.brt306eForm.item, $scope.brt306eForm.payYyymm, $scope.brt306eForm.endDate)) {

//呼叫 後端create rest api
var result = brt306eService.execute('rest/create', $scope.model, true);
result.then(function(response){ //針對回傳做事
//沒有錯誤發生時執行以下
$scope.model.payYyymm = "";
$scope.model.endDate = "";
$scope.query();
});
}
};

//刪除
$scope.remove = function(){
bootbox.confirm("是否確定刪除", function(result) { //呼叫 bootbox 元件
if (result){ // 如果按下確定

//呼叫 後端 刪除 rest api
var result = brt306eService.remove($scope.model);
result.then(function(response) { //針對回傳做事
init(); //沒有錯誤發生時執行這
}, function(response) {
alerter.fatal("無資料可刪除! 失敗!"); //有錯誤發生時執行這
});
};
});
};

//做清除
$scope.reset = function () {
init(); //呼叫初始
alerter.clear(); //清除 alerter
$scope.clearValidationMessages(); //用來清除所有的驗證錯誤
};

//gird 欄位定義變數
$scope.itemGridCols = [ {field:'item', width:'120px', displayName:'付款類別', cellFilter: "itemFilter"},
{field:'payYyymm', width:'100px', displayName:'款項年月'},
{field:'endDate', width:'100px', displayName:'代繳期限'},
{field:'uids', width:'100px', displayName:'異動人'},
{field:'mdate', width:'120px', displayName:'異動日期'} ];
// grid 物件
$scope.itemGrid = {
multiSelect : false, //是否可多選
data : 'itemData', // 資料來源 陣列變數名稱: 請用字串
columnDefs : 'itemGridCols', //gird 欄位定義變數名稱: 請用字串
enableColumnResize: true, //欄位是否可以調整大小
selectedItems: $scope.selectedItemData, //選取那些資料
afterSelectionChange : function(rowItem){ // 選取後要做什麼事
if (rowItem.selected) {
$scope.model = angular.copy(rowItem.entity);
}
}
};

// 自訂檢核
var checkItem = function(value) {
return (value === '') ? false : true;
};
cValidation.register('checkItem', checkItem, '付款類別錯誤!');

var checkEndDate = function(value, scope) {
var payYyymm = parseInt(scope.model.payYyymm + '01', 10);
var endDate = parseInt(scope.model.endDate, 10);
return (payYyymm > endDate) ? false : true;
};
cValidation.register('checkEndDate', checkEndDate, '代繳期限錯誤!!');

});

//自訂 filter
app.filter('itemFilter', function() {
return function(input) {
switch(input) {
case "01":
return "01-公保";
break;
case "02":
return "02-勞保";
break;
case "03":
return "03-勞工退休金";
break;
case "04":
return "04-健保";
break;
case "05":
return "05-退撫基金";
break;
default:
return "全部";
}
};
});

})();

ng-grid

https://github.com/angular-ui/ng-grid-legacy/wiki