Учебное пособие для начинающих веб-пользователей
Всем привет, меня зовут Се Вэй, я программист.
web
Приложения — очень популярная область применения самых разных языков программирования.
Такweb
Какие знания включает в себя фоновое развитие?
- Проектирование модели: проектирование модели реляционной базы данных
- SQL, ОРМ
- Спокойный дизайн API
дизайн модели
Разработка веб-бэкенда, как правило, ориентирована на бизнес, то есть разработка представляет собой прикладную сущность: например, она ориентирована на область электронной коммерции, такую как поле данных, например, социальное поле и т. д.
В разных областях используются разные абстрактные модели.Электронная коммерция в основном нацелена на такие модели, как товары, магазины, заказы и логистика, а социальные сети нацелены в основном на такие модели, как люди, сообщения, группы и посты.
Несмотря на то, что на рынке существует множество баз данных и разные сценарии приложений выбирают разные базы данных, реляционные базы данных по-прежнему являются основным выбором для малых и средних предприятий, а реляционные базы данных очень удобны для организации данных.
Его можно быстро применить к бизнес-сценариям.Только когда данные достигают определенной точки, возникает какое-то узкое место, например, слишком много данных и медленный запрос.В это время подбаза данных, подтаблица, режим ведущий-ведомый, и т. д. будут выбраны.
Разработка модели базы данных остается важной темой. Хорошая модель данных очень полезна для непрерывной итерации и расширения последующих требований.
Как спроектировать хорошую модель базы данных?
- Следуйте некоторым парадигмам: например, знаменитым трем парадигмам проектирования баз данных.
- Разрешить небольшую избыточность
В деталях это не что иное, как: 1. Структура таблицы базы данных 2. Дизайн полей БД, шрифтовой дизайн 3. Дизайн взаимосвязей таблиц данных: 1-к-1, 1-ко-многим, многие-ко-многим
1. дизайн таблицы базы данных
Имя таблицыЭто не о чем говорить, это можно назвать по смыслу знания, но я все же рекомендую использоватьdatabase+实体
форма.
Например:beeQuick_products
Представление: база данных:beeQuick
,поверхность:products
Реальная сцена разработана: свежая платформа: таблица товаров в любви
2. дизайн поля базы данных
Полевой дизайн, шрифтовой дизайн
- Количество полей: если полей слишком много, таблицу нужно разделить на более позднем этапе, если полей слишком мало, будут задействованы многотабличные операции, поэтому очень важно настроить масштаб. показатель: менее 12 полей.
- Как оформить поля? : По абстрактным объектам, таким как система образования: информация об учениках, информация об учителях, роли и т. д., легко узнать, какие поля и типы полей требуются в таблице.
- Если вы знаете реальную сцену, постарайтесь ограничить пространство, занимаемое полем, например: номер телефона 11 цифр, например: длина пароля не более 12 цифр
дизайн внешнего ключа
- Внешний ключ изначально используется для обеспечения согласованности данных, но он не используется в реальных сценариях использования, а зависит от бизнес-оценки.Например, первичный ключ записи рассматривается как поле таблицы.
Отношения 1-к-1, 1-ко-многим, многие-ко-многим
- 1 к 1: поле одной таблицы является первичным ключом другой таблицы.
type Order struct{
base
AccountId int64
}
- 1-ко-многим: поле одной таблицы представляет собой набор первичных ключей другой таблицы.
type Order struct {
base `xorm:"extends"`
ProductIds []int `xorm:"blob"`
Status int
AccountId int64
Account Account `xorm:"-"`
Total float64
}
- «Многие ко многим»: используйте третью таблицу для поддержания отношений «многие ко многим».
type Shop2Tags struct {
TagsId int64 `xorm:"index"`
ShopId int64 `xorm:"index"`
}
ORM
Идея ORM заключается в отображении объектов в таблицы базы данных.
В конкретном использовании:
1. В соответствии с сопоставлением между языком программирования ORM и типом данных базы данных разумно определите поля и типы полей. 2. определить имя таблицы 3. Создание таблицы базы данных, удаление и т. д.
Более популярные библиотеки ORM в Go: GORM и XORM, правила определения таблиц базы данных, в основном начинаются с полей структуры и тегов.
Поле соответствует имени столбца в таблице базы данных, а тип, тип ограничения, индекс и т. д. указываются в теге. Если тег не определен, используется форма по умолчанию. Для конкретного типа языка программирования и соответствующей связи в базе данных вам необходимо проверить конкретный документ ORM.
// XORM
type Account struct {
base `xorm:"extends"`
Phone string `xorm:"varchar(11) notnull unique 'phone'" json:"phone"`
Password string `xorm:"varchar(128)" json:"password"`
Token string `xorm:"varchar(128) 'token'" json:"token"`
Avatar string `xorm:"varchar(128) 'avatar'" json:"avatar"`
Gender string `xorm:"varchar(1) 'gender'" json:"gender"`
Birthday time.Time `json:"birthday"`
Points int `json:"points"`
VipMemberID uint `xorm:"index"`
VipMember VipMember `xorm:"-"`
VipTime time.Time `json:"vip_time"`
}
// GORM
type Account struct {
gorm.Model
LevelID uint
Phone string `gorm:"type:varchar" json:"phone"`
Avatar string `gorm:"type:varchar" json:"avatar"`
Name string `gorm:"type:varchar" json:"name"`
Gender int `gorm:"type:integer" json:"gender"` // 0 男 1 女
Birthday time.Time `gorm:"type:timestamp with time zone" json:"birthday"`
Points sql.NullFloat64
}
Другая конкретная операция: Завершите добавление, удаление, изменение и запрос базы данных.Конкретная идея по-прежнему состоит в том, чтобы управлять объектом структуры и завершать операцию SQL базы данных.
Конечно, в соответствии с дизайном каждой модели я обычно определяю сериализованную структуру, а метод сериализации реальной модели заключается в возврате определенной сериализованной структуры.
Конкретно:
// 定义一个具体的序列化结构体,注意名称的命名,一致性
type AccountSerializer struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Phone string `json:"phone"`
Password string `json:"-"`
Token string `json:"token"`
Avatar string `json:"avatar"`
Gender string `json:"gender"`
Age int `json:"age"`
Points int `json:"points"`
VipMember VipMemberSerializer `json:"vip_member"`
VipTime time.Time `json:"vip_time"`
}
// 具体的模型的序列化方法返回定义的序列化结构体
func (a Account) Serializer() AccountSerializer {
gender := func() string {
if a.Gender == "0" {
return "男"
}
if a.Gender == "1" {
return "女"
}
return a.Gender
}
age := func() int {
if a.Birthday.IsZero() {
return 0
}
nowYear, _, _ := time.Now().Date()
year, _, _ := a.Birthday.Date()
if a.Birthday.After(time.Now()) {
return 0
}
return nowYear - year
}
return AccountSerializer{
ID: a.ID,
CreatedAt: a.CreatedAt.Truncate(time.Minute),
UpdatedAt: a.UpdatedAt.Truncate(time.Minute),
Phone: a.Phone,
Password: a.Password,
Token: a.Token,
Avatar: a.Avatar,
Points: a.Points,
Age: age(),
Gender: gender(),
VipTime: a.VipTime.Truncate(time.Minute),
VipMember: a.VipMember.Serializer(),
}
}
проектирование структуры проекта
├── cmd
├── configs
├── deployments
├── model
│ ├── v1
│ └── v2
├── pkg
│ ├── database.v1
│ ├── error.v1
│ ├── log.v1
│ ├── middleware
│ └── router.v1
├── src
│ ├── account
│ ├── activity
│ ├── brand
│ ├── exchange_coupons
│ ├── make_param
│ ├── make_response
│ ├── order
│ ├── product
│ ├── province
│ ├── rule
│ ├── shop
│ ├── tags
│ ├── unit
│ └── vip_member
└── main.go
└── Makefile
Зачем организовывать структуру проекта? Позвольте задать вам вопрос: в грязном доме быстрее найти предмет, или в чистом и опрятном доме быстрее найти предмет?
Разумная организация проекта способствует расширению проекта и удовлетворению меняющихся потребностей Такой тип модульного мышления часто встречается в программировании, например, разделение всей системы по функциям.
- cmd для командной строки
- конфиги для конфигурационных файлов
- сценарий развертывания развертываний, Dockerfile
- модель для модельного дизайна
- pkg для вспомогательных библиотек
- Основной логический слой src, этот слой, мой общий метод организации: разделить разные папки в соответствии с объектами, разработанными моделью, такими как указанные выше учетные записи, действия, бренды, купоны и т. д., и конкретной логикой обработки, я разделяю ее вот так:
├── assistance.go // 辅助函数,如果重复使用的辅助函数,会提取到 pkg 层,或者 utils 层
├── controller.go // 核心逻辑处理层
├── param.go // 请求参数层:包括参数校验
├── response.go // 响应信息
└── router.go // 路由
- Вход в функцию main.go
- Сборка проекта Makefile
Конечно, вы также можете обратиться к:GitHub.com/go wave-beach…
выбор кадра
- gin
- iris
- echo ...
Просто выберите основной, без проблем. Также можно использовать нативные, но может потребоваться написать много кода, например, проектирование маршрута, проверка параметров: параметры пути, параметры запроса, обработка информации об ответе и т.д.
Разработка API в спокойном стиле
- дизайн маршрутизации
- проверка параметров
- ответная информация
дизайн маршрутизации
Хотя в Интернете есть много руководств по дизайну API в стиле Restful, я все же рекомендую вам ознакомиться с приведенным ниже введением.
Доменное имя (хост)
Рекомендуется использовать выделенное доменное имя API, например:https://api.example.com
Но на самом деле поместите его прямо под хостом:https://example.com/api
Версия
Требования будут продолжать меняться, и интерфейс будет продолжать меняться, поэтому лучше всего привести версию к API: например:https://example.com/api/v1
, что указывает на первую версию.
Некоторые из них содержат информацию о версии в заголовке, что не рекомендуется и не интуитивно понятно.
Способов очень много, но они должны быть унифицированы. С информацией о версии в информации заголовка это всегда будет так. Если оно находится в пределах дорожного пути, оно соответствует пути, и единство очень важно.
метод запроса
- POST: создайте ресурс на сервере, соответствующая операция базы данных: создать
- ИСПРАВЛЕНИЕ: Обновите ресурсы на сервере, соответствующая операция базы данных: обновление
- УДАЛИТЬ: удалить ресурс на сервере, соответствующая операция базы данных: удалить
- GET: получить ресурсы на сервере, соответствующая операция базы данных: выбрать
- Другое: обычно не используется
дизайн маршрутизации
Общая рекомендация:版本 + 实体(名词)
форма:
Например: в вышеуказанной структуре проектаorder
Представляет сущность заказа.
Итак, как устроен маршрут?
POST /api/v1/order
PATCH /api/v1/order/{order_id:int}
DELETE /api/v1/order/{order_id:int}
GET /api/v1/orders
Хотя есть и другие способы, я все же рекомендую необходимость последовательности.
Например, интерфейс активности:
POST /api/v1/activity
PATCH /api/v1/activity/{activity_id:int}
DELETE /api/v1/activity/{activity_id:int}
GET /api/v1/activities
Быть последовательным.
проверка параметров
Проектирование маршрутизации является важным знанием, включающим в себя: Проверка параметров
- например, проверка типа параметра
- Например, проверка длины параметра
- Например, указание проверки опции
Конкретная структура проекта интерфейса каждой сущности в приведенном выше примере проекта выглядит следующим образом:
├── assistance.go
├── controller.go
├── param.go
├── response.go
└── router.go
- Ядром param.go является организация определения параметров в интерфейсе и проверка параметров.
Существует два способа проверки параметров: 1: использовать метод структуры для реализации логики проверки; 2: использовать тег в структуре для определения проверки.
type RegisterParam struct {
Phone string `json:"phone"`
Password string `json:"password"`
}
func (param RegisterParam) suitable() (bool, error) {
if param.Password == "" || len(param.Phone) != 11 {
return false, fmt.Errorf("password should not be nil or the length of phone is not 11")
}
if unicode.IsNumber(rune(param.Password[0])) {
return false, fmt.Errorf("password should start with number")
}
return true, nil
}
Таким образом, настройте структуру параметров и метод структуры для проверки параметров.
Недостатком является то, что нужно написать много кода и рассмотреть множество сценариев.
Другой способ: используйте тег структуры для достижения.
type RegisterParam struct {
Phone string `form:"phone" json:"phone" validate:"required,len=11"`
Password string `form:"password" json:"password"`
}
func (r RegisterParam) Valid() error {
return validator.New().Struct(r)
}
Последний использует:перейти на doc.org/GOP кг.in/go…Библиотека проверки, проверка параметров веб-фреймворка gin также использует эту схему.
Существует множество сценариев, и пользователям нужно только обратить внимание на значение тега Tag в структуре.
- Логарифмический параметр: Направление проверки: 1. Является ли оно 0. 2. Максимальное значение, минимальное значение (например, операция перелистывания страниц, отображение каждой страницы) 3. Интервал, больше, меньше и т. д.
- Для строковых параметров: направление проверки: 1. Является ли оно нулевым, 2. Перечисление или конкретное значение: eq="a"|eq="b" и т. д.
- Конкретные сценарии: такие как почтовые ящики, цвета, Base64, hex и т. д.
Наиболее часто используются числовые и строковые типы.
ответная информация
Фронтенд и бэкенд разделены, а самый популярный формат обмена данными — json. Хотя он поддерживает разнообразную ответную информацию, такую как html, xml, string, json и т. д.
Для создания API в стиле Restful я рекомендую только json, который удобен для вызова разработчиками внешнего интерфейса или клиентской стороны.
После определения, что формат обмена данными json, какие еще заботы нужны?
- код состояния
- конкретная информация об ответе
{
"code": 200,
"data": {
"id": 1,
"created_at": "2019-06-19T23:14:11+08:00",
"updated_at": "2019-06-20T10:40:09+08:00",
"status": "已付款",
"phone": "18717711717",
"account_id": 1,
"total": 9.6,
"product_ids": [
2,
3
]
}
}
Рекомендуется использовать приведенный выше формат единообразно: код используется для представления кода состояния, а данные используются для представления конкретной информации об ответе.
В случае ошибки рекомендуется следующий формат:
{
"code": 404,
"detail": "/v1/ordeda",
"error": "no route /v1/orderda"
}
Также существует множество типов кодов состояния:
- 1XX: запрос получен
- 2xx: успех
- 3XX: перенаправление
- 4XX: ошибка клиента
- 5XX: ошибка сервера
Выберите код состояния в соответствии с конкретным сценарием.
Реальное применение: определить пакет err в пакете pkg и реализовать метод Error.
type ErrorV1 struct {
Detail string `json:"detail"`
Message string `json:"message"`
Code int `json:"code"`
}
type ErrorV1s []ErrorV1
func (e ErrorV1) Error() string {
return fmt.Sprintf("Detail: %s, Message: %s, Code: %d", e.Detail, e.Message, e.Code)
}
Определите некоторые распространенные сообщения об ошибках и коды ошибок:
var (
// database
ErrorDatabase = ErrorV1{Code: 400, Detail: "数据库错误", Message: "database error"}
ErrorRecordNotFound = ErrorV1{Code: 400, Detail: "记录不存在", Message: "record not found"}
// body
ErrorBodyJson = ErrorV1{Code: 400, Detail: "请求消息体失败", Message: "read json body fail"}
ErrorBodyIsNull = ErrorV1{Code: 400, Detail: "参数为空", Message: "body is null"}
)
разное
- Документация по API: документация по swagger более популярна. Документация — важный способ для других разработчиков понять интерфейс. Учитывая стоимость связи, документация по API имеет важное значение.
- Журналы: журналы необходимы разработчикам для просмотра проблем. Бизнес-объем не сложен. Журналы можно записывать в файлы и сохранять. Для более сложных сценариев можно выбрать ELK.
- Dockerfile: веб-приложение, конечно, очень подходящее для развертывания на хосте в виде контейнера.
- Makefile: команды сборки проекта, включая некоторые тесты, сборки, запуск автозапуска и т. д.