当前位置:网站首页>14. User web layer services (II)
14. User web layer services (II)
2022-07-28 11:53:00 【Endless character】
Catalog
One 、 Paging optimization
- Paging optimization : We paged before pn and psize It's dead , Now it needs to be optimized to display paging data through page parameter requests
- user_web\api\api_user.go
func GetUserList(ctx *gin.Context) {
// Dial up connection users grpc The server
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d",
global.ServerConfig.UserSrvInfo.Host,
global.ServerConfig.UserSrvInfo.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
zap.S().Errorw("[GetUserList] Connect 【 User service failed 】", "msg", err.Error())
}
// Generate grpc Of client And call the interface
userSrvClient := proto.NewUserClient(userConn)
pn := ctx.DefaultQuery("pn", "0")
pnInt, _ := strconv.Atoi(pn)
pSize := ctx.DefaultQuery("psize", "10")
pSizeInt, _ := strconv.Atoi(pSize)
rsp, err := userSrvClient.GetUserList(context.Background(), &proto.PageInfo{
Pn: uint32(pnInt),
PSize: uint32(pSizeInt),
})
if err != nil {
zap.S().Errorw("[GetUserList] Inquire about 【 User list 】 Failure ")
HandleGrpcErrorToHttp(err, ctx)
return
}
result := make([]interface{
}, 0)
for _, value := range rsp.Data {
user := response.UserResponse{
Id: value.Id,
NickName: value.NickName,
//Birthday: time.Time(time.Unix(int64(value.BirthDay), 0)).Format("2006-01-02"),
Birthday: response.JsonTime(time.Unix(int64(value.BirthDay), 0)),
Gender: value.Gender,
Mobile: value.Mobile,
}
result = append(result, user)
}
ctx.JSON(http.StatusOK, result)
}
- Paging optimization test


Two 、 Sign in + Form validation
1 - Login implementation
- user_web\global\global.go: Add global variable Trans
package global
import (
ut "github.com/go-playground/universal-translator"
"web_api/user_web/config"
)
var (
Trans ut.Translator
ServerConfig *config.ServerConfig = &config.ServerConfig{
}
)
- user_web\forms\form_user.go: Add user form
package forms
type PassWordLoginForm struct {
Mobile string `form:"mobile" json:"mobile" binding:"required"` // There are specifications for the format of mobile phone numbers , Customize validator
PassWord string `form:"password" json:"password" binding:"required,min=3,max=20"`
}
- user_web\initialize\init_validator.go: Initialize translator
package initialize
import (
"fmt"
"reflect"
"strings"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"web_api/user_web/global"
)
func InitTrans(locale string) (err error) {
// modify gin In the framework validator Engine properties , Implementation customization
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// Register to get json Of tag The custom method of
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
zhT := zh.New() // Chinese translator
enT := en.New() // English translator
// The first parameter is the alternate locale , The latter parameter is the language environment that should be supported
uni := ut.New(enT, zhT, enT)
global.Trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", locale)
}
switch locale {
case "en":
en_translations.RegisterDefaultTranslations(v, global.Trans)
case "zh":
zh_translations.RegisterDefaultTranslations(v, global.Trans)
default:
en_translations.RegisterDefaultTranslations(v, global.Trans)
}
return
}
return
}
- user_web\router\router_user.go: Add logged in router
package router
import (
"web_api/user_web/api"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func InitUserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user")
zap.S().Info(" Configure user related url")
{
UserRouter.GET("list", api.GetUserList)
UserRouter.POST("pwd_login", api.PassWordLogin)
}
}
- user_web\api\api_user.go: Add login api
func PassWordLogin(c *gin.Context) {
// Form validation
passwordLoginForm := forms.PassWordLoginForm{
}
if err := c.ShouldBind(&passwordLoginForm); err != nil {
HandleValidatorError(c, err)
return
}
}
func HandleValidatorError(c *gin.Context, err error) {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
}
c.JSON(http.StatusBadRequest, gin.H{
"error": removeTopStruct(errs.Translate(global.Trans)),
})
return
}
func removeTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{
}
for field, err := range fileds {
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}
- user_web\main.go: Add call initialization translator
package main
import (
"fmt"
"web_api/user_web/global"
"web_api/user_web/initialize"
"go.uber.org/zap"
)
func main() {
//1. initialization logger
initialize.InitLogger()
//2. Initialize configuration file
initialize.InitConfig()
//3. initialization routers
Router := initialize.Routers()
//4. Initialize translation
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
}
/* 1. S() You can get a global sugar, We can set a global logger 2. Logs are graded ,debug, info , warn, error, fetal debug The minimum ,fetal The highest , If configured to info, All than info Low ones will not output NewProduction The default log level is info NewDevelopment The default log level is debug 3. S Functions and L Functions are useful , Provides a global security access logger Way */
zap.S().Debugf(" Start the server , port : %d", global.ServerConfig.Port)
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic(" Boot failure :", err.Error())
}
}
- YApi: Add login interface



