SoFunction
Updated on 2025-03-05

Golang realizes the development notes sharing of short URL/short link services

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!