Flask项目(二)Restful风格、图片、短信验证码、注册登录

Restful风格

REST:Representational State Transfer的缩写,翻译:“具象状态传输”。一般解释为“表现层状态转换”。

REST是设计风格而不是标准。是指客户端和服务器的交互形式。我们需要关注的重点是如何设计REST风格的网络接口。

REST的特点:

  • 具象的。一般指表现层,要表现的对象就是资源。比如,客户端访问服务器,获取的数据就是资源。比如文字、图片、音视频等。

  • 表现:资源的表现形式。txt格式、html格式、json格式、jpg格式等。浏览器通过URL确定资源的位置,但是需要在HTTP请求头中,用Accept和Content-Type字段指定,这两个字段是对资源表现的描述。

  • 状态转换:客户端和服务器交互的过程。在这个过程中,一定会有数据和状态的转化,这种转化叫做状态转换。其中,GET表示获取资源,POST表示新建资源,PUT表示更新资源,DELETE表示删除资源。HTTP协议中最常用的就是这四种操作方式。

RESTful架构:

  • 每个URL代表一种资源;
  • 客户端和服务器之间,传递这种资源的某种表现层;
  • 客户端通过四个http动词,对服务器资源进行操作,实现表现层状态转换。

如何设计符合RESTful风格的API

一、域名:

将api部署在专用域名下:

http://api.example.com
或者将api放在主域名下:

http://www.example.com/api/

二、版本:

将API的版本号放在url中。

http://www.example.com/app/1.0/info
http://www.example.com/app/1.2/info

三、路径:

路径表示API的具体网址。每个网址代表一种资源。 资源作为网址,网址中不能有动词只能有名词,一般名词要与数据库的表名对应。而且名词要使用复数。

错误示例:

http://www.example.com/getGoods
http://www.example.com/listOrders

正确示例:

#获取单个商品
http://www.example.com/app/goods/1
#获取所有商品
http://www.example.com/app/goods

四、使用标准的HTTP方法:

对于资源的具体操作类型,由HTTP动词表示。 常用的HTTP动词有四个。

GET SELECT :从服务器获取资源。
POST CREATE :在服务器新建资源。
PUT UPDATE :在服务器更新资源。
DELETE DELETE :从服务器删除资源。
示例:

#获取指定商品的信息
GET http://www.example.com/goods/ID
#新建商品的信息
POST http://www.example.com/goods
#更新指定商品的信息
PUT http://www.example.com/goods/ID
#删除指定商品的信息
DELETE http://www.example.com/goods/ID

五、过滤信息:

如果资源数据较多,服务器不能将所有数据一次全部返回给客户端。API应该提供参数,过滤返回结果。 实例:

#指定返回数据的数量
http://www.example.com/goods?limit=10
#指定返回数据的开始位置
http://www.example.com/goods?offset=10
#指定第几页,以及每页数据的数量
http://www.example.com/goods?page=2&per_page=20

六、状态码:

服务器向用户返回的状态码和提示信息,常用的有:

200 OK  :服务器成功返回用户请求的数据
201 CREATED :用户新建或修改数据成功。
202 Accepted:表示请求已进入后台排队。
400 INVALID REQUEST :用户发出的请求有错误。
401 Unauthorized :用户没有权限。
403 Forbidden :访问被禁止。
404 NOT FOUND :请求针对的是不存在的记录。
406 Not Acceptable :用户请求的的格式不正确。
500 INTERNAL SERVER ERROR :服务器发生错误。

七、错误信息:

一般来说,服务器返回的错误信息,以键值对的形式返回。

{
    error:'Invalid API KEY'
}

八、响应结果:

针对不同结果,服务器向客户端返回的结果应符合以下规范。

#返回商品列表
GET    http://www.example.com/goods
#返回单个商品
GET    http://www.example.com/goods/cup
#返回新生成的商品
POST   http://www.example.com/goods
#返回一个空文档
DELETE http://www.example.com/goods

九、使用链接关联相关的资源:

在返回响应结果时提供链接其他API的方法,使客户端很方便的获取相关联的信息。

十、其他:

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

图片验证码

在这里插入图片描述
将生成验证码的包、response_code.py放入utils
在这里插入图片描述
api_1_0/verify_code.py

# coding:utf-8

from . import api
from ihome.utils.captcha.captcha import captcha
from ihome import redis_store, constants
from flask import current_app, jsonify, make_response
from ihome.utils.response_code import RET