2 - Customize mobile Validator
Be careful -> validator want import v10 Version of :
github.com/go-playground/validator/v10
- user_web\validator\validators.go:
package validator
import (
"regexp"
"github.com/go-playground/validator/v10"
)
func ValidateMobile(fl validator.FieldLevel) bool {
mobile := fl.Field().String()
// Use regular expressions to determine whether it is legal
ok, _ := regexp.MatchString(`^1([38][0-9]|14[579]|5[^4]|16[6]|7[1-35-8]|9[189])\d{8}$`, mobile)
return ok
}
- user_web\forms\form_user.go: Add a custom verifier name
package forms
type PassWordLoginForm struct {
Mobile string `form:"mobile" json:"mobile" binding:"required,mobile"` // There are specifications for the format of mobile phone numbers , Customize validator
PassWord string `form:"password" json:"password" binding:"required,min=3,max=20"`
}
- user_web\main.go: Register custom validators
package main
import (
"fmt"
"web_api/user_web/global"
"web_api/user_web/initialize"
"github.com/gin-gonic/gin/binding"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
myvalidator "web_api/user_web/validator"
)
func main() {
//1. initialization logger
initialize.InitLogger()
//2. Initialize configuration file
initialize.InitConfig()
//3. initialization routers
Router := initialize.Routers()
//4. Initialize translation
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
}
// Register verifier
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
_ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
return ut.Add("mobile", "{0} Illegal cell phone number !", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("mobile", fe.Field())
return t
})
}
/* 1. S() You can get a global sugar, We can set a global logger 2. Logs are graded ,debug, info , warn, error, fetal debug The minimum ,fetal The highest , If configured to info, All than info Low ones will not output NewProduction The default log level is info NewDevelopment The default log level is debug 3. S Functions and L Functions are useful , Provides a global security access logger Way */
zap.S().Debugf(" Start the server , port : %d", global.ServerConfig.Port)
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic(" Boot failure :", err.Error())
}
}
3 - Improve login logic
- Improve the login business : The logic after the previous login form is successful is not handled
- user_web\api\api_user.go: Here we call rpc Of userSrvClient.CheckPassWord, Login requires a real database phone number and password
func PassWordLogin(c *gin.Context) {
// Form validation
passwordLoginForm := forms.PassWordLoginForm{
}
if err := c.ShouldBind(&passwordLoginForm); err != nil {
HandleValidatorError(c, err)
return
}
// Dial up connection users grpc The server
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d",
global.ServerConfig.UserSrvInfo.Host,
global.ServerConfig.UserSrvInfo.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
zap.S().Errorw("[GetUserList] Connect 【 User service failed 】", "msg", err.Error())
}
// Generate grpc Of client And call the interface
userSrvClient := proto.NewUserClient(userConn)
// Login logic
if rsp, err := userSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequest{
Mobile: passwordLoginForm.Mobile,
}); err != nil {
if e, ok := status.FromError(err); ok {
switch e.Code() {
case codes.NotFound:
c.JSON(http.StatusBadRequest, map[string]string{
"mobile": " The user doesn't exist ",
})
default:
c.JSON(http.StatusInternalServerError, map[string]string{
"mobile": " Login failed ",
})
}
return
}
} else {
// I just found the user , Did not check password
if passRsp, pasErr := userSrvClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
Password: passwordLoginForm.PassWord,
EncryptedPassword: rsp.PassWord,
}); pasErr != nil {
c.JSON(http.StatusInternalServerError, map[string]string{
"password": " Login failed ",
})
} else {
if passRsp.Success {
c.JSON(http.StatusOK, map[string]string{
"msg": " Login successful ",
})
} else {
c.JSON(http.StatusBadRequest, map[string]string{
"msg": " Login failed ",
})
}
}
}
}

