Nội dung bài học
- REST và RESTful API
- Web Service
- Phân tích XML
- Mock Server
REST và RESTful API
REST (Representational State Transfer - Chuyển đổi trạng thái đại diện) là một kiến trúc cho các hệ thống mạng. Trong REST, mỗi địa chỉ URI được xem như một tài nguyên (nguồn lực) có thể biểu diễn dưới dạng văn bản, hình ảnh, dịch vụ, v.v.
Khái niệm "chuyển đổi trạng thái" nghĩa là chúng ta sử dụng các phương thức như POST/PUT để gửi thông tin trạng thái mới và thay đổi trạng thái của tài nguyên.
URI khác URL như thế nào?: URL là một loại URI chứa đầy đủ thông tin về giao thức (như https://www.example.com), trong khi URN là một loại URI không chứa giao thức (như /doc/1.html). Cả hai đều có thể định vị duy nhất một tài nguyên.
Đặc điểm của RESTful API:
- Sử dụng giao thức HTTPS
- Sử dụng tên miền riêng cho API (ví dụ: https://api.example.com)
- Quản lý phiên bản trong đường dẫn (ví dụ: https://api.example.com/v1/)
- Đường dẫn chỉ chứa danh từ, có thể dùng số nhiều để biểu thị tập hợp tài nguyên
- Một đường dẫn hỗ trợ nhiều phương thức HTTP: GET (lấy dữ liệu), POST (tạo mới), PUT/PATCH (cập nhật), DELETE (xóa)
- Hỗ trợ lọc thông tin qua tham số URL > Ưu tiên sử dụng JSON thay vì XML
- Xác thực bằng OAuth2.0, Basic Auth hoặc token, tránh sử dụng Cookie và Session
Ví dụ thực tế:
API của một dịch vụ mạng xã hội:
https://api.socialnetwork.com
Để lấy thông tin người dùng:
GET https://api.socialnetwork.com/profile
Để cập nhật hồ sơ:
PUT https://api.socialnetwork.com/profile
Dữ liệu gửi đi:
{
"first_name": "Nguyen",
"last_name": "Van A",
"email": "nguyenvana@example.com",
"bio": "Developer at Tech Company",
"location": "Hanoi, Vietnam",
"website": "https://example.com",
"birth_date": "1990-01-01",
"interests": ["programming", "reading", "traveling"],
"settings": {
"privacy_level": 2,
"show_email": false,
"allow_messages": true
}
}
Web Service
Web Service là một giải pháp RPC (Gọi thủ tục từ xa) đa nền tảng, cho phép các ứng dụng trên các ngôn ngữ lập trình khác nhau (như Java và Python) có thể giao tiếp với nhau.
Web Service dựa trên giao thức SOAP, sử dụng XML (ngôn ngữ đa nền tảng) để truyền thông tin về việc gọi phương thức từ xa và kết quả trả về.
Các thành phần chính:
- SOAP (Simple Object Access Protocol): Giao thức đơn giản truy cập đối tượng, dựa trên XML và sử dụng HTTP để truyền tải
- XML (eXtensible Markup Language): Ngôn ngữ đánh dấu có khả năng mở, tương tự JSON nhưng cấu trúc phức tạp hơn
- WSDL (Web Service Description Language): Ngôn ngữ mô tả dịch vụ Web, cung cấp thông tin về cách gọi đối tượng từ xa
Cấu trúc SOAP:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<!-- Thông tin header tùy chọn -->
</soap:Header>
<soap:Body>
<!-- Nội dung chính của yêu cầu -->
</soap:Body>
</soap:Envelope>
Sử dụng Python để gọi Web Service:
Cài đặt thư viện:
pip install pysimplesoap
Ví dụ sử dụng:
from pysimplesoap.client import SoapClient
# Kết nối đến dịch vụ Web
client = SoapClient(
wsdl='http://example.com/service?wsdl',
trace=True
)
# Gọi phương thức từ xa
result = client.addUser(
name='Tran Thi B',
password='654321'
)
# In kết quả
print(result['addUserResult'])
Cách sử dụng thư viện requests:
import requests
url = 'http://example.com/service/'
# Tạo yêu cầu SOAP
soap_request = '''<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<addUser>
<name>Le Van C</name>
<password>789456</password>
</addUser>
</soap:Body>
</soap:Envelope>
'''.encode('utf-8')
# Gửi yêu cầu
headers = {
'Content-Type': 'text/xml; charset=utf-8',
'SOAPAction': 'addUser'
}
response = requests.post(url, data=soap_request, headers=headers)
print(response.text)
Phân tích XML
XML (eXtensible Markup Language) là ngôn ngữ đánh dấu có khả năng mở, sử dụng cặp thẻ <tag></tag> để cấu trúc dữ liệu dưới dạng cây phân cấp.
Ví dụ XML:
<sanpham>
<sach>
<ten>Lập trình Python</ten>
<tacgia>Nguyen Van A<tacgia>
<namxuatban>2023</namxuatban>
<gia>:299000<gia>
</sach>
<sach>
<ten>Hướng dẫn JavaScript</ten>
<tacgia>Tran Thi B<tacgia>
<namxuatban>2022</namxuatban>
<gia>:349000<gia>
</sach>
</sanpham>
Phân loại thành phần XML:
- Phần tử gốc: Phần tử cao nhất trong cấu trúc cây (ví dụ: sanpham)
- Phần tử cha, con: Phần tử chứa phần tử khác là phần tử cha, phần tử chứa là phần tử con
- Phần tử cùng cấp: Các phần tử có cùng cấp độ (ví dụ: các thẻ ten)
- Thẻ, thuộc tính, văn bản: Tên thẻ, giá trị thuộc tính và nội dung văn bản
Phân tích XML bằng Python:
from xml.etree import ElementTree
# Chuỗi XML mẫu
xml_data = '''<sanpham>
<sach>
<ten>Lập trình Python</ten>
<tacgia>Nguyen Van A<tacgia>
<namxuatban>2023</namxuatban>
<gia>:299000<gia>
</sach>
<sach>
<ten>Hướng dẫn JavaScript</ten>
<tacgia>Tran Thi B<tacgia>
<namxuatban>2022</namxuatban>
<gia>:349000<gia>
</sach>
</sanpham>
'''
# Phân tích XML
root = ElementTree.fromstring(xml_data)
# Lấy tên phần tử gốc
print(f"Phần tử gốc: {root.tag}")
# Tìm tất cả các sách
books = root.findall('sach')
print(f"Số lượng sách: {len(books)}")
# Lấy thông tin từng sách
for i, book in enumerate(books):
title = book.find('ten').text
author = book.find('tacgia').text
year = book.find('namxuatban').text
price = book.find('gia').text
print(f"\nSách {i+1}:")
print(f"Tên: {title}")
print(f"Tác giả: {author}")
print(f"Năm xuất bản: {year}")
print(f"Giá: {price} VND")
Trình chọn XPath:
- Đường dẫn: Chọn theo đường dẫn (ví dụ: /sanpham/sach/ten)
- Tên thẻ: Chọn tất cả các thẻ có tên tương ứng
- *: Chọn tất cả các phần tử
- .: Chọn phần tử hiện tại
- ..: Chọn phần tử cha
- //: Chọn tất cả các phần tử ở mọi cấp độ
- [@tên_thuộc tính]: Chọn các phần tử có thuộc tính tương ứng
- [@tên_thuộc tính='giá_trị']: Chọn các phần tử có thuộc tính bằng giá trị chỉ định
- [tên_thẻ_con]: Chọn các phần tử có thẻ con tương ứng
- [tên_thẻ_con='văn_bản']: Chọn các phần tử có thẻ con với văn bản cụ thể
Mock Server
Mock (mô phỏng) là quá trình tạo ra các đối tượng ảo để thay thế cho các đối tượng phức tạp, khó tạo hoặc chưa sẵn có trong quá trình kiểm thử. Lợi ích chính của Mock là giảm sự phụ thuộc giữa các thành phần phần mềm, cho phép phát triển độc lập.
Mock Server là một máy chủ mô phỏng API, cho phép nhanh chóng tạo ra các API giả để sử dụng trong quá trình phát triển và kiểm thử.
Ứng dụng của Mock Server:
- Dự án tách biệt giữa frontend và backend
- API phụ thuộc vào hệ thống bên thứ ba chưa sẵn có
- API phụ thuộc phức tạp hoặc không ổn định
- API chưa được phát triển hoàn chỉnh nhưng cần để test frontend
Tạo Mock Server bằng Python và Flask:
Cài đặt Flask:
pip install flask
Ví dụ code:
from flask import Flask, request, jsonify, abort
import random
import datetime
app = Flask(__name__)
@app.route("/api/auth/login", methods=["POST"])
def login():
if not request.json or not 'username' in request.json or not 'password' in request.json:
abort(400) # Bad request
username = request.json['username']
password = request.json['password']
# Mô phỏng xác thực
responses = [
{
"status": "success",
"code": 200,
"message": "Đăng nhập thành công",
"data": {
"user_id": 12345,
"username": username,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"expires_at": (datetime.datetime.now() + datetime.timedelta(hours=24)).isoformat()
}
},
{
"status": "error",
"code": 401,
"message": "Tên đăng nhập hoặc mật khẩu không đúng",
"data": None
},
{
"status": "error",
"code": 429,
"message": "Đăng nhập quá nhiều lần, vui lòng thử lại sau 5 phút",
"data": None
}
]
# Mô phỏng xác thực ngẫu nhiên
if username == "admin" and password == "admin123":
return jsonify(responses[0])
elif username == "locked" and password == "locked123":
return jsonify(responses[2])
else:
return jsonify(responses[1])
@app.route("/api/products", methods=["GET"])
def get_products():
# Mô phỏng danh sách sản phẩm
products = [
{
"id": 1,
"name": "Laptop Dell XPS 13",
"price": 35000000,
"currency": "VND",
"category": "Laptop",
"in_stock": True,
"rating": 4.5
},
{
"id": 2,
"name": "iPhone 14 Pro",
"price": 28000000,
"currency": "VND",
"category": "Điện thoại",
"in_stock": True,
"rating": 4.7
},
{
"id": 3,
"name": "Tai nghe Sony WH-1000XM4",
"price": 7000000,
"currency": "VND",
"category": "Phụ kiện",
"in_stock": False,
"rating": 4.8
}
]
# Lấy tham số từ query string
category = request.args.get('category')
limit = request.args.get('limit', default=10, type=int)
# Lọc sản phẩm theo danh mục nếu có
if category:
products = [p for p in products if p['category'].lower() == category.lower()]
# Giới hạn số lượng sản phẩm trả về
products = products[:limit]
return jsonify({
"status": "success",
"code": 200,
"message": "Lấy danh sách sản phẩm thành công",
"data": products,
"total": len(products)
})
if __name__ == '__main__':
app.run(debug=True, port=5000)
Để chạy server Mock:
python mock_server.py
Server sẽ chạy trên địa chỉ http://localhost:5000 và cung cấp các API giả để kiểm thử.