从零开始的 Rust 学习笔记(17) —— 做一个提问箱 Boxy ?

于是我自己的提问箱就是 https://ask.[data deleted]

源代码在这里 #/boxy ?(给大家表演如何用一个项目气死你写前端和 Rust 的朋友(x

回答是人工的,并不是人工智能?(不过感觉加上人工智能的话似乎也蛮好玩诶!

然后因为是模仿 peing 或者 sarahah 那样的匿名的提问箱,所以这个截图里就差不多是后台了~既然说是匿名的话,那就是真匿名~除了记录了提问时间,IP 只用来做了速度限制,没扔数据库。Question 在 MongoDB 里的模型则是

{ 
    "_id" : ObjectId("5e0a0ca0001cd1ea00876f2e"), 
    "question" : "What's this?", 
    "question_time" : NumberLong(1577716896), 
    "answer" : "It's a question box!", 
    "answer_time" : NumberLong(1577716906), 
    "id" : NumberLong(0) 
}

箱子的 owner 登录之后,直接点击答案的部分就进入编辑 / 回答模式,然后也可以删除提问什么的~

代码里倒是 Web 和纯 API 方式都实现了~然后回答之后自动生成 Twitter 卡片什么的还没有做_(:3」∠)_

再从技术层面上来说的话,这个项目用 Rust 上的 Hyper 作为 HTTP 服务器,然后数据库使用了 MongoDB,考虑到是作为单一用户的提问箱,因此用户名和密码是需要写在 boxy.json 里的~当然放进数据库里的是密码加盐后再 HMAC + SHA512 过的

stored_password = HMACSHA512(password + password_salt, password_salt)
{
    "_id" : ObjectId("5e09a250004b7da70096e3dc"),
    "user" : "ryza",
    "password" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Web 界面和后端做了分离,我自己写了一个简单的~因此就直接手写了,没有用到现在前端流行的 Vue 或者 Angular 之类的框架。访问后端的 API 也是直接用的 jQuery 的 AJAX 模块。

登录之后会返回一个 token,包含了有效期,默认是 365 天,保存在了浏览器的 localStorage 里。登出的时候会自动清掉,但不是关闭页面 / 浏览器自动清除,所以在公用电脑上要记得登出~

那么下面是目前有的一些 API~ 以 http://localhost:5534 为例子!

1. 登录

API: http://localhost:5534/api/v1/token
Method: POST
{
    "type": "new",
    "username": "ryza",
    "password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt),
    "time": 1577716906,
}

在密码正确,且送达时 API Backend 的时间与计算 HMACSHA512 的时间相差 3 秒以内时就没问题。通过验证时可能的返回值如下~正确 status 肯定是 0,发生错误时 status 的值为相应的 HTTP 状态码,并且会有 reason 告知原因(后面所有的 API 均如此

{
    "status": 0,
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "expired_time": 1597716906,
}

在发生错误时则可能是

{
    "status": 403,
    "reason": "Access denied"
}

2. 提问题

API: http://localhost:5534/api/v1/question
Method: POST
{
    "question": "How to send questions?"
}

因为是匿名提问箱,所以提问不需要别的东西啦~只要有 question 就行~成功时会将对应的 Question ID 返回回来~

{
    "status": 0,
    "id": 233
}

3. 回答问题

API: http://localhost:5534/api/v1/answer/{:id}
Method: POST
{
    "answer": "Type anything you want to ask in the textarea above*3^)",
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}

需要注意这里的 API 的 URL,最后的 {:id} 替换成要回答的问题的 Question ID,比如要回答 ID 为 233 的问题的话,就是

http://localhost:5534/api/v1/answer/233

成功时只会返回 status 并且值为 0.

4. 删除问题

API: http://localhost:5534/api/v1/question/{:id}
Method: DELETE
{
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~

5. 获取某个问题的详情

API: http://localhost:5534/api/v1/question/{:id}
Method: GET

如果有 {:id} 对应的问题的话,将返回

{
    "status": 0,
    "question": {
        "id": 233,
        "question_time": 1577718525,
        "question": "How to send questions?",
        "answer": "Type anything you want to ask in the textarea above*3^)",
        "answer_time": 1577718525
    }
}

6. 获取从某一 ID 之前的最多 30 个问题

API: http://localhost:5534/api/v1/question/from/{:id}
Method: GET

假如有 [1, 2, ..., 45] 个 问题的话,要拿到 33 之前(不包含 33)的 30 个问题则是

API: http://localhost:5534/api/v1/question/from/33

(如果中间有的问题被删掉了,则数满 30 个为止,或者没有更多在 33 之前的问题了)

{
    "status": 0,
    "questions": [
        {
            "id": 3,
            "question_time": 1577718523,
            "question": "How to build my own boxy?",
            "answer": "It's too long to answer here >_< But you can visit tis GitHub for usage ➜ #/boxy",
            "answer_time": 1577718556
        },
        {
            "id": 2,
            "question_time": 1577718522,
            "question": "How to send questions?",
            "answer": "Type anything you want to ask in the textarea above*3^)",
            "answer_time": 1577718532
        },
        {
            "id": 1,
            "question_time": 1577718448,
            "question": "What's this?",
            "answer": "It's a question box!",
            "answer_time": 1577718458
        }
    ]
}

7. 获取新的 Token

其实这个就是登录啦~

API: http://localhost:5534/api/v1/login
Method: POST
{
    "type": "new",
    "username": "ryza",
    "password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt),
    "time": 1577716906,
}

可能的返回值如下~

{
    "status": 0,
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "expired_time": 1597716906,
}

8. 注销当前 Token

API: http://localhost:5534/api/v1/login
Method: POST
{
    "type": "revoke",
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}

成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~

9. 注销所有 Token

API: http://localhost:5534/api/v1/login
Method: POST
{
    "type": "revoke_all",
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}

成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~

Leave a Reply

Your email address will not be published. Required fields are marked *

two × three =