3、 ... and 、json web token
1 - session Application in single architecture

2 - session The problem of mechanism under microservice
- In microservices session problem : In microservices , Each service is independent , The database is also independent , Therefore, a public data warehouse is needed to store session Can be used in microservices session Mechanism
- In microservice session Solution analysis
- redis colony : User service will session Save in redis in , Other services are based on sessionid To redis Query to get users

- redis colony : User service will session Save in redis in , Other services are based on sessionid To redis Query to get users
3 - json web token
- JWT Official website :https://jwt.io/
- What is? json web token:JSON Web Token Is an open standard protocol , It defines a “ compact ” and “ Self contained ” The way , It is used between the parties as JSON Objects transmit information securely
- json web token Use scenarios
- Authorization( to grant authorization ): This is the use of JWT The most common scenario . Once the user logs in , Each subsequent request will contain JWT, Allow users to access the routes allowed by the token 、 Services and resources . Single sign on is now widely used JWT A feature of , Because it costs very little , And it's easy to use across domains
- Information Exchange( Information switching ): For the safe transmission of information between parties ,json web tokens No doubt it's a good way . because JWT Can be signed , for example , Use public key / Private key pair , You can be sure that the sender is the person they say . in addition , Because signatures are computed using headers and payloads , You can also verify whether the content has been tampered with
- JWT data structure :JSON Web Tokens By the point of use ( .) The three separate parts make up , They are
- Header( Head )
- Payload( load )
- Signature( Signature )
- A typical JWT:
xxxxx.yyyyy.zzzzz

- JWT Application in microservices : As long as the same key encryption and decryption is used between various services , Then the services can be connected through sessionid To pass on
- JWT Workflow

