A Do-it-yourself Implementation of the Golang Middleware | by Dwen | Apr, 2022

Picture by Surface on Unsplash

What do you consider once we speak about middleware? Some individuals would say that middleware is an interceptor/filter, which can be utilized to do some pre-processing and post-processing.

You’re proper, middleware is basically to offer normal course of management.

What to manage? That’s, the pre-processing and post-processing talked about above.

Typically, we’ll put frequent course of management code into middleware to do it, similar to:

  • Permission verification
  • Request monitoring
  • Pre-parameter examine
  • Logging

If we have been to implement a middleware, what would we have to do?

First, let’s analyze what features a middleware must have.

  • Can intercept requests, failure to intercept requests is meaningless.
  • Can bind a number of generic course of management features.
  • Binding order to execute course of management features.

After evaluation, we will conclude that the middleware is used to deal with the decision relationship of generic features.

Its goal is to wrap the enterprise perform and return the identical sort of technique.

By wrapping the enterprise perform within the innermost half, after which wrapping the generic course of management perform layer by layer outdoors, the entire appears to be like a bit just like the meals “onion”, so additionally it is referred to as the 「onion mannequin」.

In every middleware, there can be three elements:

  • Pre-interception of enterprise logic.
  • Subsequent processing.
  • Publish-interception after enterprise logic processing is accomplished.

The next determine reveals a processing circulation with 3 middleware.

Right here we implement a rudimentary model of the middleware with the assistance of the http package deal.

After beginning this system, we use postman to entry http://localhost:8099, after which set Authorizationthe parameter within the header with the worth foo .

After accessing it, we get the next outcome.

View consumer printing:

[AuthHandle] token is: foo[LogHandle] begin at: 1645004016price 0.000103 second

In fact, our middleware right here is simply an empty shell, however it doesn’t do something.

Right here we simply simulate the impact of a easy model of the middleware, which is applied within the type of nested wrappers, leaving an enormous downside that each time we have to add a brand new middleware, we have to nest it like this, which can ultimately result in code bloat.

The ultimate code would appear to be the next.

middlewareA(middlewareB(middlewareC(middlewareD(enterprise()))))

When there’s an increasing number of middleware, this will’t be maintained in any respect.

Let’s analyze how middleware is applied within the Gin framework.

In comparison with the dumb technique we used above, the implementation of Gin is significantly better, it makes use of an array of features.

At any time when a request is available in, Ginallocates a Contextfor the request, which carries an array of HandlerFuncto be processed, together with an index indexthat retains monitor of which HandlerFuncis at present being processed.

It must be famous that the default preliminary worth of indexis -1. For an index equal to -1`, you possibly can see the perform ofreset() .

Why is he utilizing the default -1 right here? As a result of within the handleHTTPRequest() technique, Gincalls the Subsequent() perform straight.

In order that c.index++ could be received a worth of 0, that’s, begin executing the primary subscripted middleware.

In fact, it’s also possible to manually execute the 0th middleware within the middleware array by default, so that you don’t have to set it to -1, you possibly can set the default worth to 0, however it doesn’t really feel that elegant.

There’s a level to notice right here, the kind of attribute index is int8, we all know that the size vary of int8 is -128 ~ 127 .

So theoretically Gin can solely deal with as much as 127 HandlerFunc.

However the precise state of affairs is that solely 63could be processed, let’s have a look at the code subsequent.

Gin units up the nesting of HandlerFunc by utilizing the use() technique.

Discover within the code above that the worth of the variable abortIndex is simply 63.

When the Group() perform is named, the HandlerFunc can be merged, and the utmost quantity can be decided right here, and if it exceeds the abortIndex, an error can be reported.

The Use() perform could be very easy, simply append HandlerFunc to HandlersChain.

How one can implement the decision to HandlerFunc operation within the context?

The reply is straightforward, we simply have to name the Subsequent() perform, inside this technique, the index of the present context execution can be added by one, after which the subsequent `HandlerFunc` technique can be executed.

The benefit of that is that when the decision stack comes again with an index bigger than the scale of HandlersChain, it is not going to be executed repeatedly.

On the similar time, including one every time additionally ensures the order execution of HandlerFunc, as a result of the underside layer is named in line with the array index.

The particular code is as follows:

Right here we have to clarify why we have to execute c.index++ twice.

If there’s an array with 3 parts and the Subsequent() perform is executed sequentially when the final factor is executed, that’s when the subscript is the same as 2, if there isn’t any second version, it can result in a useless loop.

As a result of when the subscript is 2 is the final factor, he doesn’t have the subsequent handler() that may be executed.

The loop situation is the index is lower than the size of the array, the place 2 < 3is at all times established, getting into an infinite loop.

So right here add a second c.index++, when the final factor is executed, the index continues so as to add 1, the ultimate worth can be 3, and the loop exits.

After speaking about including middleware and executing middleware, the subsequent step is to implement a perform, which is to interrupt the middleware upfront.

For instance, if I fail a check-in middleware and need to return on to the middleware and never proceed, I want to interrupt all middleware processing early.

Let’s first have a look at the implementation of Gin.

Right here, you solely have to set the index to the utmost worth, as a result of every HandlerFunc will decide the index when calling the Subsequent() technique, and it’ll not be executed after the index exceeds the size of the slice of the perform to be processed.

Nicely, now that we perceive the design idea of Gin, let’s implement a easy model of middleware.

Outline a Context construction.

There are two key parts inside it handlersand index.

handlers is used to retailer our generic judgment logic for every middleware, which can be equal to a layer by layer onion.

index is used to retailer the variety of middleware we’re at present executing.

If we use the onion mannequin, it’s the variety of layers we’re at present peeling.

Outline add middleware technique.

It’s quite simple right here, simply add the handlers pending perform to our context property MyContext .

Outline execution technique.

After I’ve the add technique, I additionally have to have the execute technique, which corresponds to the operation of peeling the subsequent layer of onion within the onion mannequin.

In Gin this technique is named Subsequent(), and right here we use the identical identify.

Begin utilizing.

Nicely, after making ready every part above, let’s take a look at it.

Output after execution:

[AuthMiddleware Start][LogHandle] begin at: 1645605404GET handler funcprice 0.000006 second

Thanks for studying this text. For those who discover any errors on this article, please let me know.

More Posts