Project address:/astak16/shortlink
Error handling
When processing business logic, if an error occurs, the format of the error response needs to be handled uniformly, which can facilitate the front-end to process error information
Therefore, you need to define an Error interface, which contains the error interface and a Status() method to return the error status code
type Error interface { error Status() int }
This interface is used to determine the type of error. In go, you can use e.(type) to determine the type of error
func respondWithError(w , err error) { switch e.(type) { case Error: respondWithJSON(w, (), ()) default: respondWithJSON(w, , ()) } }
To implement the Error interface in go, you only need to implement the Error() and Status() methods
func () Error() string { return "" } func () Status() int { return 0 }
The method defined in this way can only return fixed text and status codes. If you want to return dynamic content, you can define a structure
Then the Error and Status methods accept the StatusError type
In this way, as long as the structure of StatusError type is satisfied, dynamic content can be returned
So the above code can be modified to:
type StatusError struct { Code int Err error } func (se StatusError) Error() string { return () } func (se StatusError) Status() int { return }
middlerware
RecoverHandler
The middleware RecoverHandler function is to capture panic through defer and then return 500 status codes.
func RecoverHandler(next ) { fn := func(w , r *) { defer func() { if r := recover(); r != nil { ("Recover from panic %+v", r) (w, (500), 500) } }() (w, r) } return (fn) }
LoggingHandler
LoggingHandler functions to record the time-consuming request
func (m Middleware) LoggingHandler(next ) { fn := func(w , r *) { start := () (w, r) end := () ("[%s] %q %v", , , (start)) } return (fn) }
Middleware usage
alice is a middleware library in go. Middleware can be added through () and the specific use is as follows:
m := (, ) ("/api/v1/user", (controller)).Methods("POST")
Generate short links
redis connection
func NewRedisCli(addr string, passwd string, db int) *RedisCli { c := (&{ Addr: addr, Password: passwd, DB: db, }) if _, err := ().Result(); err != nil { panic(err) } return &RedisCli{Cli: c} }
Generate a unique ID
redis can generate a unique self-increment ID based on a key name. This key name can be arbitrary. This method is Incr
The code is as follows:
err = (URLIDKEY).Err() if err != nil { return "", err } id, err := (URLIDKEY).Int64() if err != nil { return "", err } (id) // Each call will increase automatically
Store and parse short links
An ID corresponds to a url, which means that when an id is passed outside, the corresponding url needs to be returned.
func Shorten() { err := ((ShortlinkKey, eid), url, *(exp)).Err() if err != nil { return "", err } } func UnShorten() { url, err := ((ShortlinkKey, eid)).Result() }
redis precautions
There are two cases where the error returned by redis:
- Indicates that no corresponding value was found
- Other errors indicate that a redis service error occurred
Therefore, when using redis, you need to determine the returned error type
if err == { // No corresponding value was found} else if err != nil { // There is an error in redis service} else { // Correct response}
test
In a test case, how do you initiate a request and then get the response data?
1. Construct the request
var jsonStr = []byte(`{"url":"","expiration_in_minutes":60}`) req, err := ("POST", "/api/shorten", (jsonStr)) if err != nil { (err) } ("Content-Type", "application/json")
2. Capture http response
rw := ()
3. The simulation request is processed
(rw, req)
4. Analyze the response
if != { ("Excepted status created, got %d", ) } resp := struct { Shortlink string `json:"shortlink"` }{} if err := ().Decode(&resp); err != nil { ("should decode the response", err) }
Final full code:
var jsonStr = []byte(`{"url":"","expiration_in_minutes":60}`) req, err := ("POST", "/api/shorten", (jsonStr)) if err != nil { (err) } ("Content-Type", "application/json") rw := () (rw, req) if != { ("Excepted status created, got %d", ) } resp := struct { Shortlink string `json:"shortlink"` }{} if err := ().Decode(&resp); err != nil { ("should decode the response") }
Code explanation
( | )
It is to set the log output flag
They are all flag constants, connected with vertical lines |, which are bit operators, which combine them into an integer value as an argument to ()
- It is standard time format: 2022-01-23 01:23:23
- It is file name and line number::23
When we use output logs, the time, file name, line number information will be automatically brought with us
Use the recover function
The recover function is similar to the try...catch of other languages, used to capture panic and do some processing
How to use:
func MyFunc() { defer func() { if r := recover(); r != nil { // Handle panic situation } } }
What should be noted is:
- The recover function can only be used in defer. If used outside defer, it will directly return nil
- The recover function will only take effect after panic. If it is called before panic, it will directly return nil
- The recover function can only capture the panic of the current goroutine, but not the panic of other goroutines
(w, r)
(w, r), used to pass http request to the next handler
The difference between HandleFunc and Handle
HandleFunc accepts a function of normal type:
func myHandle(w , r *) {} ("xxxx", myHandle)
Handle receives a function that implements the Handler interface:
func myHandler(w , r *) {} ("xxxx", (myHandler))
Their difference is: using Handle requires packaging yourself, and using HandleFunc does not require
defer ()
Why is there no () method?
Because the header is not a resource, and the body is a resource, in go, after operating the resource, the resource must be closed in time, so go provides the Close() method for the body
Yes type interface, indicating an object that can read response data and close the response body
()
After the code has been executed (res), it will continue to execute, unless the execution of the reture and panic function is displayed.
func controller(w , r *) { if res, err := xxx; err != nil { respondWithJSON(w, , err) } // If there is code here, it will continue to execute} func respondWithJSON(w , code int, payload interface{}) { res, _ (payload) ().Set("Content-Type", "application/json") (code) (res) }
It should be noted that although () is executed, it will continue to execute, but no modification or writing any content will be made to the response, because () has written the response to
Get request parameters
Routing /api/info?shortlink=2
("/api/info", ()).Methods("GET") func getShortlinkInfo(w , r *) { vals := () s := ("shortlink") (s) // 2 }
Routing /2
("/{shortlink:[a-zA-Z0-9]{1,11}}", ()).Methods("GET") func redirect(w , r *) { vars := (r) shortlink := vars["shortlink"] (shortlink) // 2 }
Get the request body
() is to parse the body content of the http request into json format
is a type that represents the requested original data
If the association is successful, you can use the Decode() method to parse json data
type User struct { Name string `json:"name"` Age int `json:"age"` } func controller(w , r *){ var user User if err := ().Decode(&user); err != nil { (err) } (user) }
new
Used to create a new zero value object and return a pointer to that object
It takes a type as an argument and returns a pointer to that type
Suitable for any allocable type, such as primitive types, structures, arrays, slices, maps, and interfaces.
// Create a new zero-value object of type int and return a pointer to itptr := new(int) // 0
It should be noted that: new only allocates memory and is initialized to zero value, and will not perform any further initialization of the object. If you need to perform custom initialization operations on the object, you can use structure literals or constructors, etc.
This is the article shared about Golang's development notes on implementing short URL/short link services. For more related Golang short URL content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!