- JWT advantage
- because json The generality of , therefore JWT Cross language support is available , image JAVA,JavaScript,NodeJS,PHP And many other languages can be used .
- Because of the payload part , therefore JWT It can store some non sensitive information necessary for other business logic in itself .
- Easy to transmit ,jwt It's very simple , Bytes are very small , So it's very easy to transmit .
- It doesn't need to save session information on the server , So it's easy to apply extensions
- JWT Security
- Should not be in jwt Of payload Some store sensitive information , Because this part is the decryptable part of the client
- Well protected secret Private key , The private key is very important
- If possible , Please use https agreement
4 - gin Integrate JWT
- add to yaml To configure
- Use a random password generator to generate a random key :https://suijimimashengcheng.bmcx.com/
//user_web\config_debug.yaml
name: 'user-web'
port: '8081'
user_srv:
host: '127.0.0.1'
port: '50051'
jwt:
key: 'VYLDYq3&hGWjWqF$K1ih'
//user_web\config_pro.yaml
name: 'user-web'
port: '8031'
user_srv:
host: '127.0.0.1'
port: '50052'
jwt:
key: 'VYLDYq3&hGWjWqF$K1ih'
- user_web\middlewares\jwt.go: Middleware addition jwt
package middlewares
import (
"errors"
"net/http"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"web_api/user_web/global"
"web_api/user_web/models"
)
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// Here jwt Authentication header information x-token When logging in, return to token Information Here, the front end needs to token Store in cookie Or local localSstorage in However, the expiration time needs to be negotiated with the back end You can agree to refresh the token or log in again
token := c.Request.Header.Get("x-token")
if token == "" {
c.JSON(http.StatusUnauthorized, map[string]string{
"msg": " Please log in ",
})
c.Abort()
return
}
j := NewJWT()
// parseToken analysis token Information contained
claims, err := j.ParseToken(token)
if err != nil {
if err == TokenExpired {
if err == TokenExpired {
c.JSON(http.StatusUnauthorized, map[string]string{
"msg": " Authorization expired ",
})
c.Abort()
return
}
}
c.JSON(http.StatusUnauthorized, " Not logged in ")
c.Abort()
return
}
c.Set("claims", claims)
c.Set("userId", claims.ID)
c.Next()
}
}
type JWT struct {
SigningKey []byte
}
var (
TokenExpired = errors.New("Token is expired")
TokenNotValidYet = errors.New("Token not active yet")
TokenMalformed = errors.New("That's not even a token")
TokenInvalid = errors.New("Couldn't handle this token:")
)
func NewJWT() *JWT {
return &JWT{
[]byte(global.ServerConfig.JWTInfo.SigningKey), // You can set the expiration time
}
}
// Create a token
func (j *JWT) CreateToken(claims models.CustomClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.SigningKey)
}
// analysis token
func (j *JWT) ParseToken(tokenString string) (*models.CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &models.CustomClaims{
}, func(token *jwt.Token) (i interface{
}, e error) {
return j.SigningKey, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenInvalid
}
}
}
if token != nil {
if claims, ok := token.Claims.(*models.CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, TokenInvalid
} else {
return nil, TokenInvalid
}
}
// to update token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
jwt.TimeFunc = func() time.Time {
return time.Unix(0, 0)
}
token, err := jwt.ParseWithClaims(tokenString, &models.CustomClaims{
}, func(token *jwt.Token) (interface{
}, error) {
return j.SigningKey, nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(*models.CustomClaims); ok && token.Valid {
jwt.TimeFunc = time.Now
claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
return j.CreateToken(*claims)
}
return "", TokenInvalid
}
- user_web\models\request.go:CustomClaims structure
package models
import (
"github.com/dgrijalva/jwt-go"
)
type CustomClaims struct {
ID uint
NickName string
AuthorityId uint
jwt.StandardClaims
}
- user_web\config\config.go: add to JWTConfig structure
package config
type UserSrvConfig struct {
Host string `mapstructure:"host" json:"host"`
Port int `mapstructure:"port" json:"port"`
}
type JWTConfig struct {
SigningKey string `mapstructure:"key" json:"key"`
}
type ServerConfig struct {
Name string `mapstructure:"name" json:"name"`
Port int `mapstructure:"port" json:"port"`
UserSrvInfo UserSrvConfig `mapstructure:"user_srv" json:"user_srv"`
JWTInfo JWTConfig `mapstructure:"jwt" json:"jwt"`
}
- user_web\api\api_user.go: Login add jwt
func PassWordLogin(c *gin.Context) {
// Form validation
passwordLoginForm := forms.PassWordLoginForm{
}
if err := c.ShouldBind(&passwordLoginForm); err != nil {
HandleValidatorError(c, err)
return
}
// Dial up connection users grpc The server
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d",
global.ServerConfig.UserSrvInfo.Host,
global.ServerConfig.UserSrvInfo.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
zap.S().Errorw("[GetUserList] Connect 【 User service failed 】", "msg", err.Error())
}
// Generate grpc Of client And call the interface
userSrvClient := proto.NewUserClient(userConn)
// Login logic
if rsp, err := userSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequest{
Mobile: passwordLoginForm.Mobile,
}); err != nil {
if e, ok := status.FromError(err); ok {
switch e.Code() {
case codes.NotFound:
c.JSON(http.StatusBadRequest, map[string]string{
"mobile": " The user doesn't exist ",
})
default:
c.JSON(http.StatusInternalServerError, map[string]string{
"mobile": " Login failed ",
})
}
return
}
} else {
// I just found the user , Did not check password
if passRsp, pasErr := userSrvClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
Password: passwordLoginForm.PassWord,
EncryptedPassword: rsp.PassWord,
}); pasErr != nil {
c.JSON(http.StatusInternalServerError, map[string]string{
"password": " Login failed ",
})
} else {
if passRsp.Success {
// Generate token
j := middlewares.NewJWT()
claims := models.CustomClaims{
ID: uint(rsp.Id),
NickName: rsp.NickName,
AuthorityId: uint(rsp.Role),
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix(), // Effective time of signature
ExpiresAt: time.Now().Unix() + 60*60*24*30, //30 Days overdue
Issuer: "imooc",
},
}
token, err := j.CreateToken(claims)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"msg": " Generate token Failure ",
})
return
}
c.JSON(http.StatusOK, gin.H{
"id": rsp.Id,
"nick_name": rsp.NickName,
"token": token,
"expired_at": (time.Now().Unix() + 60*60*24*30) * 1000,
})
} else {
c.JSON(http.StatusBadRequest, map[string]string{
"msg": " Login failed ",
})
}
}
}
}


