EE308FZ_First Assignment_Contacts Management Web App
EE308 - First Assignment:前后端分离通讯录系统
Course for This Assignment: EE308FZ
Assignment Requirements: This assignment requires us to develop a contacts management system that has three basic functions: add a contact, remove a contact, and edit the contact’s information. The coding requires frontend and backend separation.
Objectives of This Assignment: Implementing a Web-based Contact System with Frontend-Backend Separation, Mastering API Design, Cross-Origin Handling, and Cloud Deployment Processes.
Name: Yuxiang Xie(谢宇翔)
FZU_ID: 832301327
MU_ID: 23124237
Other References:
目录
1. Git Repository Link and Code Standards Link
2. PSP Table
| 任务 | 预估时间(分钟) | 实际时间(分钟) |
|---|---|---|
| 需求分析 | 30 | 45 |
| 前端页面开发 | 120 | 150 |
| 后端开发(Django) | 120 | 180 |
| 前后端联调 | 90 | 120 |
| 阿里云部署 | 60 | 70 |
| 博客撰写 | 60 | 120 |
| 总计 | 480 | 685 |
时间单位为分钟(min)
3. Presentation of the Finished Product
所有功能均已实现:增、删、改,数据存储于阿里云服务器 SQLite 数据库。
图 1:前端界面
访问地址:
https://markspro32.github.io/Yuxiang-Xie_832301327_Contacts-Web-App_front_end/
图 2:添加联系人与列表显示同步
输入姓名、邮箱、电话 → 点击 “Add Contact”
数据从后端 API (http://8.138.121.2:8000/contacts/) 实时加载
图3: 联系人列表展示
图 4: 添加联系人时缺少字段,系统提示
图 5: 添加联系人时格式出错,系统提示
图 6:编辑联系人
修改后点击 “Save”,数据同步到数据库
图 7:删除联系人
点击 “Delete” → 确认后移除
图 8:后端 API 测试 (在Postman中进行测试)
图 9:后端API测试2
向后端发送
GET /contacts/请求,成功返回 JSON 数据,如图所示
这说明:
–后端API正在运行
–Django REST框架返回JSON数据
–后端数据库已连接
–云端部署正常运作
– 前端和Postman均可访问后端
–没有将数据存在网页缓存
图 10:阿里云服务器运行 Django
Django Cloud Server
python manage.py runserver 0.0.0.0:8000
4. Design and Implementation Process
本次作业的前端我是用html进行网页框架搭建,用css进行网页美化,使用javascript进行前后端逻辑交互。后端我采用Django框架进行开发,在views.py里编写视图函数,在model.py里对联系人进行建模,设置名字,邮箱和电话三个对象,在url.py中配置了项目总路由和app路由,在设置中配置跨域访问,最终实现前端和后端的交互,通讯录网页app成功在本地跑了起来。接下来,我把前后端项目通过git推送到了GitHub仓库。之后,我到阿里云官网租借云服务器,我在云服务器上下载了必备的工具,如python 3.11.0以及git for windows等必要组件。通过GitHub这个中间仓库,云服务器得以在上面拉取我的项目,并部署在了云端,这样通讯录网页app便可以通过网址,在各个设备上直接访问并使用了。
5. Code Explanation
前端核心代码
a.加载联系人
// ====== Load Contacts ======
async function loadContacts() {
try {
console.log('Starting to load contacts...');
const response = await fetch(API_BASE);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contacts = await response.json();
console.log(' Loaded contacts:', contacts);
renderContacts(contacts);
} catch (error) {
console.error('Failed to load contacts:', error);
showError(`Failed to load contacts: ${error.message}`);
}
}
• 向后台API发送GET请求以获取所有联系人。
• 将返回的JSON数据转换为JavaScript对象。
• 调用renderContacts()更新用户界面。
• 如果请求失败,则显示错误信息。
目的:从后端数据库加载最新的联系人列表。
b.渲染联系人列表
// ====== RENDER CONTACTS ======
function renderContacts(contacts) {
contactsList.innerHTML = '';
if (contacts.length === 0) {
contactsList.innerHTML = '<li>No contacts yet. Add one below!</li>';
return;
}
contacts.forEach(contact => {
const li = document.createElement('li');
li.innerHTML = `
<div>
<strong>${escapeHtml(contact.name)}</strong><br>
Email: ${escapeHtml(contact.email)}<br>
Phone: ${escapeHtml(contact.phone)}
</div>
<div>
<button class="edit-btn" data-id="${contact.id}">Edit</button>
<button class="delete-btn" data-id="${contact.id}">Delete</button>
</div>
`;
contactsList.appendChild(li);
});
• 清空当前联系人列表。
• 如果没有联系人,则显示占位符信息。
• 否则,遍历每个联系人并动态创建相应的元素。
• 为每个联系人添加编辑和删除按钮。
用途:显示联系人信息并提供交互式操作按钮。
c.添加联系人
// ====== ADD CONTACT ======
async function handleAddContact(e) {
e.preventDefault();
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const phone = document.getElementById('phone').value;
try {
const response = await fetch(API_BASE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, phone })
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
// Clear the form and refresh the list after you add a contact
contactForm.reset();
loadContacts();
console.log('Contact added');
} catch (error) {
console.error(' Failed to add contact:', error);
showError(`Failed to add contact: ${error.message}`);
}
}
• 当用户提交“添加联系人”表单时触发。
• 发送包含新联系人数据(姓名、电子邮件、电话)的 JSON 格式 POST 请求。
• 如果成功:
• 清空输入表单,
• 调用 loadContacts() 刷新联系人列表。
• 如果出现错误,则显示错误消息。
用处:允许用户创建新的联系人
d.删除联系人
// ====== DELETE CONTACT ======
async function deleteContact(id) {
console.log(` Delete button clicked for ID: ${id}`);
if (!confirm('Are you sure you want to delete this contact?')) return;
try {
const response = await fetch(`${API_BASE}${id}/`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
loadContacts();
console.log('Contact deleted');
} catch (error) {
console.error(' Failed to delete contact:', error);
showError(`Failed to delete contact: ${error.message}`);
}
}
• 删除前会请求确认。
• 向 /contacts/<id_xxx>/ 发送 DELETE 请求。
• 删除成功后,重新加载联系人列表。
用途:从用户界面和后端数据库中删除联系人。
e.编辑联系人
// ====== EDIT CONTACT ======
function showEditForm(id) {
const contactElement = document.querySelector(`button[data-id="${id}"]`).closest('li');
const contactName = contactElement.querySelector('strong').textContent;
const contactEmail = contactElement.querySelectorAll('div')[0].textContent.split('Email: ')[1].split('Phone: ')[0].trim();
const contactPhone = contactElement.querySelectorAll('div')[0].textContent.split('Phone: ')[1].trim();
contactElement.innerHTML = `
<div>
<input type="text" class="edit-name" value="${escapeHtml(contactName)}" />
<input type="email" class="edit-email" value="${escapeHtml(contactEmail)}" />
<input type="text" class="edit-phone" value="${escapeHtml(contactPhone)}" />
</div>
<div>
<button class="save-btn" data-id="${id}">Save</button>
<button class="cancel-btn">Cancel</button>
</div>
`;
contactElement.querySelector('.save-btn').addEventListener('click', () => saveContact(id));
contactElement.querySelector('.cancel-btn').addEventListener('click', loadContacts);
}
async function saveContact(id) {
const name = document.querySelector('.edit-name').value;
const email = document.querySelector('.edit-email').value;
const phone = document.querySelector('.edit-phone').value;
try {
const response = await fetch(`${API_BASE}${id}/`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, phone })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
loadContacts();
console.log('Contact updated');
} catch (error) {
console.error(' Failed to update contact:', error);
showError(`Failed to update contact: ${error.message}`);
}
}
• showEditForm() 函数会将联系人显示界面替换为输入框,以便用户可以修改联系人信息。
• saveContact() 函数会发送 PUT 请求来更新后端联系人信息。
• 更新成功后,调用 loadContacts() 函数刷新联系人列表。
目的:提供无需页面重新加载即可直接编辑联系人信息的体验。
跨域配置关键:
# 允许跨域
CORS_ALLOW_ALL_ORIGINS = True
# 设置允许的主机(必须包含你的公网 IP)
ALLOWED_HOSTS = ['8.138.121.2', 'localhost', '127.0.0.1']
# 安装 CORS 插件
INSTALLED_APPS = [
...
'corsheaders',
'contacts_app',
]
# 将 CORS 中间件放在最前面
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 必须放在第一行
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
配置后端 API 地址(阿里云服务器公网 IP):
const API_BASE = 'http://8.138.121.2:8000/contacts/';
后端核心代码
Contacts_app/model.py
from django.db import models
class Contact(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
phone = models.CharField(max_length=100)
def __str__(self):
return self.name
Contacts_app/views.py
这里包含了增,删,改的核心代码。
该项目的后端提供用于管理联系人的 RESTful API。
它支持通过 HTTP 请求进行创建、读取、更新和删除(CRUD)操作。
@method_decorator(csrf_exempt, name='dispatch')
class ContactListView(View):
...
@method_decorator(csrf_exempt, name='dispatch')
class ContactDetailView(View):
...
ContactListView 和 ContactDetailView 是基于类的视图,它们通过请求方法(GET、POST、PUT、DELETE)来组织逻辑。
Helper Function
def parse_json_body(request):
...
• 此函数从请求体中提取JSON数据
• 当JSON格式错误时可防止报错
• 解析失败时返回None → 便于早期错误处理
联系人列表视图 — 处理列表和创建操作(GET 与 POST)
| 方法 | URL | 用途 |
|---|---|---|
| GET | /contacts/ | 返回所有联系人列表 |
| POST | /contacts/ | 添加新联系人 |
GET请求示例行为
contacts = list(Contact.objects.values('id', 'name', 'email', 'phone'))
return JsonResponse(contacts, safe=False)
• 从数据库获取所有联系人记录。
• 将其转换为JSON对象列表。
• 返回给前端进行显示。
POST 请求事例行为
contact = Contact.objects.create(name=..., email=..., phone=...)
• 从前端接收JSON数据
• 校验必填字段(姓名、邮箱、电话)
• 将新联系人存入SQLite数据库
• 返回已创建联系人的数据及ID
⸻
联系人详情视图 — 处理单个联系人的查看、更新和删除
| 方法 | URL | 用途 |
|---|---|---|
| GET | /contacts/<id_xxx> | 获取单个联系人 |
| PUT | /contacts/<id_xxx> | 修改现有联系人 |
| DELETE | /contacts/<id_xxx> | 删除联系人 |
GET请求示例行为
contact = Contact.objects.get(id=contact_id)
• 根据ID查找联系人。
• 如果未找到 → 发送404错误。
• 否则返回该联系人的详细信息。
PUT请求事例
contact.name = data.get('name', contact.name)
contact.save()
- 从前端接收修订后的数据。
- 仅更新提供的字段。
- 将更新后的联系人保存到数据库。
DELETE 请求事例
contact.delete()
永久地从数据库中删除选定的联系人。
使用@csrf_exempt
由于本项目采用前后端分离架构,POST/PUT/DELETE请求来自JavaScript的fetch调用而非Django表单。为避免CSRF令牌验证错误,视图函数通过以下装饰器豁免校验:
@method_decorator(csrf_exempt, name='dispatch')
6. Personal Journey and Learnings
通过这次的前后端分离式通讯录的搭建,我了解了软件开发的大致流程,对前后端的框架有了初步的了解,这为我以后从事软件开发相关工作奠定了基础。
对于前端,我自学了html,css,javascript等前端语言,但是并没有学完,所以以后有时间还要继续学习,继续丰富前端开发经验。
对于后端,我了解到Django框架更加适合大规模的开发工作,在生产实践中相比于Flask更加实用,于是我对Django框架进行了初步的了解和学习。在一开始的时候,只是跟着b站教程一步一步敲代码,其中许多函数和逻辑还是云里雾里,但是一套教程看下来,也明白了大致的操作逻辑。我对于Django的理解还不够深入,以后还需要多花时间学习。
部署服务器,在这次项目中对我来说是一个不小的挑战。服务器的各种端口,各种协议在我这里都是全新的概念,而且使用git推送项目也是我第一次接触,这增加了我的学习成本,加大了开发难度。一开始我只是把后端部署到了服务器上,点开网址里面是json文件,存的是后端数据内容,后来我通过学习了解到需要将前端代码部署到后端中的tenmplates和static文件夹,前端静态文件通过访问后端API,才能在网页中显示…
克服了种种困难,我终于做出了前后端分离的通讯录项目。这次的作业对于我而言绝对是一次历练,是从不会到会的探索,是解决一个又一个困难并从中收获颇丰的一次学习经历,这对我受益匪浅。
更多推荐

所有评论(0)