go+iris+jwt+mysql+xorm+viper, a practical simple chat room for the iris project, with login, registration, private chat, and group chat.
First, take a look at the front-end introduction below this document to know how to operate it (because of limited energy, the UI is not particularly user-friendly).
Access the demo address. If the status of the small icon above is normal, you can access it. It may enter a dormant state and need to wait for a few seconds.
The project currently only writes mysql-related adaptations, but using xorm, it is not difficult to support other databases. It can be done, but it is not necessary. Hahaha, if you are too lazy to run it yourself, just take a look at the demo URL. If you are interested No one has mysql now.
I originally wanted to support sqlite, so that there is no need to configure database parameters, but considering that compiling sqlite under windows requires configuring the gcc environment, which is more troublesome, but there is no quick startup effect, and it is simply not complete, so other databases are not supported for the time being (someday Add more when you have time).
So the project only needs to configure the following parameters
git clone https://github.com/JabinGP/demo-chatroom.git
cd demo-chatroom
// 复制config.toml.example 为 config.toml 并填写数据库信息,或者可选修改端口号
go run main.go
The default is port 8888. After startup, access http://localhost:8888
I used react, but I didn’t use the UI framework. Many small details are not performed well. The interface is made according to the size of the mobile phone. If you open the computer, you can open f12. It looks more comfortable. Let’s make do with it. The focus is on the latter. end.
The chat box sets the window to automatically scroll to the bottom, but the API is provided by React and is found to be incompatible on many browsers. This problem can be solved by using the Chrome browser.
After registration, manually return and select login. The red name in the message box is for public speech, and the gray name is for private chat. You can specify the name of the recipient in the red box. If not specified, the default is public speech. After specifying, only the corresponding users can see the information.
Your user name is displayed in the blue box. Click to log out directly.
The api format is based on restful design, and the login function is completed using jwt. Many interfaces require login status, and JWT needs to be carried when requesting. For details, please see the jwt practice of golang iris. In addition, it is convenient for testing. The JWT issuance validity time is only set for 20 minutes. It needs to be expired. Log in again.
The api request format is the same as the general interface. Get uses Params, and Post, Put, Delete, etc. use Json in Body to pass parameters.
The return format is quite controversial. I have been researching it for a while. Some people advocate using the full 200 http status code and adding code to the return content to identify errors, like this:
// 注册错误时
// http status 200
{
"code" : 40001 ,
"msg" : "注册用户名非法"
}
// 注册成功时
// http status 200
{
"code" : 200 ,
"msg" : "成功" ,
"data" : {
"username" : " JabinGP " ,
"id" : 3
}
}
Others advocate using full http status codes to indicate errors:
// 注册错误时
// http status 400
{
"msg" : "注册用户名非法"
}
// 注册成功时
// http status 200
{
"username" : " JabinGP " ,
"id" : 3
}
In fact, both of the above approaches have their own pros and cons:
Based on the above situation, I will combine the two:
When successful, return http status code 200
// 注册成功时
// http status 200
{
"username" : " JabinGP " ,
"id" : 3
}
When failure occurs, select several commonly used status codes to express the error, 400 (request error), 500 (server internal error), 404 (not found), 401 (authentication failure), roughly classify the error, and then return Customize a code, msg, and detail in the data to represent the detailed error cause:
// 注册失败
// http status 400
{
"code" : 6 ,
"msg" : "数据检验失败" ,
"detail" : "用户名已存在"
}
// 登录失效
// http status 401
{
"code" : 8 ,
"msg" : "未认证登录" ,
"detail" : " Token is expired "
}
After this combination, the successful callback is successful, and there is no need to write res.data.data. The error callback only handles errors, which can be judged by the http status code, and can be further coded, msg, and detailed. to handle errors.
The api list is as follows. Replace localhost with mike.jabingp.cn or you can directly request to the demo backend:
Function | Request method | address |
---|---|---|
Get login token | POST | http://localhost:8888/v1/login |
Find users | GET | http://localhost:8888/v1/user |
register | POST | http://localhost:8888/v1/user |
Users modify information themselves | PUT | http://localhost:8888/v1/user |
User sends message | POST | http://localhost:8888/v1/message |
User gets information | GET | http://localhost:8888/v1/message |
User obtains token information | GET | http://localhost:8888/v1/token/info |
Detailed request parameters can be viewed in the postman-api documentation of demo-chatroom.
Or view the source code. The request parameters can be viewed in model/reqo
and the response parameters can be viewed in model/reso
AJAX is not the best choice for the chat function. WebSocket is better, but I was asked to use AJAX so I did not choose the latter.
The front end of the project is relatively simple because it is only used as a demo.
My English is not very good, and the code comments are in English just because I am too lazy to switch the input method.
This is the first time I use go to develop a web project, and it is also the first time I use react to write the front end. Since the front end does not pay much attention to the project structure (xjbx), I will not put the source code. I compile the project and put it in the assets folder for readability. It's very poor, but it can be started together with the backend. There is no need to start the frontend separately, which is more convenient to see the effect. If I still have time, I will consider writing a minimalist version in native for everyone's reference.
It is the first time I use ORM to operate a database. It feels very difficult to use. I would rather write SQL by hand. There are many desired effects and I can't find a solution after reading the documents for a long time. I will consider using SQLX to reconstruct it later.
Recently, I have become more interested in Go, and I received a task to write a simple chat room. I found that there are currently few project practices in iris, only some HelloWorld-level examples, so I decided to use Go to do it, and then open sourced it for mutual reference. Of course How to design the project structure is completely based on my limited development experience. Please give your valuable opinions on any unreasonable aspects.
This project has the following requirements
The login function is implemented using JWT
this time. The advantages and disadvantages of JWT
and Session
will not be discussed in detail.
AJAX-based is a must for all front-end and back-end separation projects, so this function will not be discussed too much. The focus here is no refresh. What is the difficulty?
The user's operation logic is to send data in the chat room, and then the data is sent out. The chat interface should display the data sent by itself and update the data sent by others in real time.
The front-end and back-end communicate through AJAX. The front-end sending data and the back-end sending data can be expressed as
What's the problem here? The problem is that the front end can only initiate requests actively, and the back end can only accept requests. This means that the latest messages can never be actively sent from the backend to the frontend in real time. The latest messages can only be stored in the backend first, and then wait for the frontend to initiate a request before the backend can return data.
Since the backend does not have the ability to actively push messages to the frontend, the solution for users to obtain the latest data is for the frontend to set a timer每隔一段比较短的时间就请求一次后台接口(轮询)
, so that the data can be continuously updated. .
The front end has decided to use AJAX to regularly poll the background interface to obtain the latest data. For the sake of real-time data, the polling interval will be小于1s
, this will bring another problem. Under such frequent requests, the backend must not transmit all the data every time. One is the network transmission efficiency and traffic cost caused by the data size; the other is the data size. The resulting efficiency problem in the front-end's judgment of new data means that the back-end must return data that the front-end has not received every time. The problem is - how does the back-end know what information the front-end has received?
This requires the use of自增主键
of the message. The front-end only needs to carry最后的消息的主键
received by the front-end every time it makes a request. Since the primary key is non-repeating and auto-increasing, we can easily find out the ratio. The data with a large primary key is the data that the front end has not yet received.
language
frame
data storage
technology
Due to the use of the Xorm database ORM framework, the following tables are automatically generated and come with the
xxxxxx_at
field
Based on the above requirements, two tables, users
and messages
were designed.
Key fields
Database table structure
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
ID | bigint(20) | NO | PRI | NULL | auto_increment |
username | varchar(255) | YES | NULL | ||
passwd | varchar(255) | YES | NULL | ||
gender | bigint(20) | YES | NULL | ||
age | bigint(20) | YES | NULL | ||
interest | varchar(255) | YES | NULL | ||
created_at | datetime | YES | NULL | ||
updated_at | datetime | YES | NULL | ||
deleted_at | datetime | YES | NULL |
Key fields
Database table structure
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
ID | bigint(20) | NO | PRI | NULL | auto_increment |
sender_id | bigint(20) | YES | NULL | ||
receiver_id | bigint(20) | YES | NULL | ||
content | varchar(255) | YES | NULL | ||
send_time | bigint(20) | YES | NULL | ||
created_at | datetime | YES | NULL | ||
updated_at | datetime | YES | NULL | ||
deleted_at | datetime | YES | NULL |
The following structure is based on personal experience. If there is anything inappropriate, please give us your valuable feedback.
pojo
It is easy to understand, it is the entity corresponding to the database, but it does not require one-to-one correspondence with the database fields.
reqo (request object), reso (response object)
When making requests through different interfaces, the parameters that can be carried and the response data are also different, so a corresponding request entity and response entity are designed for each interface.
The following is my personal understanding
Controller
The main responsibility is to accept the request parameters of the request, convert them into reqo, perform simple request parameter verification (my personal definition is verification that has nothing to do with the database, such as non-null, non-zero), call the function of the Service layer to obtain the pojo result, and The pojo result conversion is encapsulated and returned in reso.
Service
The main responsibility is to further encapsulate the interface of the Dao layer and provide a common interface for the Controller to call. The returned data can be pojo. Data verification needs to be performed in the Service, such as (adding new users, verifying whether the user name is repeated).
Dao
Basically, a method here directly corresponds to a SQL statement without any verification. The received data is considered reliable (it has been verified by the parameters of the Controller and Service layers), and the returned data can be POJO.