Four 、 Interface login permission verification
1 - GetUserList Add login authentication
- user_web\router\router_user.go
package router
import (
"web_api/user_web/api"
"web_api/user_web/middlewares"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func InitUserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user")
//UserRouter := Router.Group("user").Use(middlewares.JWTAuth()) // To the whole user Add login verification
zap.S().Info(" Configure user related url")
{
UserRouter.GET("list", middlewares.JWTAuth(), api.GetUserList)
UserRouter.POST("pwd_login", api.PassWordLogin)
}
}

2 - belt token Request
Why is it headers What's added is x-token?
This should be noticed in user_web\middlewares\jwt.go in ,JWTAuth The parameters filled in the method are x-token
So we use jwt Words , It needs to be in headers Add x-token

- Log in and copy out token:



3 - Get login user information
- stay jwt Of JWTAuth In, we see that the code is gin Of context set It's worth it
c.Set("claims", claims)c.Set("userId", claims.ID)
- user_web\api\api_user.go: Use set To get login user information
func GetUserList(ctx *gin.Context) {
// Dial up connection users grpc The server
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d",
global.ServerConfig.UserSrvInfo.Host,
global.ServerConfig.UserSrvInfo.Port),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
zap.S().Errorw("[GetUserList] Connect 【 User service failed 】", "msg", err.Error())
}
claims, _ := ctx.Get("claims")
currentUser := claims.(*models.CustomClaims)
zap.S().Infof(" Access users : %d", currentUser.ID)
//... Omit

4 - YApi overall situation header Set up
- We were in GetUserList Medium header Added x-tocken: If every request is set like this, it's too cumbersome ,YApi Global settings can be used in


5 - The administrator verifies the middleware
- Demand analysis : We hope GetUserList Only administrators can call , Ordinary users have no permission to call
- user_web\middlewares\admin.go
package router
import (
"web_api/user_web/api"
"web_api/user_web/middlewares"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func InitUserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user")
//UserRouter := Router.Group("user").Use(middlewares.JWTAuth()) // To the whole user Add login verification
zap.S().Info(" Configure user related url")
{
UserRouter.GET("list", middlewares.JWTAuth(), middlewares.IsAdminAuth(), api.GetUserList)
UserRouter.POST("pwd_login", api.PassWordLogin)
}
}
- user_web\router\router_user.go:router Add administrator judgment middleware
package router
import (
"web_api/user_web/api"
"web_api/user_web/middlewares"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func InitUserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user")
//UserRouter := Router.Group("user").Use(middlewares.JWTAuth()) // To the whole user Add login verification
zap.S().Info(" Configure user related url")
{
UserRouter.GET("list", middlewares.JWTAuth(), middlewares.IsAdminAuth(), api.GetUserList)
UserRouter.POST("pwd_login", api.PassWordLogin)
}
}

5、 ... and 、 Cross-domain problem
1 - Cross domain problem analysis
- When will the browser launch options Pre inspection request : In the case of non simple request and cross domain , The browser will launch option Pre inspection request
- What is cross-domain : When a request url The agreement 、 domain name 、 Any one of the three ports and the current page url Difference is cross domain
| Current page url | The requested page url | Cross domain or not | reason |
|---|---|---|---|
| http://www.test.com | http://www.test.com/index.html | no | Homology ( agreement 、 domain name 、 Same port number ) |
| http://www.test.com | https://www.test.com/index.html | Cross domain | Different agreements (http/https) |
| http://www.test.com | http://www.baidu.com/ | Cross domain | The main domain name is different (test/baidu) |
| http://www.test.com | http://blog.baidu.com/ | Cross domain | Different subdomains (www/blog) |
| http://www.test.com:8080/ | http://www.test.com:7001/ | Cross domain | Port number is different (8080/7001) |
- What is a simple request : A simple request needs to meet the following two conditions
- The request method is one of three :HEAD、GET、POST
- HTTP The header information of does not exceed the following fields
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type: Limited to (application/x-www-form-urlencoded、multipart/form-data、text/plain)
- What is not a simple request : Non simple requests are complex requests
- The request method is PUT or DELETE
- Content-Type Field type is application/json
- Add extra http header such as access_token
- Non simple requests are cross domain : Non simple requests will be initiated once body Of OPTIONS request , It's called a pre inspection request , Used to request permission information from the server , After the pre inspection request is successfully responded , To initiate real http request
The pre check request result of the browser can be set Access-Control-Max-Age Cache
2 - Cross domain problem presentation
- user_web\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<button type="button" id="query"> Request data </button>
<div id="content" style="background-color: aquamarine; width: 300px;height: 500px"></div>
</body>
<script type="text/javascript"> $("#query").click(function () {
$.ajax( {
url:"http://127.0.0.1:8081/u/v1/user/list", dataType: "json", type: "get", beforeSend: function(request) {
request.setRequestHeader("x-token", " eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiTmlja05hbWUiOiJib2JieTAiLCJBdXRob3JpdHlJZCI6MSwiZXhwIjoxNjYxNTAwNTkzLCJpc3MiOiJpbW9vYyIsIm5iZiI6MTY1ODkwODU5M30.d0We9xsTWIJdUNYYDWtxLJABnjBdTle6M5t5ezjLUEU") }, success: function (result) {
console.log(result.data); $("#content").text(result.data) }, error: function (data) {
alert(" Request error ") } } ); }); </script>
</html>

