Registration API
This API uses a singleton pattern to refactor the cloud communication SMS sending demo (please review when free).
API Documentation
URL: /avi/v1_0/users/
Method: POST
Data format: JSON
Required parameters: mobile, sms_code, password
Response format: JSON
Response parameters: errno, errmsg
Backend Logic
- Receive parameters
resp_dict = request.get_json()
mobile = resp_dict.get('mobile')
sms_code = resp_dict.get('sms_code')
password = resp_dict.get('password')
- Validate data integrity
if not all([mobile, sms_code, password]):
resp = {
'errno': RET.PARAMERR,
'errmsg': 'Incomplete parameters'
}
return jsonify(resp)
- Validate mobile phone number
if not re.match(r'1[34578]\d{9}', mobile):
resp = {
'errno': RET.DATAERR,
'errmsg': 'Invalid mobile number format'
}
return jsonify(resp)
-
Business logic processing
- Retrieve SMS verification code from Redis:
real_sms_code = redis_store.get('sms_code_' + mobile)
- Check if the verification code has expired:
if real_sms_code is None:
resp = {
'errno': RET.NODATA,
'errmsg': 'SMS verification code has expired'
}
return jsonify(resp)
- Check if the user entered the correct verification code:
if real_sms_code != sms_code:
resp = {
'errno': RET.DATAERR,
'errmsg': 'Incorrect SMS verification code'
}
return jsonify(resp)
- Delete the verification code from Redis:
redis_store.delete('sms_code_' + mobile)
- Save user data (with database rollback):
# Create user and save data
user = User(name=mobile, mobile=mobile)
# Password handling should be delegated to the model class
user.password = password
try:
db.session.add(user)
db.session.commit()
except Exception as e:
# a. Log the error
logging.error(e)
# b. Rollback the transaction
db.session.rollback()
# c. Return error response
resp = {
'errno': RET.NODATA,
'errmsg': 'SMS verification code has expired'
}
return jsonify(resp)
- Set session information:
session['user_id'] = user.id
session['user_name'] = mobile
session['mobile'] = mobile
- Return success response
resp = {
'errno': RET.OK,
'errmsg': 'Registration successful'
}
return jsonify(resp)
User Login API
API Documentation
URL: /avi/v1_0/session/
Method: POST
Data format: JSON
Required parameters: mobile, password
Response format: JSON
Response parameters: errno, errmsg
Backend Logic Analysis
- Receive parameters (mobile, password).
- Parameter validation: overall validation, validate mobile number integrity.
- Get user's IP address, retrieve the number of incorrect attemtps from Redis, check if attempts exceed 5.
- Verify username and password match.
- If correct, delete the record of incorrect attempts from Redis.
- Set session information.
- Return result to frontend.
Implementation Steps
- Receive parameters
resp_json = request.get_json()
mobile = resp_json.get('mobile')
password = resp_json.get('password')
- Check parameter integrity
if not all([mobile, password]):
return jsonify(errno=RET.PARAMERR, errmsg='Incomplete parameters')
- Check incorrect attempts in Redis
If the number of incorrect attempts exists and is >= 5, reject login immediately. User can try again after 10 minutes.
user_ip = request.remote_addr
access_counts = redis_store.get('access_' + user_ip)
if access_counts is not None and int(access_counts) >= 5:
return jsonify(errno=RET.REQERR, errmsg='Maximum attempts exceeded')
- Query database and verify user credentials
If the user does not exist or the password does not match, increment the error count and return an error.
user = User.query.filter_by(mobile=mobile).first()
if user is None or not user.check_password(password):
# Increment error count and set expiry
try:
# incr: increment error count
redis_store.incr('access_' + user_ip)
# expire: first argument is key, second argument is expiry time in seconds
redis_store.expire('access_' + user_ip, 600)
except Exception as e:
logging.error(e)
return jsonify(errno=RET.LOGINERR, errmsg='Invalid username or password')
- Clear incorrect attempt count on successful login
redis_store.delete('access_' + user_ip)
- Set session and return success
session['user_id'] = user.id
session['user_name'] = mobile
session['mobile'] = mobile
return jsonify(errno=RET.OK, errmsg='Login successful')
Check Login Status API
API Documentation
URL: /sessions
Method: GET
Response format:
{
"data": {
"name": "18001225173"
},
"errmsg": "true",
"errno": "0"
}
Backend Logic
Attempt to retrieve the user's name from the session. If it exists, the user is logged in; otherwise, not.
name = session.get("user_name")
if name is not None:
return jsonify(errno=RET.OK, errmsg="true", data={"name": name})
else:
return jsonify(errno=RET.SESSIONERR, errmsg="false")
Login Required Decorator
If the user is logged in, store the user ID in the g variable. The g variable exists during the application's lifecycle.
def login_required(view_func):
"""Check user login status"""
@wraps(view_func)
def wrapper(*args, **kwargs):
user_id = session.get("user_id")
if user_id is not None:
# User is logged in
# Use g object to store user_id for use in view functions
# For example, when setting avatar, session data is accessed again.
# Using g variable avoids multiple Redis accesses.
g.user_id = user_id
return view_func(*args, **kwargs)
else:
# User not logged in
resp = {
"errno": RET.SESSIONERR,
"errmsg": "User not logged in"
}
return jsonify(resp)
return wrapper
Logout API
API Documentation
URL: /sessions
Method: DELETE
Backend Logic
Clear session data.
session.clear()
return jsonify(errno=RET.OK, errmsg="OK")