# GET 127.0.0.1/api/v1.0/image_codes/<image_code_id>
@api.route("/image_codes/<image_code_id>")
def get_image_code(image_code_id):
    """
    获取图片验证码
    : params image_code_id:  图片验证码编号
    :return:  正常:验证码图片  异常:返回json
    """
    # 业务逻辑处理
    # 生成验证码图片
    # 名字,真实文本, 图片数据
    name, text, image_data = captcha.generate_captcha()

    # 将验证码真实值与编号保存到redis中, 设置有效期
    # redis:  字符串   列表  哈希   set
    # "key": xxx
    # 使用哈希维护有效期的时候只能整体设置
    # "image_codes": {"id1":"abc", "":"", "":""} 哈希  hset("image_codes", "id1", "abc")  hget("image_codes", "id1")

    # 单条维护记录,选用字符串
    # "image_code_编号1": "真实值"
    # "image_code_编号2": "真实值"

    # redis_store.set("image_code_%s" % image_code_id, text)
    # redis_store.expire("image_code_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES)
    #                   记录名字                          有效期                              记录值
    try:
        redis_store.setex("image_code_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        # 记录日志
        current_app.logger.error(e)
        # return jsonify(errno=RET.DBERR,  errmsg="save image code id failed")
        return jsonify(errno=RET.DBERR,  errmsg="保存图片验证码失败")

    # 返回图片
    resp = make_response(image_data)
    resp.headers["Content-Type"] = "image/jpg"
    return resp


前端处理验证码

在这里插入图片描述

接口文档

  1. 分析需求
  2. 编写代码
  3. 编写单元测试
  4. 自测
  5. 编写接口文档
  6. 提测代码
接口文档

1. 接口名字
2. 描述(描述清楚接口的功能)
3. url
4. 请求方式
5. 传入参数
6. 返回值


接口:获取图片验证码

描述:前端访问,可以获取到验证码图片

url: /api/v1.0/image_codes/<image_code_id>

请求方式: GET

传入参数:
    格式:路径参数 (参数是查询字符串、请求体的表单、json、xml)

    名字             类型       是否必须      说明
   image_code_id    字符串       是         验证码图片的编号

返回值:
    格式: 正常:图片, 异常:json

    名字             类型       是否必传      说明
    errno          字符串         否        错误代码
    errmsg         字符串         否        错误内容

    实例:
    '{"errno": "4001", "errmsg": "保存图片验证码失败"}'

发送短信服务

容联云 云通讯
www.yuntongxun.com

lib导入容联云SDK
在这里插入图片描述
封装
sms.py

# coding=utf-8

from CCPRestSDK import REST

# 主帐号
accountSid = '8aaf0708568d4143015697b0f4960888'

# 主帐号Token
accountToken = '42d3191f0e6745d6a9ddc6c795da0bed'

# 应用Id
appId = '8aaf0708568d4143015697b0f56e088f'

# 请求地址,格式如下,不需要写http://
serverIP = 'app.cloopen.com'

# 请求端口
serverPort = '8883'

# REST版本号
softVersion = '2013-12-26'

  # 发送模板短信
  # @param to 手机号码
  # @param datas 内容数据 格式为列表 例如:['12','34'],如不需替换请填 ''
  # @param $tempId 模板Id


class CCP(object):
    """自己封装的发送短信的辅助类"""
    # 用来保存对象的类属性
    instance = None

    def __new__(cls):
        # 判断CCP类有没有已经创建好的对象,如果没有,创建一个对象,并且保存
        # 如果有,则将保存的对象直接返回
        if cls.instance is None:
            obj = super(CCP, cls).__new__(cls)

            # 初始化REST SDK
            obj.rest = REST(serverIP, serverPort, softVersion)
            obj.rest.setAccount(accountSid, accountToken)
            obj.rest.setAppId(appId)

            cls.instance = obj

        return cls.instance

    def send_template_sms(self, to, datas, temp_id):
        """"""
        result = self.rest.sendTemplateSMS(to, datas, temp_id)
        # for k, v in result.iteritems():
        #
        #     if k == 'templateSMS':
        #         for k, s in v.iteritems():
        #             print '%s:%s' % (k, s)
        #     else:
        #         print '%s:%s' % (k, v)
        # smsMessageSid:ff75e0f84f05445ba08efdd0787ad7d0
        # dateCreated:20171125124726
        # statusCode:000000
        status_code = result.get("statusCode")
        if status_code == "000000":
            # 表示发送短信成功
            return 0
        else:
            # 发送失败
            return -1


if __name__ == '__main__':
    ccp = CCP()
    ret = ccp.send_template_sms("18516952650", ["1234", "5"], 1)
    print(ret)

    
   

verify_code.py

from . import api
from ihome.utils.captcha.captcha import captcha
from ihome import redis_store, constants, db
from flask import current_app, jsonify, make_response, request
from ihome.utils.response_code import RET
from ihome.models import User
from ihome.libs.yuntongxun.sms import CCP
import random

# GET /api/v1.0/sms_codes/<mobile>?image_code=xxxx&image_code_id=xxxx
@api.route("/sms_codes/<re(r'1[34578]\d{9}'):mobile>")
def get_sms_code(mobile):
    """获取短信验证码"""
    # 获取参数
    image_code = request.args.get("image_code")
    image_code_id = request.args.get("image_code_id")

    # 校验参数
    if not all([image_code_id, image_code]):
        # 表示参数不完整
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 业务逻辑处理
    # 从redis中取出真实的图片验证码
    try:
        real_image_code = redis_store.get("image_code_%s" % image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="redis数据库异常")

    # 判断图片验证码是否过期
    if real_image_code is None:
        # 表示图片验证码没有或者过期
        return jsonify(errno=RET.NODATA, errmsg="图片验证码失效")

    # 删除redis中的图片验证码,防止用户使用同一个图片验证码验证多次
    try:
        redis_store.delete("image_code_%s" % image_code_id)
    except Exception as e:
        current_app.logger.error(e)

    # 与用户填写的值进行对比
    if real_image_code.lower() != image_code.lower():
        # 表示用户填写错误
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码错误")

    # 判断对于这个手机号的操作,在60秒内有没有之前的记录,如果有,则认为用户操作频繁,不接受处理
    try:
        send_flag = redis_store.get("send_sms_code_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if send_flag is not None:
            # 表示在60秒内之前有过发送的记录
            return jsonify(errno=RET.REQERR, errmsg="请求过于频繁,请60秒后重试")

    # 判断手机号是否存在
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
    else:
        if user is not None:
            # 表示手机号已存在
            return jsonify(errno=RET.DATAEXIST, errmsg="手机号已存在")

    # 如果手机号不存在,则生成短信验证码
    sms_code = "%06d" % random.randint(0, 999999)

    # 保存真实的短信验证码
    try:
        redis_store.setex("sms_code_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
        # 保存发送给这个手机号的记录,防止用户在60s内再次出发发送短信的操作
        redis_store.setex("send_sms_code_%s" % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码异常")

    # 发送短信
    try:
        ccp = CCP()
        result = ccp.send_template_sms(mobile, [sms_code, int(constants.SMS_CODE_REDIS_EXPIRES/60)], 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="发送异常")

    # 返回值
    if result == 0:
        # 发送成功
        return jsonify(errno=RET.OK, errmsg="发送成功")
    else:
        return jsonify(errno=RET.THIRDERR, errmsg="发送失败")


测试
在这里插入图片描述
前端发送短信验证码

function sendSMSCode() {
    
    
    // 点击发送短信验证码后被执行的函数
    $(".phonecode-a").removeAttr("onclick");
    var mobile = $("#mobile").val();
    if (!mobile) {
    
    
        $("#mobile-err span").html("请填写正确的手机号!");
        $("#mobile-err").show();
        $(".phonecode-a").attr("onclick", "sendSMSCode();");
        return;
    } 
    var imageCode = $("#imagecode").val();
    if (!imageCode) {
    
    
        $("#image-code-err span").html("请填写验证码!");
        $("#image-code-err").show();
        $(".phonecode-a").attr("onclick", "sendSMSCode();");
        return;
    }

    // 构造向后端请求的参数
    var req_data = {
    
    
        image_code: imageCode, // 图片验证码的值
        image_code_id: imageCodeId // 图片验证码的编号,(全局变量)
    };

    // 向后端发送请求
    $.get("/api/v1.0/sms_codes/"+ mobile, req_data, function (resp) {
    
    
        // resp是后端返回的响应值,因为后端返回的是json字符串,
        // 所以ajax帮助我们把这个json字符串转换为js对象,resp就是转换后对象
        if (resp.errno == "0") {
    
    
            var num = 60;
            // 表示发送成功
            var timer = setInterval(function () {
    
    
                if (num >= 1) {
    
    
                    // 修改倒计时文本
                    $(".phonecode-a").html(num + "秒");
                    num -= 1;
                } else {
    
    
                    $(".phonecode-a").html("获取验证码");
                    $(".phonecode-a").attr("onclick", "sendSMSCode();");
                    clearInterval(timer);
                }
            }, 1000, 60)
        } else {
    
    
            alert(resp.errmsg);
            $(".phonecode-a").attr("onclick", "sendSMSCode();");
        }
    });
}

注册、登录、登出逻辑

所有常量参数保存在constants.py

# coding:utf-8


# 图片验证码的redis有效期, 单位:秒
IMAGE_CODE_REDIS_EXPIRES = 180

# 短信验证码的redis有效期, 单位:秒
SMS_CODE_REDIS_EXPIRES = 300

# 发送短信验证码的间隔, 单位:秒
SEND_SMS_CODE_INTERVAL = 60

# 登录错误尝试次数
LOGIN_ERROR_MAX_TIMES = 5

# 登录错误限制的时间, 单位:秒
LOGIN_ERROR_FORBID_TIME = 600

# 七牛的域名
QINIU_URL_DOMAIN = "http://o91qujnqh.bkt.clouddn.com/"

# 城区信息的缓存时间, 单位:秒
AREA_INFO_REDIS_CACHE_EXPIRES = 7200

passport.py

# coding:utf-8

from . import api
from flask import request, jsonify, current_app, session
from ihome.utils.response_code import RET
from ihome import redis_store, db, constants
from ihome.models import User
from sqlalchemy.exc import IntegrityError
import re


@api.route("/users", methods=["POST"])
def register():
    """注册
    请求的参数: 手机号、短信验证码、密码、确认密码
    参数格式:json
    """
    # 获取请求的json数据,返回字典
    req_dict = request.get_json()

    mobile = req_dict.get("mobile")
    sms_code = req_dict.get("sms_code")
    password = req_dict.get("password")
    password2 = req_dict.get("password2")

    # 校验参数
    if not all([mobile, sms_code, password, password2]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 判断手机号格式
    if not re.match(r"1[34578]\d{9}", mobile):
        # 表示格式不对
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")

    if password != password2:
        return jsonify(errno=RET.PARAMERR, errmsg="两次密码不一致")

    # 从redis中取出短信验证码
    try:
        real_sms_code = redis_store.get("sms_code_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="读取真实短信验证码异常")

    # 判断短信验证码是否过期
    if real_sms_code is None:
        return jsonify(errno=RET.NODATA, errmsg="短信验证码失效")

    # 删除redis中的短信验证码,防止重复使用校验
    try:
        redis_store.delete("sms_code_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)

    # 判断用户填写短信验证码的正确性
    if real_sms_code != sms_code:
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码错误")

    # 判断用户的手机号是否注册过
    # try:
    #     user = User.query.filter_by(mobile=mobile).first()
    # except Exception as e:
    #     current_app.logger.error(e)
    #     return jsonify(errno=RET.DBERR, errmsg="数据库异常")
    # else:
    #     if user is not None:
    #         # 表示手机号已存在
    #         return jsonify(errno=RET.DATAEXIST, errmsg="手机号已存在")

    # 盐值   salt

    #  注册
    #  用户1   password="123456" + "abc"   sha1   abc$hxosifodfdoshfosdhfso
    #  用户2   password="123456" + "def"   sha1   def$dfhsoicoshdoshfosidfs
    #
    # 用户登录  password ="123456"  "abc"  sha256      sha1   hxosufodsofdihsofho

    # 保存用户的注册数据到数据库中
    user = User(name=mobile, mobile=mobile)
    # user.generate_password_hash(password)

    user.password = password  # 设置属性

    try:
        db.session.add(user)
        db.session.commit()
    except IntegrityError as e:
        # 数据库操作错误后的回滚
        db.session.rollback()
        # 表示手机号出现了重复值,即手机号已注册过
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAEXIST, errmsg="手机号已存在")
    except Exception as e:
        db.session.rollback()
        # 表示手机号出现了重复值,即手机号已注册过
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据库异常")

    # 保存登录状态到session中
    session["name"] = mobile
    session["mobile"] = mobile
    session["user_id"] = user.id

    # 返回结果
    return jsonify(errno=RET.OK, errmsg="注册成功")


@api.route("/sessions", methods=["POST"])
def login():
    """用户登录
    参数: 手机号、密码, json
    """
    # 获取参数
    req_dict = request.get_json()
    mobile = req_dict.get("mobile")
    password = req_dict.get("password")

    # 校验参数
    # 参数完整的校验
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # 手机号的格式
    if not re.match(r"1[34578]\d{9}", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")

    # 判断错误次数是否超过限制,如果超过限制,则返回
    # redis记录: "access_nums_请求的ip": "次数"
    user_ip = request.remote_addr  # 用户的ip地址
    try:
        access_nums = redis_store.get("access_num_%s" % user_ip)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if access_nums is not None and int(access_nums) >= constants.LOGIN_ERROR_MAX_TIMES:
            return jsonify(errno=RET.REQERR, errmsg="错误次数过多,请稍后重试")

    # 从数据库中根据手机号查询用户的数据对象
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败")

    # 用数据库的密码与用户填写的密码进行对比验证
    if user is None or not user.check_password(password):
        # 如果验证失败,记录错误次数,返回信息
        try:
            # redis的incr可以对字符串类型的数字数据进行加一操作,如果数据一开始不存在,则会初始化为1
            redis_store.incr("access_num_%s" % user_ip)
            redis_store.expire("access_num_%s" % user_ip, constants.LOGIN_ERROR_FORBID_TIME)
        except Exception as e:
            current_app.logger.error(e)

        return jsonify(errno=RET.DATAERR, errmsg="用户名或密码错误")

    # 如果验证相同成功,保存登录状态, 在session中
    session["name"] = user.name
    session["mobile"] = user.mobile
    session["user_id"] = user.id

    return jsonify(errno=RET.OK, errmsg="登录成功")


@api.route("/session", methods=["GET"])
def check_login():
    """检查登陆状态"""
    # 尝试从session中获取用户的名字
    name = session.get("name")
    # 如果session中数据name名字存在,则表示用户已登录,否则未登录
    if name is not None:
        return jsonify(errno=RET.OK, errmsg="true", data={
    
    "name": name})
    else:
        return jsonify(errno=RET.SESSIONERR, errmsg="false")


@api.route("/session", methods=["DELETE"])
def logout():
    """登出"""
    # 清除session数据
    session.clear()
    return jsonify(errno=RET.OK, errmsg="OK")

model.py 处理用户密码加密部分


from datetime import datetime
from . import db
from werkzeug.security import generate_password_hash, check_password_hash


class User(BaseModel, db.Model):
    """用户"""

    __tablename__ = "ih_user_profile"

    id = db.Column(db.Integer, primary_key=True)  # 用户编号
    name = db.Column(db.String(32), unique=True, nullable=False)  # 用户暱称
    password_hash = db.Column(db.String(128), nullable=False)  # 加密的密码
    mobile = db.Column(db.String(11), unique=True, nullable=False)  # 手机号
    real_name = db.Column(db.String(32))  # 真实姓名
    id_card = db.Column(db.String(20))  # 身份证号
    avatar_url = db.Column(db.String(128))  # 用户头像路径
    houses = db.relationship("House", backref="user")  # 用户发布的房屋
    orders = db.relationship("Order", backref="user")  # 用户下的订单

    # 加上property装饰器后,会把函数变为属性,属性名即为函数名
    @property
    def password(self):
        """读取属性的函数行为"""
        # print(user.password)  # 读取属性时被调用
        # 函数的返回值会作为属性值
        # return "xxxx"
        raise AttributeError("这个属性只能设置,不能读取")

    # 使用这个装饰器, 对应设置属性操作
    @password.setter
    def password(self, value):
        """
        设置属性  user.passord = "xxxxx"
        :param value: 设置属性时的数据 value就是"xxxxx", 原始的明文密码
        :return:
        """
        self.password_hash = generate_password_hash(value)

    # def generate_password_hash(self, origin_password):
    #     """对密码进行加密"""
    #     self.password_hash = generate_password_hash(origin_password)

    def check_password(self, passwd):
        """
        检验密码的正确性
        :param passwd:  用户登录时填写的原始密码
        :return: 如果正确,返回True, 否则返回False
        """
        return check_password_hash(self.password_hash, passwd)

前端部分
register.js

// js读取cookie的方法
function getCookie(name) {
    
    
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

  // 为表单的提交补充自定义的函数行为 (提交事件e)
    $(".form-register").submit(function(e){
    
    
        // 阻止浏览器对于表单的默认自动提交行为
        e.preventDefault();

        var mobile = $("#mobile").val();
        var phoneCode = $("#phonecode").val();
        var passwd = $("#password").val();
        var passwd2 = $("#password2").val();
        if (!mobile) {
    
    
            $("#mobile-err span").html("请填写正确的手机号!");
            $("#mobile-err").show();
            return;
        } 
        if (!phoneCode) {
    
    
            $("#phone-code-err span").html("请填写短信验证码!");
            $("#phone-code-err").show();
            return;
        }
        if (!passwd) {
    
    
            $("#password-err span").html("请填写密码!");
            $("#password-err").show();
            return;
        }
        if (passwd != passwd2) {
    
    
            $("#password2-err span").html("两次密码不一致!");
            $("#password2-err").show();
            return;
        }

        // 调用ajax向后端发送注册请求
        var req_data = {
    
    
            mobile: mobile,
            sms_code: phoneCode,
            password: passwd,
            password2: passwd2,
        };
        var req_json = JSON.stringify(req_data);
        $.ajax({
    
    
            url: "/api/v1.0/users",
            type: "post",
            data: req_json,
            contentType: "application/json",
            dataType: "json",
            headers: {
    
    
                "X-CSRFToken": getCookie("csrf_token")
            }, // 请求头,将csrf_token值放到请求中,方便后端csrf进行验证
            success: function (resp) {
    
    
                if (resp.errno == "0") {
    
    
                    // 注册成功,跳转到主页
                    location.href = "/index.html";
                } else {
    
    
                    alert(resp.errmsg);
                }
            }
        })

    });
})

login.js

function getCookie(name) {
    
    
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

$(document).ready(function() {
    
    
    $("#mobile").focus(function(){
    
    
        $("#mobile-err").hide();
    });
    $("#password").focus(function(){
    
    
        $("#password-err").hide();
    });
    $(".form-login").submit(function(e){
    
    
        e.preventDefault();
        mobile = $("#mobile").val();
        passwd = $("#password").val();
        if (!mobile) {
    
    
            $("#mobile-err span").html("请填写正确的手机号!");
            $("#mobile-err").show();
            return;
        } 
        if (!passwd) {
    
    
            $("#password-err span").html("请填写密码!");
            $("#password-err").show();
            return;
        }
        // 将表单的数据存放到对象data中
        var data = {
    
    
            mobile: mobile,
            password: passwd
        };
        // 将data转为json字符串
        var jsonData = JSON.stringify(data);
        $.ajax({
    
    
            url:"/api/v1.0/sessions",
            type:"post",
            data: jsonData,
            contentType: "application/json",
            dataType: "json",
            headers:{
    
    
                "X-CSRFToken":getCookie("csrf_token")
            },
            success: function (data) {
    
    
                if (data.errno == "0") {
    
    
                    // 登录成功,跳转到主页
                    location.href = "/";
                }
                else {
    
    
                    // 其他错误信息,在页面中展示
                    $("#password-err span").html(data.errmsg);
                    $("#password-err").show();
                }
            }
        });
    });
})

检查登录
index.js

$(document).ready(function(){
    
    
    // 检查用户的登录状态
    $.get("/api/v1.0/session", function(resp) {
    
    
        if ("0" == resp.errno) {
    
    
            $(".top-bar>.user-info>.user-name").html(resp.data.name);
            $(".top-bar>.user-info").show();
        } else {
    
    
            $(".top-bar>.register-login").show();
        }
    }, "json");

登出

function getCookie(name) {
    
    
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

// 点击推出按钮时执行的函数
function logout() {
    
    
    $.ajax({
    
    
        url: "/api/v1.0/session",
        type: "delete",
        headers: {
    
    
            "X-CSRFToken": getCookie("csrf_token")
        },
        dataType: "json",
        success: function (resp) {
    
    
            if ("0" == resp.errno) {
    
    
                location.href = "/index.html";
            }
        }
    });
}

$(document).ready(function(){
    
    
})

猜你喜欢

转载自blog.csdn.net/qq_27251475/article/details/121250656
今日推荐