28
28
isLeaf bool
29
29
// isHandler indicates that node has at least one handler registered to it
30
30
isHandler bool
31
+
32
+ // notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases
33
+ notFoundHandler HandlerFunc
31
34
}
32
35
kind uint8
33
36
children []* node
@@ -68,6 +71,7 @@ func (m *methodHandler) isHandler() bool {
68
71
m .put != nil ||
69
72
m .trace != nil ||
70
73
m .report != nil
74
+ // RouteNotFound/404 is not considered as a handler
71
75
}
72
76
73
77
func (m * methodHandler ) updateAllowHeader () {
@@ -369,6 +373,9 @@ func (n *node) addHandler(method string, h HandlerFunc) {
369
373
n .methodHandler .trace = h
370
374
case REPORT :
371
375
n .methodHandler .report = h
376
+ case RouteNotFound :
377
+ n .notFoundHandler = h
378
+ return // RouteNotFound/404 is not considered as a handler so no further logic needs to be executed
372
379
}
373
380
374
381
n .methodHandler .updateAllowHeader ()
@@ -403,7 +410,7 @@ func (n *node) findHandler(method string) HandlerFunc {
403
410
return n .methodHandler .trace
404
411
case REPORT :
405
412
return n .methodHandler .report
406
- default :
413
+ default : // RouteNotFound/404 is not considered as a handler
407
414
return nil
408
415
}
409
416
}
@@ -506,7 +513,7 @@ func (r *Router) Find(method, path string, c Context) {
506
513
// No matching prefix, let's backtrack to the first possible alternative node of the decision path
507
514
nk , ok := backtrackToNextNodeKind (staticKind )
508
515
if ! ok {
509
- return // No other possibilities on the decision path
516
+ return // No other possibilities on the decision path, handler will be whatever context is reset to.
510
517
} else if nk == paramKind {
511
518
goto Param
512
519
// NOTE: this case (backtracking from static node to previous any node) can not happen by current any matching logic. Any node is end of search currently
@@ -522,15 +529,21 @@ func (r *Router) Find(method, path string, c Context) {
522
529
search = search [lcpLen :]
523
530
searchIndex = searchIndex + lcpLen
524
531
525
- // Finish routing if no remaining search and we are on a node with handler and matching method type
526
- if search == "" && currentNode .isHandler {
527
- // check if current node has handler registered for http method we are looking for. we store currentNode as
528
- // best matching in case we do no find no more routes matching this path+method
529
- if previousBestMatchNode == nil {
530
- previousBestMatchNode = currentNode
531
- }
532
- if h := currentNode .findHandler (method ); h != nil {
533
- matchedHandler = h
532
+ // Finish routing if is no request path remaining to search
533
+ if search == "" {
534
+ // in case of node that is handler we have exact method type match or something for 405 to use
535
+ if currentNode .isHandler {
536
+ // check if current node has handler registered for http method we are looking for. we store currentNode as
537
+ // best matching in case we do no find no more routes matching this path+method
538
+ if previousBestMatchNode == nil {
539
+ previousBestMatchNode = currentNode
540
+ }
541
+ if h := currentNode .findHandler (method ); h != nil {
542
+ matchedHandler = h
543
+ break
544
+ }
545
+ } else if currentNode .notFoundHandler != nil {
546
+ matchedHandler = currentNode .notFoundHandler
534
547
break
535
548
}
536
549
}
@@ -550,7 +563,8 @@ func (r *Router) Find(method, path string, c Context) {
550
563
i := 0
551
564
l := len (search )
552
565
if currentNode .isLeaf {
553
- // when param node does not have any children then param node should act similarly to any node - consider all remaining search as match
566
+ // when param node does not have any children (path param is last piece of route path) then param node should
567
+ // act similarly to any node - consider all remaining search as match
554
568
i = l
555
569
} else {
556
570
for ; i < l && search [i ] != '/' ; i ++ {
@@ -575,13 +589,16 @@ func (r *Router) Find(method, path string, c Context) {
575
589
searchIndex += + len (search )
576
590
search = ""
577
591
578
- // check if current node has handler registered for http method we are looking for. we store currentNode as
579
- // best matching in case we do no find no more routes matching this path+method
592
+ if h := currentNode .findHandler (method ); h != nil {
593
+ matchedHandler = h
594
+ break
595
+ }
596
+ // we store currentNode as best matching in case we do not find more routes matching this path+method. Needed for 405
580
597
if previousBestMatchNode == nil {
581
598
previousBestMatchNode = currentNode
582
599
}
583
- if h := currentNode .findHandler ( method ); h != nil {
584
- matchedHandler = h
600
+ if currentNode .notFoundHandler != nil {
601
+ matchedHandler = currentNode . notFoundHandler
585
602
break
586
603
}
587
604
}
@@ -604,6 +621,8 @@ func (r *Router) Find(method, path string, c Context) {
604
621
return // nothing matched at all
605
622
}
606
623
624
+ // matchedHandler could be method+path handler that we matched or notFoundHandler from node with matching path
625
+ // user provided not found (404) handler has priority over generic method not found (405) handler or global 404 handler
607
626
if matchedHandler != nil {
608
627
ctx .handler = matchedHandler
609
628
} else {
@@ -612,7 +631,9 @@ func (r *Router) Find(method, path string, c Context) {
612
631
currentNode = previousBestMatchNode
613
632
614
633
ctx .handler = NotFoundHandler
615
- if currentNode .isHandler {
634
+ if currentNode .notFoundHandler != nil {
635
+ ctx .handler = currentNode .notFoundHandler
636
+ } else if currentNode .isHandler {
616
637
ctx .Set (ContextKeyHeaderAllow , currentNode .methodHandler .allowHeader )
617
638
ctx .handler = MethodNotAllowedHandler
618
639
if method == http .MethodOptions {
0 commit comments