Giter Site home page Giter Site logo

alimy / mir Goto Github PK

View Code? Open in Web Editor NEW
80.0 4.0 13.0 5.42 MB

Mir is a toolkit for register method handler to http engine router(eg: gin,echo,iris,mux,httprouter) use struct tag info.

Home Page: https://alimy.github.io/mir/

License: Apache License 2.0

Go 96.10% Makefile 3.79% Shell 0.11%
go web gin mux echo iris macaron go-chi go-mir httprouter

mir's People

Contributors

alimy avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

mir's Issues

group just support one level

// Epidemic contents fetch interface define
type Epidemic struct {
	Group           mir.Group `mir:"ncovh5api/THPneumoniaService"`
	GetContents     mir.Post  `mir:"getContents"`
	GetAreaContents mir.Post  `mir:"getAreaContents"`
}

will generate error code.

2020/03/27 23:52:16 generate code start
2020/03/27 23:52:16 generate code finish
mirc/gen/api/ncovh5api/_t_h_pneumonia_service/epidemic.go:3:18: expected ';', found '/'
make: *** [Makefile:38: generate] Error 2

mir:document

  • in doc.go and README add more document of mir's usage
  • add documents describe mir's usage in mir/doc.go
  • add user guide for mir (english/chinese version)

添加 net/http: ServeMux 的支持

需求:

  • 添加 net/http: ServeMux 的支持

方案1:

  • 只支持go1.22+ 的 net/http: ServeMux

方案2:

  • 通过选项支持<go1.21 与 go1.22+ 的 net/http: ServeMux

目前比较倾向于方案1,因为go1.22+后 net/http: ServeMux 添加了很多实用的功能,更值得使用,所以go-mir对其提供支持才更有意义。

Potential case-insensitive import collision

Due to GitHub handle change (to lowercase) for long term purpose, go get may fail fetching github.com/Unknwon/com.
Please consider take some time to update it to github.com/unknwon/com in the go.mod file.
I truly apology for the inconvenience and unintended troubles caused.

just use golang syntax as dsl to define RESTful API

RESTful接口定义:

// file: mirc/routes.go

package routes

import (
	. "github.com/alimy/mir/v3"
	. "github.com/alimy/mir/v3/engine"
)

func init() {
	AddEntry(new(User))
}

type LoginReq struct {
	Name   string `json:"name"`
	Passwd string `json:"passwd"`
}

type LoginResp struct {
	JwtToken string `json:"jwt_token"`
}

// User user interface info
type User struct {
	Chain  Chain                          `mir:"-"`
	Group  Group                          `mir:"v1"`
	Login  func(Post, LoginReq) LoginResp `mir:"/login/"`
	Logout func(Post)                     `mir:"/logout/"`
}

代码生成:

// file: mirc/auto/api/routes.go

// Code generated by go-mir. DO NOT EDIT.
package routes

import (
	"net/http"

	"github.com/alimy/mir/v3"
	"github.com/gin-gonic/gin"
)

type LoginReq struct {
	Name   string `json:"name"`
	Passwd string `json:"passwd"`
}

type LoginResp struct {
	JwtToken string `json:"jwt_token"`
}

type User interface {
	// Chain provide handlers chain for gin
	Chain() gin.HandlersChain

	Login(c *gin.Context, req *LoginReq) (*LoginResp, mir.Error)
	Logout(c *gin.Context) mir.Error

	mustEmbedUnimplementedUserServant()
}

type UserBinding interface {
	BindLogin(c *gin.Context) (*LoginReq, mir.Error)

	mustEmbedUnimplementedUserBinding()
}

type UserRender interface {
	RenderLogin(c *gin.Context, data *LoginResp, err mir.Error)
	RenderLogout(c *gin.Context, err mir.Error)

	mustEmbedUnimplementedUserRender()
}

// UnimplementedUserServant can be embedded to have forward compatible implementations.
type UnimplementedUserServant struct {
}

// UnimplementedSiteBinding can be embedded to have forward compatible implementations.
type UnimplementedSiteBinding struct {
	BindAny func(*gin.Context, any) mir.Error
}

// UnimplementedSiteRender can be embedded to have forward compatible implementations.
type UnimplementedSiteRender struct {
	RenderAny func(*gin.Context, any, mir.Error)
}