3 - Cross domain problem solving
- user_web\middlewares\cors.go: Add middleware to solve cross domain
package middlewares
import (
"net/http"
"github.com/gin-gonic/gin"
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token, x-token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PATCH, PUT")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
}
}
- user_web\initialize\init_router.go: by router Add cross domain Middleware
package initialize
import (
"github.com/gin-gonic/gin"
"web_api/user_web/middlewares"
"web_api/user_web/router"
)
func Routers() *gin.Engine {
Router := gin.Default()
// Configure cross domain
Router.Use(middlewares.Cors())
ApiGroup := Router.Group("/u/v1")
router.InitUserRouter(ApiGroup)
return Router
}

6、 ... and 、 Complete source code
边栏推荐
- Advanced database technology learning notes 1 -- Oracle deployment and pl/sql overview
- Full version of H5 social chat platform source code [complete database + complete document tutorial]
- 【补题日记】[2022牛客暑期多校2]L-Link with Level Editor I
- R language ggplot2 visualization: ggdensity function of ggpubr package visualizes density graph and uses stat_ overlay_ normal_ Density function superimposes positive distribution curve, custom config
- 数字孪生轨道交通:“智慧化”监控疏通城市运行痛点
- 简单选择排序与堆排序
- 目标检测领域必看的6篇论文
- R language - some metrics for unbalanced data sets
- Open source huizhichuang future | 2022 open atom global open source summit openatom openeuler sub forum was successfully held
- Database advanced learning notes - system package
猜你喜欢

拥抱开源指南

Redis installation

Service Workers让网站动态加载Webp图片

Cvpr2020 best paper: unsupervised learning of symmetric deformable 3D objects

Introduction to web security RADIUS protocol application

Four advantages of verification code to ensure mailbox security

Excel shortcut keys (letters + numbers) Encyclopedia

A lock faster than read-write lock. Don't get to know it quickly

Boutique scheme | Haitai Fangyuan full stack data security management scheme sets a "security lock" for data

Router firmware decryption idea
随机推荐
R language - some metrics for unbalanced data sets
大佬们,问下,这个不能checkpoint,因为有个jdbc的task任务状态是FINISHED,
简单选择排序与堆排序
R language uses oneway The test function performs one-way ANOVA
R language ggplot2 visualization: use the ggdotplot function of ggpubr package to visualize dot plot, set the add parameter, add violin graph to the dot plot, and add the vertical line of mean standar
Database advanced learning notes -- object type
Techniques for visualizing large time series.
MySQL (version 8.0.16) command and description
Dialogue with Zhuang biaowei: the first lesson of open source
Router firmware decryption idea
R language ggplot2 visualization: ggdensity function of ggpubr package visualizes density graph and uses stat_ overlay_ normal_ Density function superimposes positive distribution curve, custom config
Are interviews all about memorizing answers?
学会使用MySQL的Explain执行计划,SQL性能调优从此不再困难
A lock faster than read-write lock. Don't get to know it quickly
Understand how to prevent tampering and hijacking of device fingerprints
ASP. Net core 6 framework unveiling example demonstration [29]: building a file server
Network communication protocol classification and IP address
Design a system that supports millions of users
An example of the mandatory measures of Microsoft edge browser tracking prevention
Good use explosion! The idea version of postman has been released, and its functions are really powerful