先建立網頁的框架

安裝Node.js

我們使用Node.js的web框架是express,首先先確定Node.js有安裝,我是在Win10下開發的所以到nodejs的官網下載LTS版通常是下一步,可以安裝完成,在命令列中可以下node -v可以看到版本,目前我使用的版本為v10.15.3

設定package.json

先建立一個資料夾叫helloexpress, 在到它的目錄下使用命令列,下下面的指令npm init先建立package.json,它會問些問題,會將資料寫入package.json按照需要輸入或是直接按下不輸入

安裝express

在命令列下npm install --save express,它會建立node_modules的資料夾和將相關的程式碼放到此目錄

建立index.js

在和package.json的相同目錄下,建立index.js,這是程式執行的地方輸入如下

1
2
3
4
5
6
7
8
9
10
var app = require('express')();
var http = require('http').createServer(app);

app.get('/',function(req,res){
res.send('<h1>Hello express</h1>')
})

app.listen(3000,function(){
console.log('listening on *:3000');
})

在命令列下輸入node index.js接下來在瀏覽器中輸入http://127.0.0.1:3000在瀏覽器的畫面會顯示<h1>Hello express</h1>

後記

因為我是在vscode中執行,可以直接在上面可以執行單步偵錯,如下圖所示

記錄和測試前端(angualr)和後端(python)的基本使用

先在MariaDB上建立資料庫名userdb

建立資料表

