參考文章
💓Ajax-在node.js使用fetch時,常會碰到之問題"req.body返回值為空"詳解,並附上簡單實作練習
💓Ajax-運用express和fetch的結合,在node.js上實作前端和後端api路由(Get和Post)的教學
💓MongoDB的bin目錄下沒有mongoimport和mongoexport解決方法, 及使用方法教學
💓初級>使用MongoDB結合Express和Fetch來實作RESTful API簡單網站(包括用戶Post, Get, Update, Delete等功能)
💓進階>使用MongoDB結合Express和Fetch來實作RESTful API會員系統 (進階)
- Root Directory
├── models
│ └── user_shchema.js
├── app.js
├── public
│ └── index.html
│ └── vue.js
styles.css
/* 基本樣式 */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 18px;
font-size: 1em;
background-color: #c4bdbd;
}
h1 {
text-align: center;
color: #333;
}
/* 表單樣式 */
form {
font-size: 1em;
max-width: auto;
margin: 10px auto;
padding: 10px;
background-color: #f8e1e1;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
label {
font-size: 1em;
display: block;
margin-bottom: 8px;
font-weight: bold;
}
input[type="text"],
input[type="email"],
input[type="number"]{
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
input[type="submit"],
button {
font-size: 1em;
cursor: pointer;
background-color: #c6e1ff;
color: #000000;
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 2px solid #ecb5b5;
box-sizing: border-box;
}
input[type="submit"]:hover,
button:hover {
background-color: #6598ce;
}
input[type="submit"]:focus,
button:focus {
outline: none;
box-shadow: 0 0 5px #007bff;
}
/* 其他表單樣式 */
#error_txt {
text-align: center;
color: red;
}
form h3 {
margin-bottom: 15px;
color: #333;
}
h3 {
margin-bottom: 15px;
color: #7a1c1c;
text-align: center;
}
span {
text-align: left;
word-wrap: break-word; /* 自動換行 */
white-space: pre-line;
}
span#find_OneOrAll_toggle {
font-size: 1.1em; /* 调整字体大小 */
font-weight: bold; /* 加粗字体 */
color: #333; /* 文本颜色 */
}
/* 悬停效果 */
span#find_OneOrAll_toggle:hover,
h3:hover {
background-color: #ddd; /* 悬停时的背景颜色 */
}
div[type="value1"] {
position: fixed;
z-index: 1;
top: 1%; /* 將元素的頂部定位在視窗的中間位置 */
left: 1%; /* 將元素的左側定位在視窗的中間位置 */
border: 1px dashed rgb(78, 117, 245);
max-width: 100%; /* 設定最大寬度為視窗寬度的80% */
background-color: rgb(247, 199, 137);
padding: 1px;
border-radius: 5px; /* 圆角边框 */
}
/* 根據您的需求調整其他部分的樣式 */
/* 更多 CSS 樣式可以根據您的需求添加在這裡 */
@media screen and (max-width: 767px) {
/* 在螢幕寬度小於 767px 時使用以下 CSS 規則 */
body {
font-size: 0.8em;
background-color: #c1f6fa;
}
}
@media screen and (max-width: 447px) {
/* 在螢幕寬度小於 447px 時使用以下 CSS 規則 */
body {
font-size: 0.5em;
background-color: #fdf07b;
}
}
/*CSS 的規則是基於「後來者居上」的原則,
這意味著如果有多個相同規則(例如相同的選擇器),最後一個定義的規則將覆蓋之前的定義。*/
💝
em 是一種相對長度單位,它用於設置 HTML 元素的尺寸。
em 的值根據該元素的字體大小而定。它是相對於父元素字體大小的倍數。
當您設置某個元素的 font-size 為 1em 時,它將等於該元素的父元素的字體大小,
當我們的網頁中的文字沒有做任何的調整時,預設值就是1em 也就是16 px。
如果您將父元素的 font-size 設置為 20px,那麼子元素的 1em 將等於 20px。
假設您將子元素的 font-size 設置為 0.5em,那麼它的文字大小將是父元素文字大小的一半,即 10px。
em 的優點是它可以創建出相對於父元素的彈性設置,可以隨著父元素的改變而調整尺寸。
這對於構建具有可調整大小的 RWD(響應式網頁設計)非常有用,
因為元素的尺寸會根據不同螢幕尺寸和設備而自動調整。
💝
若希望在 h3 元素中的文字遇到容器邊界時自動換行,您可以使用 CSS 中的 word-wrap 或 word-break 屬性來實現這一點。
h3 {
word-wrap: break-word; /* 自動換行 */
}
h3 {
word-break: break-all; /* 在任意字符內換行 */
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Information</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/vue@3.3.0/dist/vue.global.js"></script>
</head>
<body>
<div class="member_system">
<h1>Member System Form</h1>
<h3 id="error_txt">{{ errorText }}</h3>
<form id="Posting_userForm" @submit.prevent="addUser">
<h3 @click="checked_toggle(0)">Adding User</h3>
<div v-show="checked[0]">
<label for="username">Username:</label>
<input type="text" id="username" name="username" placeholder="Enter username" v-model="formData.username"><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Enter email" v-model="formData.email"><br>
<label for="age">Age:</label>
<input type="number" id="age" name="age" placeholder="Enter age" v-model.number="formData.age"><br>
<input type="submit" value="Submit">
</div>
</form>
<form id="Updating_form" @submit.prevent="updateUser">
<h3 @click="checked_toggle(1)">Updating User</h3>
<div v-show="checked[1]">
<input type="text" id="userid2" name="userid2" placeholder="Enter userid2" v-model="formData2v.userid2v"><br>
<input type="text" id="username2" name="username2" placeholder="Enter username2" v-model="formData2v.username2v"><br>
<input type="email" id="email2" name="email2" placeholder="Enter email2" v-model="formData2v.email2v"><br>
<input type="number" id="age2" name="age2" placeholder="Enter age2" v-model.number="formData2v.age2v"><br>
<input type="submit" id="submit2" value="Submit2更新"><br>
</div>
</form>
<form id="Deleting" @submit.prevent="deleteUser">
<h3 @click="checked_toggle(2)">Deleting User</h3>
<div v-show="checked[2]">
<input type="text" id="delete_id" name="delete_id" placeholder="Enter delete_id" v-model="deleteId"><br>
<button id="delete" value="delete">delete</button> <br>
</div>
</form>
<form id="Finding_one_and_Finding_all" >
<h3 @click="checked_toggle(3)">Finding_one_and_Finding_all User</h3>
<div v-show="checked[3]">
<input type="text" id="find_one_id" name="find_one_id" placeholder="Enter find_one_id" v-model="findOneId"><br>
<!-- <input type="submit" id="find_one_btn" value="find_one_btn" @submit.prevent="findOneUser"> -->
<!-- <input type="submit" id="find_all_btn" value="find_all_btn" @submit.prevent="findAllUsers"> -->
<button @click.prevent="findOneUser" type="button" id="find_one_btn">find_one_btn</button> <!-- find_one -->
<button @click.prevent="findAllUsers" type="button" id="find_all_btn">find_all_btn</button><!-- find_all -->
</div>
</form>
<div type="value1">
<span id="find_OneOrAll_toggle" @click="value1_toggle()">Finding close</span><br/>
<span id="find_OneOrAll_txt" v-show="value1_checked" v-html="userText"></span>
</div>
</div>
<!-- 載入vue.js -->
<script src="vue.js" async></script>
</body>
</html>
💝
加入<script src="vue.js"></script>後,導致原本的<script src="script.js"></script>無法運行
原因有幾個:
加載順序問題: 當瀏覽器加載多個腳本時,腳本的加載順序對於依賴關係很重要。
如果script.js在vue.js之前加載,而您的script.js文件中依賴於 Vue.js 的一些功能或語法,
可能會因為 Vue.js 尚未加載完畢就執行script.js,導致錯誤。
如何解決這個問題?:
1. 確保兩個腳本之間沒有命名衝突或相互依賴的問題。
2. 確保vue.js在script.js之前加載。
💝
寫在.html中的@submit.prevent, v-model, v-model.number 指令講解。
@submit.prevent
v-model會和vue.js中的data()進行雙向綁定,
類似於username2v: document.getElementById('username2').value
data() {
return {
formData: {
username: '',
email: '',
age: '',
},
formData2v: {
userid2v: '',
username2v: '',
email2v: '',
age2v: ''
},
deleteId: '',
findOneId: ''
};
👉v-model常用於表單及元素來做雙向數據綁定,
EX: 當.html中,有一格<input,且有v-model="formData2v.userid2v 屬性,
則它就會和下面這個vue.js中的data()雙向綁定在一起。
formData2v: {
userid2v: '',
v-model 是通用的雙向綁定指令,
而 v-model.number 則是專門用於處理數字輸入的特殊情況,自動將值轉換為數字類型。
👉@submit.prevent 是 Vue.js 中的事件修飾符,用於防止表單預設的提交行為,同時觸發特定的事件處理器。
使用 @submit.prevent 可以在提交表單時防止瀏覽器執行預設的提交動作,同時調用 Vue 實例中的指定方法來處理提交事件。
以下是 @submit.prevent 的詳細說明:
1. submit 是一個事件綁定的指令,用於監聽表單提交事件。
2. prevent 是一個事件修飾符,用於阻止事件的默認行為。
它防止了表單提交時的默認行為,例如頁面刷新或頁面跳轉。
EX: 寫在.html中的 @submit.prevent="updateUser">
這個示例中的 @submit.prevent 會在表單提交時觸發vue.js中的 updateUser函式,並防止表單的默認提交行為。
這樣可以方便你使用 Vue 實例的方法來處理表單提交所需的邏輯,而不會導致頁面的刷新或跳轉。
vue.js
const app = Vue.createApp({
data() {
return {
checked: [false, false, false, false], // 包含4个boolin的数组,初始值均为 true
value1_checked: true,
errorText: '',
userText: '',
formData: {
username: '',
email: '',
age: '',
},
formData2v: {
userid2v: '',
username2v: '',
email2v: '',
age2v: ''
},
deleteId: '',
findOneId: ''
};
},
methods: {
checked_toggle (index) {
this.checked[index] = !this.checked[index]; // 切换相应表单的显示状态
},
value1_toggle () {
this.value1_checked = !this.value1_checked; // 切换相应表单的显示状态
if (this.value1_checked===true){
document.querySelector("#find_OneOrAll_toggle").textContent = "Finding close";
}else{
document.querySelector("#find_OneOrAll_toggle").textContent = "Finding open";
}
},
async addUser() {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(this.formData)
};
fetch('/users', requestOptions)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('New user created:', data);
this.errorText = JSON.stringify(data);
alert('New user created:' + this.errorText);
})
.catch(error => {
console.error('Error花:', error);
this.errorText = 'Post Error花' + JSON.stringify(error);
alert(this.errorText);
});
},
async updateUser() {
let requestOptions2 = {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(this.formData2v)
};
fetch(`/users/${this.formData2v.userid2v}`, requestOptions2)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('New updateUser created:', data);
this.errorText = JSON.stringify(data);
alert(this.errorText);
})
.catch(error => {
console.error('Error花:', error);
this.errorText = 'Put你ID連輸入都沒輸入' + JSON.stringify(error);
alert(this.errorText);
});
},
async deleteUser() {
const deleteUserOptions = {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
}
};
fetch(`/users/${this.deleteId}`, deleteUserOptions)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('New delete created:', data);
this.errorText = JSON.stringify(data);
alert(this.errorText);
})
.catch(error => {
console.error('Error花:', error);
this.errorText = 'delete你ID連輸入都沒輸入' + JSON.stringify(error);
alert(this.errorText);
});
},
async findOneUser() {
fetch(`/users/${this.findOneId}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('New find_one created:', data);
let userText = '';
data.forEach((res_user) => {
userText += `_id: ${res_user._id}; username: ${res_user.username}; email: ${res_user.email}; age: ${res_user.age} </br>`;
});
this.userText = userText;
})
.catch(error => {
console.error('Error花:', error);
});
},
async findAllUsers() {
fetch('/users')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('New Find created:', data);
let userText = '';
data.forEach((res_user) => {
userText += `_id: ${res_user._id}; username: ${res_user.username}; email: ${res_user.email}; age: ${res_user.age} </br>`;
});
this.userText = userText;
})
.catch(error => {
console.error('Error花:', error);
});
}}
})
app.mount('.member_system');
/*
checked: [true, true]。在這個陣列中,checked[0] 和 checked[1] 分別代表陣列中的第一個元素和第二個元素。
這裡的 checked[0] 對應陣列中的第一個元素,即 true,而 checked[1] 對應陣列中的第二個元素,同樣是 true。
*/
💝
checked:[true, true] 代表初始化了一個包含兩個布林值的陣列,即 [true, true],也為checked:[0, 0]。
在這個陣列中,checked[0] 和 checked[1] 分別代表陣列中的第一個元素和第二個元素。
這裡的 checked[0] 對應陣列中的第一個元素,即 true,
而 checked[1] 對應陣列中的第二個元素,同樣是 true。
如果使用fetch 方法從伺服器端取得數據,且該資料是包含多個使用者物件的JSON 數組,
則需要先將回應用response.json() 解析為JavaScript 物件或數組,
然後遍歷數組以取得每個使用者物件的屬性,例如age 屬性。
response.json(), JSON.parse(), JSON.stringify()介紹,延伸閱讀>>
在此例中,response.json() 傳回的是一個包含多個使用者物件的 JSON 陣列。
若要存取其中每個使用者物件的 age 屬性,可以使用.forEach() 方法。
.forEach() 方法用於遍歷數組中的每個使用者對象,並輸出每個使用者的 age 屬性值。
let userText = ''; // 用于保存所有用户信息的字符串
await fetch(`/users/${document.getElementById('find_one_id').value}`)
.then((response) => {
return response.json();
})
.then((response) => {
console.log('New find_one created:',response);
// response 是包含多个用户对象的数组,您需要遍历数组来获取每个用户的 属性
response.forEach((res_user) => {
userText += `_id: ${res_user._id}; username: ${res_user.username};
email: ${res_user.email}; age: ${res_user.age} <br>`;
// 将每个用户对象的属性添加到字符串中,使用换行符或其他分隔符分隔每个用户信息
document.querySelector("#find_OneOrAll_txt").innerHTML = userText;
// 将包含所有用户信息的字符串设置为 textContent
});
})
此圖為回傳2個JSON 數組
app.js
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const User = require('./models/user_shchema'); // 假設已經建立了User模型
// 連接至 MongoDB
mongoose.connect('mongodb://localhost:27017/my_database')
.then(() => {
console.log('成功連接至 MongoDB');
})
.catch((err) => {
console.error('連接失敗:', err);
});
// 設置靜態資源目錄(假設HTML檔案存放在 public 目錄下)
const path = require("path")
const static_path = path.join(__dirname, "public") //變成絕對路徑 D:\qqq\public,nodejs使用絕對路徑較安全可靠,在 Node 中使用相對路徑進行檔案讀取是很危險的, 建議一律都透過絕對路徑的方式來處理
app.use(express.static(static_path))
//app.use(express.static('.'));
app.use(express.urlencoded({ extended: true })); // 處理 URL 編碼的資料
app.use(express.json()); //這個必須存在
//Post新增用戶, POST請求的路由處理
app.post('/users', async (req, res) => {
var newUser = {
username: req.body.username,
email: req.body.email,
age: req.body.age,}
console.log("newUser",newUser);
await User.create(newUser)
.then(createdUser => {
console.log(createdUser);
res.status(201).json(createdUser); // 返回從資料庫返回的新建用戶資料
})
.catch (error => {
res.status(500).json({ message: "qqq POST 錯誤" });
})
});
//Put更新用戶
app.put('/users/:id', async function (req, res) {
const updatedFields = {}; // 創建一個空物件來存儲要更新的欄位
// 檢查並將用戶輸入的欄位添加到要更新的物件中
if (req.body.username2v) {
updatedFields.username = req.body.username2v;
}
if (req.body.age2v) {
updatedFields.age = req.body.age2v;
}
if (req.body.email2v) {
updatedFields.email = req.body.email2v;
}
await User.findOneAndUpdate(
{
"_id": req.params.id, //req.params.id 的值,因為它是在路由中 :id 的位置提供的值。
}, // 條件,選擇要更新的文檔
{ $set:
updatedFields // 使用包含有輸入值的欄位的物件進行更新
},
{ new: true } // 選項,返回更新後的文檔
)
.then(updatedUser => {
// ...處理更新後的用戶
console.log('更新後的用戶:', updatedUser);
console.log('updatedFields', updatedFields);
res.status(201).json(updatedUser);
})
.catch(error => {
console.error('更新用戶時出錯:', error);
res.status(500).json({ message: "qqq put更新 ID出錯ㄌ" ,id: req.params.id});
});
})
//delete刪除用戶
app.delete('/users/:id', async (req, res) => {
await User.deleteOne({ _id: req.params.id })
.then(delete_user => {
console.log("delete: "+JSON.stringify(delete_user),"delete_ID: "+req.params.id);
res.status(201).json(delete_user); // 返回從資料庫返回的新建用戶資料
})
.catch (error => {
res.status(500).json({ message: "Delete qqq 沒有這個ID啦" });
})
});
//Get個別別用戶
app.get('/users/:id', async function (req, res) {
await User.find({"_id": req.params.id})
.then(FindUser => {
console.log('用戶:', FindUser);
res.status(201).json(FindUser);
})
.catch (error => {
console.error('更新用戶時出錯:', error);
res.status(500).json({ message: "qqq Get個別 沒有這個用戶" });
})
})
//Get全部用戶
app.get('/users', async function (req, res) {
await User.find({})
.then(FindUser => {
console.log('用戶列表:', FindUser);
res.status(201).json(FindUser);
})
.catch (error => {
console.error('更新用戶時出錯:', error);
res.status(500).json({ message: "qqq Get全部 出錯了" });
})
})
// 監聽端口
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Express 伺服器已啟動,監聽在端口 ${PORT}`);
});
user_shchema.js
const mongoose = require('mongoose');
// 定義模型 Schema
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: String,
email: String,
age: Number
}, { collection: 'my_custom_users' }); // 在這裡指定集合名稱為 my_custom_users collection;
// 創建 User 模型
const User = mongoose.model('User', userSchema);
/*創建 User 模型:透過 mongoose.model('User', userSchema) 來創建名為 User 的 Mongoose 模型。
使用 mongoose.model 方法將定義的模式 userSchema 轉換為模型 User */
module.exports = User;
/*匯出模型:module.exports = User;
將創建的 User 模型透過 module.exports 導出,使其可以在其他文件中引入和使用。 */
首頁
新增會員
更新會員資料
尋找各別單一會員
尋找所有會員
刪除會員
沒有留言:
張貼留言
喜歡我的文章嗎? 喜歡的話可以留言回應我喔! ^^