// RegisterUserServant register User servant to gin
func RegisterUserServant(e *gin.Engine, s User, b UserBinding, r UserRender) {
	router := e.Group("v1")
	// use chain for router
	middlewares := s.Chain()
	router.Use(middlewares...)

	// register routes info to router
	router.Handle("POST", "/login/", func(c *gin.Context) {
		req, err := b.BindLogin(c)
		if err != nil {
			r.RenderLogin(c, nil, err)
		}
		resp, err := s.Login(c, req)
		r.RenderLogin(c, resp, err)
	})
	router.Handle("POST", "/logout/", func(c *gin.Context) {
		r.RenderLogout(c, s.Logout(c))
	})
}

func (UnimplementedUserServant) Chain() gin.HandlersChain {
	return nil
}

func (UnimplementedUserServant) Login(c *gin.Context, req *LoginReq) (*LoginResp, mir.Error) {
	return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

func (UnimplementedUserServant) Logout(c *gin.Context) mir.Error {
	return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

func (UnimplementedUserServant) mustEmbedUnimplementedUserServant() {}

func (b UnimplementedUserBinding) BindLogin(c *gin.Context) (*LoginReq, mir.Error) {
	obj := new(LoginReq)
	err := b.BindAny(c, obj)
	return obj, err
}

func (b UnimplementedUserBinding) mustEmbedUnimplementedUserBinding() {}

func (r UnimplementedUserRender) RenderLogin(c *gin.Context, data *LoginResp, err mir.Error) {
	r.RenderAny(c, data, err)
}

func (r UnimplementedUserRender) RenderLogout(c *gin.Context, err mir.Error) {
	r.RenderAny(c, nil, err)
}

func (r UnimplementedUserRender) mustEmbedUnimplementedUserRender() {}

接口实现

// file: servants/user.go

package servants

import (
	"github.com/alimy/mir-example/mirc/auto/api"
)

type userSrv struct {
	api.UnimplementedUserServant
}

type userBinding struct {
	*api.UnimplementedUserBinding
}

type userRender struct {
	*api.UnimplementedUserRender
}

func newUserSrv() api.Site {
	return &userSrv{}
}

func newUserBinding() api.SiteBinding {
	return &siteBinding{
		UnimplementedSiteBinding: &api.UnimplementedSiteBinding{
			BindAny: bindAny,
		},
	}
}

func newUserRender() api.SiteRender {
	return &siteRender{
		UnimplementedSiteRender: &api.UnimplementedSiteRender{
			RenderAny: renderAny,
		},
	}
}

func bindAny(c *gin.Context, obj any) mir.Error {
	if err != c.ShouldBind(obj); err != nil {
		return mir.NewError(http.StatusBadRequest, err)
	}
	return nil
}

func renderAny(c *gin.Context, data any, err mir.Error) {
	if err == nil {
		c.JSON(http.StatusOK, data)
	} else {
		c.JSON(err.StatusCode(), err.Error())
	}
}

服务注册:

// file: servants/servants.go

package servants

import (
	"github.com/alimy/mir-example/mirc/auto/api"
	"github.com/gin-gonic/gin"
)

// RegisterServants register all the servants to gin.Engine
func RegisterServants(e *gin.Engine) {
	api.RegisterUserServant(e, newUserSrv(), newUserBinding(), newUserRender())
	
	// TODO: some other servant to register
}

add Context primitive indicator use Engine's raw handler as method

功能特性

  • 添加 Context 原语用于指示代码生成器 生成http引擎框架原生的handler作为接口方法;eg: gin as engine
// go-mir route interface define file
// Site site interface info
type Site struct {
	Chain            `mir:"-"`
	Index            func(Get, Chain)                               `mir:"/index/"`
	Assets           func(Get, Context)                             `mir:"/assets/:name"`
}

使用gin的代码生成器生成的代码如下

type _binding_ interface {
	Bind(*gin.Context) mir.Error
}

type _render_ interface {
	Render(*gin.Context)
}

type _default_ interface {
	Bind(*gin.Context, any) mir.Error
	Render(*gin.Context, any, mir.Error)
}

type Site interface {
	_default_

	// Chain provide handlers chain for gin
	Chain() gin.HandlersChain

	Assets(*gin.Context)
	Index() mir.Error

	mustEmbedUnimplementedSiteServant()
}

// RegisterSiteServant register Site servant to gin
func RegisterSiteServant(e *gin.Engine, s Site, m ...SiteChain) {
	var cc SiteChain
	if len(m) > 0 {
		cc = m[0]
	} else {
		cc = &UnimplementedSiteChain{}
	}
	router := e
	// use chain for router
	middlewares := s.Chain()
	router.Use(middlewares...)

	router.Handle("GET", "/assets/:name", s.Assets)	
	router.Handle("GET", "/index/", append(cc.ChainIndex(), func(c *gin.Context) {
		select {
		case <-c.Request.Context().Done():
			return
		default:
		}

		s.Render(c, nil, s.Index())
	})...)
}

具体示例请参考项目的examples。

适配进度

  • gin
  • chi
  • echo
  • fiber
  • hertz
  • httprouter
  • iris
  • macaron
  • mux
  • mirc

add new dsl to describe api design

package v2

// Site service
service Site {
        _             Chain
	v2            Group
	Index         Get   `/index/`
	Articles      Get   `/articles/:category/`
	Category      Get   `/category/`
        PostArticle   Post  `/articles/:category/`
}

add Thrift idl as DSL support for go-mir

Add Thrift idl as DSL support for go-mir

namespace go car

include "../base/common.thrift"
include "../base/car.thrift"

struct AdminCreateCarRequest {
    1:  string plate_num (api.raw = "plate_num", api.vd = "len($) > 0 && len($) < 25>"),
}

struct AdminDeleteCarRequest {
    1:  string id (api.raw = "id", api.vd = "len($) > 0 && len($) < 25>"),
}

struct AdminGetSomeCarsRequest {}

struct AdminGetAllCarsRequest {}

struct GetCarsRequest {}

struct GetCarRequest {
    1:  string id (api.raw = "id", api.vd = "len($) > 0 && len($) < 25>"),
}

service CarService {
    // for back-stage management
    common.NilResponse AdminCreateCar(1: AdminCreateCarRequest req) (api.post = "/admin/car", api.chain = "true"),
    common.NilResponse AdminDeleteCar(1: AdminDeleteCarRequest req) (api.delete = "/admin/car", api.chain = "true"),
    common.NilResponse AdminGetSomeCars(1: AdminGetSomeCarsRequest req) (api.get = "/admin/car/some"),
    common.NilResponse AdminGetAllCars(1: AdminGetAllCarsRequest req) (api.get = "/admin/car/all"),

    // for mini-program
    common.NilResponse GetCars(1: GetCarsRequest req) (api.get = "/cars"),
    common.NilResponse GetCar(1: GetCarRequest req) (api.get = "/car"),
} (api.group = "v1", api.chain = "true")

添加UseRequestContext支持

源由:

最近在集成OpenTelemetry到使用gin的项目中,使用otelginotelgorm进行 Trace链路追踪(详情请见paopao-ce),集成过程中遇到一些问题,比如使用go-mir生成的代码默认是不带Engine Context或 HTTP Request Context,而otelgin是使用 HTTP Request Context来透传OpenTelemetry相关上下文的,如果需要将从HTTP Request 到DB数据库之间的链路串连在一起,就需要显示的Context来透传链路的上下文信息,也就是需要显示获取最原始请求的HTTP Request Context。目前go-mir支持代码生成的接口显示带有Engine Context,比如Gin的*gin.Context,只需要在接口定义处添加Context原语,一般HTTP Request Context可以从Engine Context中获取。还有一种获取HTTP Request Context的方法就是在请求参数Bind的时候获取,比如在默认的Bind函数中获取HTTP Request Context后赋值给接口的请求参数。以上两种方法都能满足需求,但都是采用了取巧的法子来完成,本提按提供一种直接在生成代码的接口方法中直接带上HTTP Request Context来满足需求。

需求:

  • 使用UseRequestContext Option选项开启添加HTTP Request Context到接口方法的功能;
  • 默认不开启UseRequestContext功能;
  • 如果接口方法开启了 Engine Context的约束(添加了Context原语),优先使用 Engine Context约束;

方案:

  • 为go-mir添加UseRequestContext Option选项开启添加HTTP Request Context到接口方法的功能;

适配进度

  • gin
  • chi
  • echo
  • fiber
  • hertz
  • httprouter
  • iris
  • macaron
  • mux
  • mirc

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.