1
2
3
4
5
6
7
CREATE TABLE `tbl_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_email` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_password` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

導入flask-marshmallow
透過此函式庫可以讓我們的程式在處理請求參數更彈性

1
pip install -U marshmallow

Flask-CORS使用

1
2
3
from flask_cors import CORS
## ...
CORS(app)

安裝

1
pip install -U flask-cors

建立python的restful的API

先建立crud_user.py為執行python的主執行程式

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
from flask import Flask, request, redirect, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# 資料庫相關
app.config['SECRET_KEY'] = 'Fianna'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345678@localhost:3306/userdb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
ma = Marshmallow(app)

# 定義ORM物件
class User(db.Model):
__tablename__ = 'tbl_user'
user_id = db.Column(db.Integer, nullable=False,
primary_key=True, autoincrement=True)
user_name = db.Column(db.String(45), nullable=True)
user_email = db.Column(db.String(45), nullable=True)
user_password = db.Column(db.String(45), nullable=True)
def __init__(self, username, useremail, userpassword=""):
self.user_name=username
self.user_email=useremail
self.user_password=userpassword
def __repr__(self):
return '<User %r,user_id %r>' % (self.user_name, self.user_id)
class UserSchema(ma.Schema):
class Meta:
# Fields to expose
fields = ('user_id', 'user_name', 'user_email', 'user_password')

user_schema = UserSchema()
users_schema = UserSchema(many=True)

# endpoint to create new user
@app.route("/user", methods=["POST"])
def add_user():
try:
username = request.json['user_name']
email = request.json['user_email']
password= request.json['user_password']
new_user = User(username, email,password)

db.session.add(new_user)
db.session.commit()
return user_schema.jsonify(new_user)
# resp = jsonify('User added successfully!')
# resp.status_code = 200
# return resp
except Exception as e:
print("Failed to add user")
print(e)

# endpoint to show all users
@app.route("/user", methods=["GET"])
def get_user():
try:
all_users = User.query.all()
result = users_schema.dump(all_users)
return jsonify(result.data)
except Exception as e:
print("Failed to get all user")
print(e)

# endpoint to get user detail by id
@app.route("/user/<id>",methods=["GET"])
def user_detail(id):
try:
user = User.query.get(id)
return user_schema.jsonify(user)
except Exception as e:
print("Failed to get user")
print(e)
# endpoint to update user
@app.route("/user/<id>", methods=["PUT"])
def user_update(id):
try:
user = User.query.get(id)
username = request.json['user_name']
email = request.json['user_email']
password= request.json['user_password']
user.user_name= username
user.user_email = email
user.user_password = password
db.session.commit()
return user_schema.jsonify(user)
except Exception as e:
print("Failed to update user")
print(e)

# endpoint to delete user
@app.route("/user/<id>", methods=["DELETE"])
def user_delete(id):
try:
user = User.query.get(id)
db.session.delete(user)
db.session.commit()
return user_schema.jsonify(user)
except:
print("Failed to del user")
print(e)

if __name__ == "__main__":
app.run(debug=False)

以上的程式碼分成幾部分

導入需要的程式庫和建利App物件

1
2
3
4
5
from flask import Flask, request, redirect, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

app = Flask(__name__)

有關資料庫相關

1
2
3
4
5
6
# 資料庫相關
app.config['SECRET_KEY'] = 'Fianna'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345678@localhost:3306/userdb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
ma = Marshmallow(app)

有關ORM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定義ORM物件
class User(db.Model):
__tablename__ = 'tbl_user'
user_id = db.Column(db.Integer, nullable=False,
primary_key=True, autoincrement=True)
user_name = db.Column(db.String(45), nullable=True)
user_email = db.Column(db.String(45), nullable=True)
user_password = db.Column(db.String(45), nullable=True)
def __init__(self, username, useremail, userpassword=""):
self.user_name=username
self.user_email=useremail
self.user_password=userpassword
def __repr__(self):
return '<User %r,user_id %r>' % (self.user_name, self.user_id)
class UserSchema(ma.Schema):
class Meta:
# Fields to expose
fields = ('user_id', 'user_name', 'user_email', 'user_password')

user_schema = UserSchema()
users_schema = UserSchema(many=True)

有關C,建立使用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# endpoint to create new user
@app.route("/user", methods=["POST"])
def add_user():
try:
username = request.json['user_name']
email = request.json['user_email']
password= request.json['user_password']
new_user = User(username, email,password)

db.session.add(new_user)
db.session.commit()
return user_schema.jsonify(new_user)
# resp = jsonify('User added successfully!')
# resp.status_code = 200
# return resp
except Exception as e:
print("Failed to add user")
print(e)

有關R,讀取使用者和所有使用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# endpoint to show all users
@app.route("/user", methods=["GET"])
def get_user():
try:
all_users = User.query.all()
result = users_schema.dump(all_users)
return jsonify(result.data)
except Exception as e:
print("Failed to get all user")
print(e)

# endpoint to get user detail by id
@app.route("/user/<id>",methods=["GET"])
def user_detail(id):
try:
user = User.query.get(id)
return user_schema.jsonify(user)
except Exception as e:
print("Failed to get user")
print(e)

有關U,更新使用者資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# endpoint to update user
@app.route("/user/<id>", methods=["PUT"])
def user_update(id):
try:
user = User.query.get(id)
username = request.json['user_name']
email = request.json['user_email']
password= request.json['user_password']
user.user_name= username
user.user_email = email
user.user_password = password
db.session.commit()
return user_schema.jsonify(user)
except Exception as e:
print("Failed to update user")
print(e)

有關D,刪除使用者

1
2
3
4
5
6
7
8
9
10
11
# endpoint to delete user
@app.route("/user/<id>", methods=["DELETE"])
def user_delete(id):
try:
user = User.query.get(id)
db.session.delete(user)
db.session.commit()
return user_schema.jsonify(user)
except:
print("Failed to del user")
print(e)

建立Agnular測試端

先建立專案

1
ng new angular-python

在建立各個元件在user的目錄下

1
2
3
4
ng g c user/user-list
ng g c user/user-add
ng g c user/user-edit
ng g c user/user-detail

src\app\app-routing.module.ts下來建立接下來的路由路徑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user/user-list/user-list.component';

const routes: Routes = [
{ path: '', redirectTo: '/user', pathMatch: 'full' },
{ path: 'user', component: UserListComponent }

];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

先測試路由和元件是否能顯示輸入http://localhost:4200/會自動轉成http://localhost:4200/user會顯示user-list的元件

建立使用者物件在user的資料夾下src\app\user\user.ts

1
2
3
4
5
6
export class User{
user_id?:number;
user_name:string;
user_email:string;
user_password:string;
}

建立服務物件在user 的資料夾下

1
ng g s user\user

src\app\user\user.service.ts下先加入列出所有的使用者服務

要先將使用的模塊加入到src\app\app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ...
import { HttpClientModule } from '@angular/common/http';
// ...
@NgModule({
declarations: [
AppComponent,
// ...
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

顯示所有使用者

src\app\user\user.service.ts的服務引用,並在構造函數中聲明,因為有使用到user的物件也需要引用進來,因為使用Obserable所以也要引用進來,在加入取得所以的資料流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Injectable } from '@angular/core';
import { HttpClient ,HttpHeaders} from "@angular/common/http";
import { User } from './user';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class UserService {
// api address
private userUrl = "http://localhost:5000";
constructor(private http:HttpClient) { }
// 取得所有的使用者
getUsers():Observable<User[]>{
var api = this.userUrl+"/user";
return this.http.get<User[]>(api);
}
}

在顯示端src\app\user\user-list\user-list.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<h3>使用者列表</h3>
<div>
<!-- <a [routerLink]="['/add']">加入使用者</a> -->
</div>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users" >
<td>{{user.user_id}}</td>
<td>{{user.user_name}}</td>
<td>{{user.user_email}}</td>
<td>notimp</td>
</tr>
</table>

加入使用者

因為要加入雙向綁定所以要加入FormsModule模組

1
import { FormsModule } from '@angular/forms';

在路由端要加入src\app\app-routing.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user/user-list/user-list.component';
import { UserAddComponent } from './user/user-add/user-add.component';

const routes: Routes = [
{ path: '', redirectTo: '/user', pathMatch: 'full' },
{ path: 'user', component: UserListComponent },
{ path: 'add', component: UserAddComponent }

];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

在服務端src\app\user\user.service.ts中要加入HttpHeaders的物件

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
import { Injectable } from '@angular/core';
import { HttpClient ,HttpHeaders} from "@angular/common/http";
import { User } from './user';
import { Observable } from 'rxjs';

const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
providedIn: 'root'
})
export class UserService {

// api address
private userUrl = "http://localhost:5000";

constructor(private http:HttpClient) { }

// 取得所有的使用者
getUsers():Observable<User[]>{
var api = this.userUrl+"/user";
return this.http.get<User[]>(api);
}
// 加入一個使用者
addUser(user: User): Observable<User> {
var api = this.userUrl + "/user";
return this.http.post<User>(api, user, httpOptions);
}
}

src\app\user\user-add\user-add.component.ts

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
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { User } from '../user';
import { UserService } from '../user.service';

@Component({
selector: 'app-user-add',
templateUrl: './user-add.component.html',
styleUrls: ['./user-add.component.scss']
})
export class UserAddComponent implements OnInit {

public user: User= new User();

constructor(private usersevice:UserService,private location:Location) { }

ngOnInit() {
}
// 加人使用者
save():void{
this.usersevice.addUser(this.user).subscribe((res)=>{
console.log(res);
this.goBack()});
}
// 回上一頁
goBack():void{
this.location.back();
}
}

在顯示端src\app\user\user-add\user-add.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div>
<h2>加入使用者</h2>
<p>
<label>姓名:<input [(ngModel)]="user.user_name" placeholder="請輸人姓名"> </label>
</p>
<p>
<label>Email:<input [(ngModel)]="user.user_email" placeholder="請輸人email" ></label>
</p>
<p>
<label>密碼:<input type="password" [(ngModel)]="user.user_password" placeholder="請輸人密碼" ></label>
</p>
<button (click)="goBack()" >回到上一頁</button>
<button (click)="save()" >加入</button>
</div>

顯示一個使用者

在路由上加上顯示使用者src\app\app-routing.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user/user-list/user-list.component';
import { UserAddComponent } from './user/user-add/user-add.component';
import { UserDetailComponent } from './user/user-detail/user-detail.component';

const routes: Routes = [
{ path: '', redirectTo: '/user', pathMatch: 'full' },
{ path: 'user', component: UserListComponent },
{ path: 'add', component: UserAddComponent },
{ path: 'detail/:id', component: UserDetailComponent }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

在路由顯示端src\app\user\user-list\user-list.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<h3>使用者列表</h3>
<div>
<a [routerLink]="['/add']">加入使用者</a>
</div>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users" >
<td>{{user.user_id}}</td>
<td>{{user.user_name}}</td>
<td>{{user.user_email}}</td>
<td>
<a routerLink="/detail/{{user.user_id}}">使用者資訊</a>
</td>
</tr>
</table>

先在服務端src\app\user\user.service.ts加入以id來取得使用者資訊

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
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { User } from './user';
import { Observable } from 'rxjs';

const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
providedIn: 'root'
})
export class UserService {

// api address
private userUrl = "http://localhost:5000";

constructor(private http: HttpClient) { }

// 取得所有的使用者
getUsers(): Observable<User[]> {
var api = this.userUrl + "/user";
return this.http.get<User[]>(api);
}
// 加入一個使用者
addUser(user: User): Observable<User> {
var api = this.userUrl + "/user";
return this.http.post<User>(api, user, httpOptions);
}
// 取得一個使用者的資訊
getUser(id: string):Observable<User>{
var api = `${this.userUrl}/user/${id}`;
//console.log(api);
return this.http.get<User>(api);
}
}

src\app\user\user-detail\user-detail.component.ts中加入

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
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { User } from '../user';
import { UserService } from '../user.service';

@Component({
selector: 'app-user-detail',
templateUrl: './user-detail.component.html',
styleUrls: ['./user-detail.component.scss']
})
export class UserDetailComponent implements OnInit {
user: User;
constructor(private route: ActivatedRoute, private userService: UserService, private location: Location) { }

ngOnInit() {
this.getUser();
}
getUser(): void {
const id = this.route.snapshot.paramMap.get('id');
this.userService.getUser(id).subscribe((data) => {
this.user = data;
// console.log(this.user);
});
}
goBack(): void {
this.location.back();
}
}

在顯示端src\app\user\user-detail\user-detail.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<div *ngIf="user">
<h2>{{user.user_name | uppercase }} 細節</h2>
<div><span>Id:</span> {{user.user_id}}</div>
<div>
<p>
Name: {{user.user_name}}
</p>
<p>
Email: {{user.user_email}}
</p>
</div>
<button (click)="goBack()">go back</button>
</div>

編輯一個使用者

在路由上加上顯示使用者src\app\app-routing.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { UserListComponent } from './user/user-list/user-list.component';
import { UserAddComponent } from './user/user-add/user-add.component';
import { UserDetailComponent } from './user/user-detail/user-detail.component';
import { UserEditComponent } from './user/user-edit/user-edit.component';

const routes: Routes = [
{ path: '', redirectTo: '/user', pathMatch: 'full' },
{ path: 'user', component: UserListComponent },
{ path: 'add', component: UserAddComponent },
{ path: 'detail/:id', component: UserDetailComponent },
{ path: 'edit/:id', component: UserEditComponent }

];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

在路由顯示端src\app\user\user-list\user-list.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<h3>使用者列表</h3>
<div>
<a [routerLink]="['/add']">加入使用者</a>
</div>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users" >
<td>{{user.user_id}}</td>
<td>{{user.user_name}}</td>
<td>{{user.user_email}}</td>
<td>
<a routerLink="/detail/{{user.user_id}}">使用者資訊</a>
&nbsp;
<a routerLink="/edit/{{user.user_id}}">編輯使用者資訊</a>
</td>
</tr>
</table>

先在服務端src\app\user\user.service.ts加入以傳入user來編輯使用者資訊

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
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { User } from './user';
import { Observable } from 'rxjs';

const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
providedIn: 'root'
})
export class UserService {

// api address
private userUrl = "http://localhost:5000";

constructor(private http: HttpClient) { }

// 取得所有的使用者
getUsers(): Observable<User[]> {
var api = this.userUrl + "/user";
return this.http.get<User[]>(api);
}
// 加入一個使用者
addUser(user: User): Observable<User> {
var api = this.userUrl + "/user";
return this.http.post<User>(api, user, httpOptions);
}
// 取得一個使用者的資訊
getUser(id: string):Observable<User>{
var api = `${this.userUrl}/user/${id}`;
//console.log(api);
return this.http.get<User>(api);
}
// 編輯一個使用者的資訊
updateUser(user:User):Observable<User>{
var api = `${this.userUrl}/user/${user.user_id}`;
return this.http.put<User>(api,user,httpOptions);

}
}

src\app\user\user-edit\user-edit.component.ts中加入

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
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { User } from '../user';
import { UserService } from '../user.service';

@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.scss']
})
export class UserEditComponent implements OnInit {
user: User;
constructor(private route: ActivatedRoute, private userService: UserService, private location: Location) { }

ngOnInit() {
this.getUser();
}
getUser() {
const id = this.route.snapshot.paramMap.get('id');
this.userService.getUser(id).subscribe((data) => {
this.user = data;
});
}
save():void{
this.userService.updateUser(this.user).subscribe((success)=>{
this.goBack();
});
}
goBack(): void {
this.location.back();
}
}

在顯示端src\app\user\user-edit\user-edit.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<div *ngIf="user">
<h2>{{user.user_name | uppercase }} 細節</h2>
<div><span>Id:</span> {{user.user_id}}</div>
<div>
<p>
Name: {{user.user_name}}
</p>
<p>
Email: {{user.user_email}}
</p>
</div>
<button (click)="goBack()">go back</button>
</div>

刪除一個使用者

先在服務端src\app\user\user.service.ts加入以傳入user來刪除使用者資訊

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
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { User } from './user';
import { Observable } from 'rxjs';

const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
providedIn: 'root'
})
export class UserService {

// api address
private userUrl = "http://localhost:5000";

constructor(private http: HttpClient) { }

// 取得所有的使用者
getUsers(): Observable<User[]> {
var api = this.userUrl + "/user";
return this.http.get<User[]>(api);
}
// 加入一個使用者
addUser(user: User): Observable<User> {
var api = this.userUrl + "/user";
return this.http.post<User>(api, user, httpOptions);
}
// 取得一個使用者的資訊
getUser(id: string):Observable<User>{
var api = `${this.userUrl}/user/${id}`;
//console.log(api);
return this.http.get<User>(api);
}
// 編輯一個使用者的資訊
updateUser(user:User):Observable<User>{
var api = `${this.userUrl}/user/${user.user_id}`;
return this.http.put<User>(api,user,httpOptions);

}
// 刪除一個使用者的資訊
deleteUser(user:User):Observable<User>{
var api = `${this.userUrl}/user/${user.user_id}`;
return this.http.delete<User>(api);
}
}

src\app\user\user-list\user-list.component.ts元件中加入刪除使用者

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
import { Component, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { UserService } from '../user.service';
import { Subscriber } from 'rxjs';
import { User } from '../user';

@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.scss']
})
export class UserListComponent implements OnInit {
users: User[] = [];
constructor(private uservice:UserService,private location: Location) { }

ngOnInit() {
this.getUsers();
}
getUsers():void{
//console.log('getUsers------------------');
this.uservice.getUsers().subscribe((users)=>{
this.users=users;
//console.log(users);
});

}
delete(user:User):void{
if(confirm("Are you sure to delete?"))
{
this.uservice.deleteUser(user).subscribe(()=>{
window.location.reload();
});
}
}

}

在顯示端加入src\app\user\user-list\user-list.component.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
<h3>使用者列表</h3>
<div>
<a [routerLink]="['/add']">加入使用者</a>
</div>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users" >
<td>{{user.user_id}}</td>
<td>{{user.user_name}}</td>
<td>{{user.user_email}}</td>
<td>
<a routerLink="/detail/{{user.user_id}}">使用者資訊</a>
&nbsp;
<a routerLink="/edit/{{user.user_id}}">編輯使用者資訊</a>
&nbsp;
<button (click)="delete(user)">X</button>
</td>
</tr>
</table>

可以參考https://github.com/ttom921/StudyPython.git裏的TestFlaskAngular的專案

參考資料

Build Simple Restful Api With Python and Flask Part 2
Python REST API CRUD Example using Flask and MySQL
Python REST APIs + Flask + Angular CRUD Example

記錄flask存取MariaDB

一樣在虛擬環境,先安裝flask-sqlalchemy

1
2
pip install flask
pip install flask-sqlalchemy

建立產生靜態網頁

在它的資料夾下建立templates,這是為了可以回傳html檔,可以讓瀏覽器可以看到

1
2
3
4
Testflasksql/
bookmanager.py
templates/
home.html

home.html加入以下的碼

1
2
3
4
5
6
7
8
<html>
<body>
<form method="POST" action="/">
<input type="text" name="title">
<input type="submit" value="Add">
</form>
</body>
</html>

bookmanager.py下加入以下的碼

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.route("/")
def home():
return render_template("home.html")

if __name__ == "__main__":
app.run(debug=True)

這樣執行bookmanager.py可以產生一個有輸入資料的網頁

加入後端的處理

在home.html碼可以看到的碼<form method="POST" action="/">指到處理的後端程式,因為要取到資料所以在bookmanager.py裏加入from flask import request

1
2
3
4
5
6
7
8
from flask import Flask
from flask import render_template
from flask import request
@app.route("/", methods=["GET", "POST"])
def home():
if request.form:
print(request.form)
return render_template("home.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
from flask import Flask
from flask import render_template
from flask import request
from flask_sqlalchemy import SQLAlchemy
from flask import redirect

app = Flask(__name__)

app.config['SECRET_KEY'] = 'Fianna'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345678@localhost:3306/bookdatabase'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

class Book(db.Model):
title = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)

def __repr__(self):
return "<Title: {}>".format(self.title)

@app.route("/", methods=["GET", "POST"])
def home():
if request.form:
book = Book(title=request.form.get("title"))
db.session.add(book)
db.session.commit()
return render_template("home.html")

if __name__ == "__main__":
app.run(debug = True)

要在資料庫先建立資料庫bookdatabase,才可以來以物件來建立資料表

建立Book物件

建立Book物件資料庫,可以之後在資料庫建立資料表

初始化資料庫

在python的命令列下,執行下面的命令

1
2
3
>>> from bookmanager import db
>>> db.create_all()
>>> exit()

取得所有的資料

bookmanager.py裏加入books = Book.query.all()return render_template("home.html", books=books)如下面的代碼

1
2
3
4
5
6
7
8
@app.route("/", methods=["GET", "POST"])
def home():
if request.form:
book = Book(title=request.form.get("title"))
db.session.add(book)
db.session.commit()
books = Book.query.all()
return render_template("home.html", books=books)

home.html修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<body>
<h1>Add book</h1>
<form method="POST" action="/">
<input type="text" name="title">
<input type="submit" value="Add">
</form>

<h1>Books</h1>
{% for book in books %}
<p>{{book.title}}</p>
{% endfor %}
</body>
</html>

更新資料

homt.html裏修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<body>
<h1>Add book</h1>
<form method="POST" action="/">
<input type="text" name="title">
<input type="submit" value="Add">
</form>

<h1>Books</h1>
{% for book in books %}
<p>{{book.title}}</p>
<form method="POST" action="./update">
<input type="hidden" value="{{book.title}}" name="oldtitle">
<input type="text" value="{{book.title}}" name="newtitle">
<input type="submit" value="Update">
</form>
{% endfor %}
</body>
</html>

bookmanager.py下加入和修改

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
from flask import Flask
from flask import render_template
from flask import request
from flask_sqlalchemy import SQLAlchemy
from flask import redirect

app = Flask(__name__)

app.config['SECRET_KEY'] = 'Fianna'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345678@localhost:3306/bookdatabase'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

class Book(db.Model):
title = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)

def __repr__(self):
return "<Title: {}>".format(self.title)

@app.route("/", methods=["GET", "POST"])
def home():
if request.form:
book = Book(title=request.form.get("title"))
db.session.add(book)
db.session.commit()
books = Book.query.all()
return render_template("home.html", books=books)

@app.route("/update", methods=["POST"])
def update():
newtitle = request.form.get("newtitle")
oldtitle = request.form.get("oldtitle")
book = Book.query.filter_by(title=oldtitle).first()
book.title = newtitle
db.session.commit()
return redirect("/")



if __name__ == "__main__":
app.run(debug = True)


刪除資料

和更新資料很像

home.html修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<body>
<h1>Add book</h1>
<form method="POST" action="/">
<input type="text" name="title">
<input type="submit" value="Add">
</form>

<h1>Books</h1>
{% for book in books %}
<p>{{book.title}}</p>
<form method="POST" action="./update">
<input type="hidden" value="{{book.title}}" name="oldtitle">
<input type="text" value="{{book.title}}" name="newtitle">
<input type="submit" value="Update">
</form>
<form method="POST" action="./delete">
<input type="hidden" value="{{book.title}}" name="title">
<input type="submit" value="Delete">
</form>
{% endfor %}
</body>
</html>

bookmanager.py加入delete的功能

1
2
3
4
5
6
7
@app.route("/delete", methods=["POST"])
def delete():
title = request.form.get("title")
book = Book.query.filter_by(title=title).first()
db.session.delete(book)
db.session.commit()
return redirect("/")

最律修改一下home.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
<html>

<body>
<h1>Add book</h1>
<form method="POST" action="/">
<input type="text" name="title">
<input type="submit" value="Add">
</form>

<h1>Books</h1>
<table>
{% for book in books %}
<tr>
<td>
{{book.title}}
</td>
<td>
<form method="POST" action="./update" style="display: inline">
<input type="hidden" value="{{book.title}}" name="oldtitle">
<input type="text" value="{{book.title}}" name="newtitle">
<input type="submit" value="Update">
</form>
</td>
<td>
<form method="POST" action="./delete" style="display: inline">
<input type="hidden" value="{{book.title}}" name="title">
<input type="submit" value="Delete">
</form>
</td>
</tr>
{% endfor %}
</table>
</body>

</html>

最後在bookmanager.py加入try catch

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
from flask import Flask
from flask import render_template
from flask import request
from flask_sqlalchemy import SQLAlchemy
from flask import redirect

app = Flask(__name__)

app.config['SECRET_KEY'] = 'Fianna'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345678@localhost:3306/bookdatabase'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

class Book(db.Model):
title = db.Column(db.String(80), unique=True, nullable=False, primary_key=True)

def __repr__(self):
return "<Title: {}>".format(self.title)

@app.route("/", methods=["GET", "POST"])
def home():
books = None
if request.form:
try:
book = Book(title=request.form.get("title"))
db.session.add(book)
db.session.commit()
except Exception as e:
print("Failed to add book")
print(e)
books = Book.query.all()
return render_template("home.html", books=books)

@app.route("/update", methods=["POST"])
def update():
try:
newtitle = request.form.get("newtitle")
oldtitle = request.form.get("oldtitle")
book = Book.query.filter_by(title=oldtitle).first()
book.title = newtitle
db.session.commit()
except Exception as e:
print("Failed to add book")
print(e)
return redirect("/")

@app.route("/delete", methods=["POST"])
def delete():
try:
title = request.form.get("title")
book = Book.query.filter_by(title=title).first()
db.session.delete(book)
db.session.commit()
except Exception as e:
print("Failed to add book")
print(e)
return redirect("/")


if __name__ == "__main__":
app.run(debug = True)

重構Resource

先重新檢視一下現有的程式碼,有把一些驗證請求參的部份抽離到UserSchema內,不過Resource下的user.py仍有許多改善的空間

截至目前的user.py(Resource)

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
from flask_restful import Resource
from flask import request
from models.schema.user import UserSchema
users = []
user_schema= UserSchema()
class User(Resource):
def get(self, name):
find = [item for item in users if item['name'] == name]
if len(find) == 0:
return {
'message': 'username not exist!'
}, 403
user = find[0]
if not user:
return {
'message': 'username not exist!'
}, 403
return {
'message': '',
'user': user
}

def post(self, name):
result = user_schema.load(request.json)
if len(result.errors)> 0:
return result.errors, 433
user = {
'name': name,
'email': result.data['email'],
'password': result.data['password']
}
global users
users.append(user)
return {
'message':'Inser user success',
'user':user
}
def put(self, name):
result = user_schema.load(request.json)
if len(result.errors) >0:
return result.errors, 433

find = [item for item in users if item['name']==name]
if len(find) == 0:
return{
'message':'username not exist'
},403
user = find[0]
user['email']= result.data['email']
user['password'] = result.data['password']
return {
'message':'Update user success',
'user':user
}

def delete(self, name):
global users
users = [item for item in users if item['name'] != name]
return{
'message': 'Delete done!'
}

class Users(Resource):
def get(self):
return {
'message': '',
'users': users
}

建立user.py(models)

首先將使用者相關的函式抽離User這Resource,而且將users這個變數也放到models.user.py內,所以重構的第一個步驟就是產生含有以下內肉的models.user.py

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
users = []


class UserModel:
name = ''
email = ''
password = ''

def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = password

def add_user(self):
users.append(self)

@staticmethod
def get_user(name):
find = [item for item in users if item.name == name]
if len(find)==0:
return None
return find[0]
@staticmethod
def delete_user(name):
global users
users = [item for item in users if item.name!= name]
@staticmethod
def get_all_user():
return users


修改User這個Resource

我們首先整理User這個Resource,簡單來說就是把相容的動作以呼叫models.user來取代,但是在返還回客戶端會遇到Model解析的問是,因為我們之前的使用者資料都是返回dict格式,所以flask-restful會自動轉換為JSON格式,但是現在的使用者資料都是UserModel所以返還時無法解析,這時再透過flask-marshmallow來處理即可,處理的方式如下說明

1
user_schema.dump(user).data

最後user修改如下:

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
from flask_restful import Resource
from flask import request
from models.schema.user import UserSchema
from models.user import UserModel

user_schema = UserSchema(many=False)

class User(Resource):

def get(self, name):
user = UserModel.get_user(name)
if not user:
return {
'message': 'username not exist!'
}, 403
return {
'message': '',
'user': user_schema.dump(user).data
}

def post(self, name):
result = user_schema.load(request.json)
if len(result.errors)> 0:
return result.errors, 433
user = UserModel(name,result.data['email'],result.data['password'])
user.add_user()
return {
'message':'Inser user success',
'user':user_schema.dump(user)
}
def put(self, name):
result = user_schema.load(request.json)
if len(result.errors) >0:
return result.errors, 433
user = UserModel.get_user(name)
if not user:
return{
'message':'username not exist'
},403
user.email= result.data['email']
user.password = result.data['password']
return {
'message':'Update user success',
'user':user_schema.dump(user).data
}

def delete(self, name):
UserModel.delete_user(name)
return{
'message': 'Delete done!'
}

class Users(Resource):
def get(self):
return {
'message': '',
'users': user_schema.dump(UserModel.get_all_user(),True).data
}

改善取得請求參數問題

這邊我們在針對POST與PUT都在處理請求參數抽離共同函式來處理,而相關改善的程式碼如下

1
2
3
4
5
6
# 放在uer.py的上面
def get_param():
data = request.get_json(force=False)
if data is None:
data = request.form
return data

導入flask-marshmallow

透過此函式庫可以讓我們的程式在處理請求參數更彈性。在user.py的寫法沒有辦法驗證==email==以及==password==資料的正確性,頂多只確定使用者有提交此參數,所以這裡我們要介紹一個套件來取代原來flask-restul內建的reqpase。

安裝

安裝的方法很簡單就是輸入以下指令即可。記得如果有虛擬環境要先啟動才來安裝

1
pip install flask-marshmallow

初始設定

在安裝完flask-marshmallow後要處理的是如何在我們先新增一個ma.pycommon資料夾內。

1
2
3
# common/ma.py
from flask_marshmallow import Marshmallow
ma = Marshmallow()

為什麼要做這個動作呢,主要是為了避色以後的開法產生循環參考的狀況,因為在後續的動作很會使用到ma這個實體。

加入應用程序內

在設定好flask-marshmallow後要把flask-marshmallow加到我們的flask-restful應用程序之中

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from flask_restful import Api
from resources.user import User,Users
app = Flask(__name__)
api = Api(app)
api.add_resource(User, '/user/<string:name>')
api.add_resource(Users, '/users/')
if __name__ == "__main__":
# 在此加入
from common.ma import ma
ma.init_app(app)
app.run()

marshmallow的元素

在這邊我們透過建立一個解析請求參數內容的方式介紹marshmallow內含的元素,先介紹schema

schema

我們可以透過這一個schema來建立我們預期請求參數的內容的基本結構,下面例子我們建立一個UserSchema來定義我們接收到使用者的請求參數所包含的元素

1
2
3
4
5
from common.ma import ma

class UserSchema(ma.Schema):
email = ma.Str()
password = ma.Str()

在定義UserSchema之前我們需要把先前的flask-marshmallow函式庫import進來,因為所有自訂義的marshmallow的schema都要繼承Marshmllow.Schema,接下來我們在UserSchema定義他有兩個屬性emailpassword這兩個屬性的型別是字串,而marshmallow的field有很多䅜以下針對下列幾點加以說明

  • Str,Int,Bool,Float
  • DateTime,LocalDatTime
  • Email
  • Nested

Str,Int,Bool,Float

這就是一些常用的基本型別

DateTime,LocalDatTime

這是一些時間的型別

Email

這就限制字串內容是Email的格式

Nested

這個Nested比較特別,就是的個複雜的型號它可以透過傳入另外一個Schema告訴目前的Schema知道那個屬性是該Schema的型別,這樣說來很抽象就讓大家看看下列例子

1
2
3
4
5
6
class ASchema(ma.Schema):
name = ma.Str()
password = ma.Str()

class BSchema(ma.Schema):
aItem = ma.Nested(ASchema)

validate

由於先前的內容會驗證所需內容一要會提交,但是沒有驗證格式是否相符,所以我們 進一步修正上述的例子

1
2
3
4
5
from common.ma import ma

class UserSchema(ma.Schema):
email = ma.Email(required=True)
password = ma.Str(required=True)

經過修正之後我們針對email的內容加規範,所以當提交內容不符合Email的規範時他會提示錯誤內容,並且該屬性的內容會是Node,不過password若是提交空字串他還是會通過,因此我們再日以修正如下

1
2
3
4
5
6
7
8
9
# models/schema/user.py
from common.ma import ma
from marshmallow import validate


class UserSchema(ma.Schema):
email = ma.Email(required=True)
password = ma.Str(required=True, validate=[
validate.Length(min=6, max=36)])

透過validate.Length我們可以指定密碼的長度落在6~36之間,所以當下次提交請求時若是password提交空字串時會得到驗證失敗的錯誤

在flask-restful內加入Schema

首先建立一個資料夾models在下面在建立一個資料夾schema,並在schema內加入user.py的檔案將上述的內容貼到user.py檔案內,接下來我們針對Resource下的user.py加以修改

1
from models.schema.user import UserSchema

當然第一步是先將先前寫好的UserSchema給import進來, 在來下一步是產生一個UserSchema的實體

1
user_schema = UserSchema()

最後就是在處理POST與PUT方法時使用user_schema來解析傳入參數的內容以下是例子

1
2
3
4
5
6
7
8
9
json_data = request.json
result = user_schema.load(json_data)
if len(result.errors) > 0:
return result.errors, 433
user = {
'name':name,
'email':result.data['email'],
'password':result.data['password']
}

不遛這裡有個缺點,就是需要明確的定義請求的方式是透過form或是json格式,像上述是透過json方式,如果改成form的方式傳遞就會解析失敗,如果是以form來傳遞就要修改成以下內容

1
json_data = request.form

簡介

因為有看到使用POSTMAN來測試自已寫好的API,如果已經寫好的API可以寫一系列的測試案例來測試自已的程式,如果測試案例全過,代表程式應該被修改到,可以安心,這只是一個簡單的測試用例,記錄一下如何使用POSTMAN

  • 測試用例是取得所有的使用者

    因為一開始將所有的使用者清空,所有得到的是長度為0的陣列,可以來判斷是否是沒有使用者

在上圖中選擇{{url}}/users來測試此API,在右邊的橘色的框框是測試的範本,可以直接來點選,在左邊的紅色框框是可以寫的測試腳本是以JavaScript來撰寫

1
2
3
4
pm.test("get users", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.users.length).to.eql(0);
});

處理參數

之前處理的參數是透個url來傳遞參數,如果是使用post和put可就沒辦法使用,而是需要透過參數傳遞

新增Users

我們要確認users的內容到底有多少,可以新增一個Users的Resource來提交取得所有user的==GET==方法

1
2
3
4
5
6
class Users(Resource):
def get(self):
return {
'message':'',
'users':users
}

修改一下app.py註冊Users

1
api.add_resource(Users, '/users/')

執行結果如下

開發POST

我們先import一個函式庫進來,因為我們需要解析請求的參數

1
from flask_restful import reqparse

在import reqparse這函式庫之後首先要定義欲接受請求的參數內容,在要使用的地方

1
2
3
4
5
class User(Resource):
parser = reqparse.RequestParser()
parser.add_argument('email',required=True,help='Email is required')
parser.add_argument('password',required=True,help='Password is required')
##以下省略

type:是要指定的型別

required:請求參數中屬於必填,入無輸入會直接回傳錯誤

help:是當請求參睥驗證失敗(型別不符,未填寫)會返回的訊息

1
parser.add_argument('foo', type=int, required=True,help='error message')

實作POST

使用reqparse之後,實作就變的很簡單

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 在user.py中的class User
def post(self, name):
arg = self.parser.parse_args()
user = {
'name': name,
'email': arg['email'],
'password': arg['password']
}
global users
users.append(user)
return {
'message':'Inser user success',
'user':user
}
設定postman

可以設定postman來驗證POST是否正確

首先先選擇POST的方法如下圖

接下來設定傳送的內容有兩種JSON和FORM

JSON
FORM

這兩個作法都能接受,如果要指定格式參考以下例子

1
2
3
4
5
6
7
8
9
10
11
# Look only in the POST body
parser.add_argument('name', type=int, location='form')

# Look only in the querystring
parser.add_argument('name', type=int, location='args')

# Look only in the json
parser.add_argument('name', type=int, location='json')

# Look only in the multi location
parser.add_argument('name', type=int, location=['form', 'json'])

開發PUT

put是更新資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 在user.py中的class User
def put(self, name):
arg =self.parser.parse_args()
find = [item for item in users if item['name']==name]
if len(find) == 0:
return{
'message':'username not exist'
},403
user = find[0]
user['email']= arg['email']
user['password'] = arg['password']
return {
'message':'Update user success',
'user':user
}
驗證PUT

首先先選擇PUT方法

在json中輸入資料和form中輸入資料,可以看到有改變,可以使用get/users來查看

搞懂Url Routing的參數處理

來做個使用者CRUD

先來做一個基本的CRUD的操作

User基本結構

先定義User這個Resource的基本結構, 這只是定義結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask_restful import Resource


class User(Resource):
def get(self, name):
pass

def post(self, name):
pass

def put(self, name):
pass

def delete(self, name):
pass

註冊Resouce

不過如何將name由url帶入方法內

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask
from flask_restful import Api
from resources.user import User

app = Flask(__name__)
api = Api(app)

api.add_resource(User, '/user/<string:name>')

if __name__ == "__main__":
app.run()

如此可以把User綁定到url/user/,而最後一個內容會轉型成字串傳遞給熱法當作是name的變數, 可以先以postman來測試,之前的uri應該是url

設定POSTMAN

可以在postman上執行get,post,put,delete的方法,只不過服務器的回應是null,應為我們的方法是空的

#### 實作GET方法

先不以資料庫的操作,修改user.py

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
from flask_restful import Resource

users = [{
'name':'kitty'
}]


class User(Resource):
def get(self, name):
find = [item for item in users if item['name'] == name]
if len(find) == 0:
return {
'message': 'username not exist!'
}, 403
user = find[0]
if not user:
return {
'message': 'username not exist!'
}, 403
return {
'message': '',
'user': user
}

def post(self, name):
pass

def put(self, name):
pass

def delete(self, name):
pass

實作delete

可以使用get方法來測試

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
from flask_restful import Resource

users = [
{'name': 'kitty'},
{'name': 'tom'}
]

class User(Resource):
def get(self, name):
find = [item for item in users if item['name'] == name]
if len(find) == 0:
return {
'message': 'username not exist!'
}, 403
user = find[0]
if not user:
return {
'message': 'username not exist!'
}, 403
return {
'message': '',
'user': user
}

def post(self, name):
pass

def put(self, name):
pass

def delete(self, name):
global users
users = [item for item in users if item['name'] != name]
return{
'message': 'Delete done!'
}

flask-restful起手式

安裝flask-restful

要在虛擬python版本的環境安裝

1
pip install flask-restful

第一個resource

以下是基本的結構

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from flask_restful import Api


app = Flask(__name__)
api =Api(app)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run()

以下是新增一個resource,而restful就是以資源為基礎,每一個項目都可以是一個資源,所以我們在添加一個resource在原來的內容中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask
from flask_restful import Api
from flask_restful import Resource


class PrintHelloWorld(Resource):
def get(self):
return{
'message':'Hello World'
},200

app = Flask(__name__)
api =Api(app)

api.add_resource(PrintHelloWorld,'/print_hello_world')

if __name__ == "__main__":
app.run()

第一個POSTMAN請求

安裝postman, 可以登入或是不登人(在畫面的最不方可以選擇不登入)

首先先建立目前的使用環境的通用變數,例如目前的測試server是127.0.0.1:5000,可以在postman先建立好,可以直接來使用在’Manage Environments’來設定

在按下’Add’後可以開始設定要用到的變數

在VARIABLE-> uri , INITIAL VALUE-> 120.0.0.1 CURRENT VALUE-> 127.0.0.1:5000接下來按下’Add’,便可接下來送post命令

選擇’flask-restful’在GET輸入’/print_hello_world’接下來按下’Send’的按鈕,可以在Body收到如上圖的’Hello world’,即完成第一個restfule api的功能

專案的基本結構

1
2
3
4
5
6
7
8
9
10
11
│- app.py
│ __init__.py

├─common
│ util.py
│ __init__.py

└─resources
foo.py
bar.py
__init__.py
  • app.py 主要是主程式和routes
  • util.py 主要是常用的公用函數
  • foo.py和bar.py 相關邏輯

建立launch.json

在打開vscode之後,使用Ctrl+Shift+D,會動生成一個launch.json,如果有出現選擇環境,我們選擇Python,只留下”name”: “Python: Flask”,剩下的可以刪除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
// 選擇虛擬環境中的python版本
"pythonPath": "D:/Project/Python/study_flask/flaskenv/Scripts/python.exe",
"env": {
// 入口文件
"FLASK_APP": "main.py"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
}
]
}

加入main.py

1
2
3
4
5
6
7
8
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == '__main__':
app.debug = False # vscode 才可以偵錯
app.run(host='localhost', port=5000)
0%