code.gitea.io/gitea@v1.22.3/modules/web/route.go (about)

     1  // Copyright 2020 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package web
     5  
     6  import (
     7  	"net/http"
     8  	"strings"
     9  
    10  	"code.gitea.io/gitea/modules/web/middleware"
    11  
    12  	"gitea.com/go-chi/binding"
    13  	"github.com/go-chi/chi/v5"
    14  )
    15  
    16  // Bind binding an obj to a handler's context data
    17  func Bind[T any](_ T) http.HandlerFunc {
    18  	return func(resp http.ResponseWriter, req *http.Request) {
    19  		theObj := new(T) // create a new form obj for every request but not use obj directly
    20  		data := middleware.GetContextData(req.Context())
    21  		binding.Bind(req, theObj)
    22  		SetForm(data, theObj)
    23  		middleware.AssignForm(theObj, data)
    24  	}
    25  }
    26  
    27  // SetForm set the form object
    28  func SetForm(dataStore middleware.ContextDataStore, obj any) {
    29  	dataStore.GetData()["__form"] = obj
    30  }
    31  
    32  // GetForm returns the validate form information
    33  func GetForm(dataStore middleware.ContextDataStore) any {
    34  	return dataStore.GetData()["__form"]
    35  }
    36  
    37  // Route defines a route based on chi's router
    38  type Route struct {
    39  	R              chi.Router
    40  	curGroupPrefix string
    41  	curMiddlewares []any
    42  }
    43  
    44  // NewRoute creates a new route
    45  func NewRoute() *Route {
    46  	r := chi.NewRouter()
    47  	return &Route{R: r}
    48  }
    49  
    50  // Use supports two middlewares
    51  func (r *Route) Use(middlewares ...any) {
    52  	for _, m := range middlewares {
    53  		if m != nil {
    54  			r.R.Use(toHandlerProvider(m))
    55  		}
    56  	}
    57  }
    58  
    59  // Group mounts a sub-Router along a `pattern` string.
    60  func (r *Route) Group(pattern string, fn func(), middlewares ...any) {
    61  	previousGroupPrefix := r.curGroupPrefix
    62  	previousMiddlewares := r.curMiddlewares
    63  	r.curGroupPrefix += pattern
    64  	r.curMiddlewares = append(r.curMiddlewares, middlewares...)
    65  
    66  	fn()
    67  
    68  	r.curGroupPrefix = previousGroupPrefix
    69  	r.curMiddlewares = previousMiddlewares
    70  }
    71  
    72  func (r *Route) getPattern(pattern string) string {
    73  	newPattern := r.curGroupPrefix + pattern
    74  	if !strings.HasPrefix(newPattern, "/") {
    75  		newPattern = "/" + newPattern
    76  	}
    77  	if newPattern == "/" {
    78  		return newPattern
    79  	}
    80  	return strings.TrimSuffix(newPattern, "/")
    81  }
    82  
    83  func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Handler, http.HandlerFunc) {
    84  	handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h)+1)
    85  	for _, m := range r.curMiddlewares {
    86  		if m != nil {
    87  			handlerProviders = append(handlerProviders, toHandlerProvider(m))
    88  		}
    89  	}
    90  	for _, m := range h {
    91  		if h != nil {
    92  			handlerProviders = append(handlerProviders, toHandlerProvider(m))
    93  		}
    94  	}
    95  	middlewares := handlerProviders[:len(handlerProviders)-1]
    96  	handlerFunc := handlerProviders[len(handlerProviders)-1](nil).ServeHTTP
    97  	mockPoint := RouteMockPoint(MockAfterMiddlewares)
    98  	if mockPoint != nil {
    99  		middlewares = append(middlewares, mockPoint)
   100  	}
   101  	return middlewares, handlerFunc
   102  }
   103  
   104  // Methods adds the same handlers for multiple http "methods" (separated by ",").
   105  // If any method is invalid, the lower level router will panic.
   106  func (r *Route) Methods(methods, pattern string, h ...any) {
   107  	middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h)
   108  	fullPattern := r.getPattern(pattern)
   109  	if strings.Contains(methods, ",") {
   110  		methods := strings.Split(methods, ",")
   111  		for _, method := range methods {
   112  			r.R.With(middlewares...).Method(strings.TrimSpace(method), fullPattern, handlerFunc)
   113  		}
   114  	} else {
   115  		r.R.With(middlewares...).Method(methods, fullPattern, handlerFunc)
   116  	}
   117  }
   118  
   119  // Mount attaches another Route along ./pattern/*
   120  func (r *Route) Mount(pattern string, subR *Route) {
   121  	subR.Use(r.curMiddlewares...)
   122  	r.R.Mount(r.getPattern(pattern), subR.R)
   123  }
   124  
   125  // Any delegate requests for all methods
   126  func (r *Route) Any(pattern string, h ...any) {
   127  	middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h)
   128  	r.R.With(middlewares...).HandleFunc(r.getPattern(pattern), handlerFunc)
   129  }
   130  
   131  // Delete delegate delete method
   132  func (r *Route) Delete(pattern string, h ...any) {
   133  	r.Methods("DELETE", pattern, h...)
   134  }
   135  
   136  // Get delegate get method
   137  func (r *Route) Get(pattern string, h ...any) {
   138  	r.Methods("GET", pattern, h...)
   139  }
   140  
   141  // Head delegate head method
   142  func (r *Route) Head(pattern string, h ...any) {
   143  	r.Methods("HEAD", pattern, h...)
   144  }
   145  
   146  // Post delegate post method
   147  func (r *Route) Post(pattern string, h ...any) {
   148  	r.Methods("POST", pattern, h...)
   149  }
   150  
   151  // Put delegate put method
   152  func (r *Route) Put(pattern string, h ...any) {
   153  	r.Methods("PUT", pattern, h...)
   154  }
   155  
   156  // Patch delegate patch method
   157  func (r *Route) Patch(pattern string, h ...any) {
   158  	r.Methods("PATCH", pattern, h...)
   159  }
   160  
   161  // ServeHTTP implements http.Handler
   162  func (r *Route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   163  	r.R.ServeHTTP(w, req)
   164  }
   165  
   166  // NotFound defines a handler to respond whenever a route could not be found.
   167  func (r *Route) NotFound(h http.HandlerFunc) {
   168  	r.R.NotFound(h)
   169  }
   170  
   171  // Combo delegates requests to Combo
   172  func (r *Route) Combo(pattern string, h ...any) *Combo {
   173  	return &Combo{r, pattern, h}
   174  }
   175  
   176  // Combo represents a tiny group routes with same pattern
   177  type Combo struct {
   178  	r       *Route
   179  	pattern string
   180  	h       []any
   181  }
   182  
   183  // Get delegates Get method
   184  func (c *Combo) Get(h ...any) *Combo {
   185  	c.r.Get(c.pattern, append(c.h, h...)...)
   186  	return c
   187  }
   188  
   189  // Post delegates Post method
   190  func (c *Combo) Post(h ...any) *Combo {
   191  	c.r.Post(c.pattern, append(c.h, h...)...)
   192  	return c
   193  }
   194  
   195  // Delete delegates Delete method
   196  func (c *Combo) Delete(h ...any) *Combo {
   197  	c.r.Delete(c.pattern, append(c.h, h...)...)
   198  	return c
   199  }
   200  
   201  // Put delegates Put method
   202  func (c *Combo) Put(h ...any) *Combo {
   203  	c.r.Put(c.pattern, append(c.h, h...)...)
   204  	return c
   205  }
   206  
   207  // Patch delegates Patch method
   208  func (c *Combo) Patch(h ...any) *Combo {
   209  	c.r.Patch(c.pattern, append(c.h, h...)...)
   210  	return c
   211  }