简化路由处理程序以处理没有框架的参数。

huangapple go评论85阅读模式
英文:

Simplifying route handler to handle parameters without framework

问题

rocket.rs中,我们有这样一个简单的路由代码:

#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

如果你在浏览器中访问http://localhost:8000/hello/John/58,你会看到:

Hello, 58 year old named John!

我阅读了这个,但是被接受的答案是关于如何对单个路由进行Go url参数映射的方法,它可以将http://localhost:8080/blob/123/test读取为/blob/{id}/test并显示所需的路由。

我知道有一些很棒的路由器/框架,但是我想自己构建简单的代码,以更好地理解http路由处理程序。

假设我有以下代码:

type Tender struct {
    tenderReference string
    venderCode      int
}

func (t Tender) readWrite() {
    fmt.Printf("Tender %s is ready for vendor %d to review and submit\n", t.tenderReference, t.venderCode)
}

func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("Tender %s already submitted by vender %d\n", t.tenderReference, t.venderCode)
}

我希望我的路由是这样的:

  1. /api/tender/readWrite/{tenderReference}/vendor/{venderCode} 调用 func (t Tender) readWrite(){}

  2. /api/tender/readOnly/{tenderReference}/vendor/{venderCode} 调用 func (t Tender) readOnly(){}

我需要构建多少个路由处理程序?

英文:

In rocket.rs, we've this simple route code:

#[get(&quot;/hello/&lt;name&gt;/&lt;age&gt;&quot;)]
fn hello(name: &amp;str, age: u8) -&gt; String {
    format!(&quot;Hello, {} year old named {}!&quot;, age, name)
}

where if you were to visit http://localhost:8000/hello/John/58 in the browser, you’d see:
Hello, 58 year old named John!

I read this, but the accepted answer is about a way to do Go url parameters mapping for single route, that could read http://localhost:8080/blob/123/test as /blob/{id}/test and display the required route.

I know there are some great routers/frameworks there, but looking to build simple code myself to understand http route handlers in a better way.

Let's say I've:

type Tender struct {
	tenderReference string
	venderCode      int
}

func (t Tender) readWrite() {
	fmt.Printf(&quot;Tender %s is ready for vendor %d to review and submit\n&quot;, t.tenderReference, t.venderCode)
}

func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
	fmt.Printf(&quot;Tender %s already submitted by vender %d\n&quot;, t.tenderReference, t.venderCode)
}

And want my routes to be something like:

  1. /api/tender/readWrite/{tenderReference}/vendor/{venderCode} that is calling func (t Tender) readWrite(){}

  2. /api/tender/readOnly/{tenderReference}/vendor/{venderCode} that is calling func (t Tender) readOnly(){}

How many route handler do I have to build?

答案1

得分: 0

我按照以下方式解决了问题,欢迎提出其他想法:

  1. 404.go
package main

import (
	"fmt"
	"net/http"
)

func handle404(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "嗯,看起来你在玩耍,页面不可用 :)\n")
}
  1. getField.go
package main

import "net/http"

type ctxKey struct{}

func getField(r *http.Request, index int) string {
	fields := r.Context().Value(ctxKey{}).([]string)
	return fields[index]
}
  1. routes.go
package main

import (
	"net/http"
	"regexp"
)

type route struct {
	method  string
	regex   *regexp.Regexp
	handler http.HandlerFunc
}

var routes = []route{
	newRoute("GET", "/api/tender/(rw|r)/([^/]+)/vendor/([0-9]+)", apiTenders),
}

func newRoute(method, pattern string, handler http.HandlerFunc) route {
	return route{method, regexp.MustCompile("^" + pattern + "$"), handler}
}
  1. tendor.go
package main

import (
	"fmt"
	"net/http"
	"strconv"
)

type Tender struct {
	tenderReference string
	venderCode      int
}

// 处理 GET /api/tender/(rw|r)/([^/]+)/vendor/([0-9]+)
func apiTenders(w http.ResponseWriter, r *http.Request) {
	action := getField(r, 0)
	tenderReference := getField(r, 1)
	venderCode, _ := strconv.Atoi(getField(r, 2))
	tender := Tender{tenderReference, venderCode}
	switch action {
	case "rw":
		tender.readWrite(w, r) // 显示招标并允许供应商提交反馈
	case "r":
		tender.readOnly(w, r) // 显示只读招标副本
	default:
		fmt.Fprintf(w, "招标错误\n")
	}
}

func (t Tender) readWrite(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "招标 %s 已准备好供应商 %d 进行查看和提交\n", t.tenderReference, t.venderCode)
}

func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "招标 %s 已由供应商 %d 提交\n", t.tenderReference, t.venderCode)
}
  1. server.go
package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"
)

type apiHandler struct{}

func main() {
	http.Handle("/api/", apiHandler{})
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		// The "/" pattern matches everything, so we need to check
		// that we're at the root here.
		if req.URL.Path != "/" {
			http.NotFound(w, req)
			return
		}
		fmt.Fprintf(w, "欢迎来到首页!")
	})
	http.ListenAndServe(":8000", nil)
}

func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var allow []string
	for _, route := range routes {
		matches := route.regex.FindStringSubmatch(r.URL.Path)
		if len(matches) > 0 {
			if r.Method != route.method {
				allow = append(allow, route.method)
				continue
			}
			ctx := context.WithValue(r.Context(), ctxKey{}, matches[1:])
			route.handler(w, r.WithContext(ctx))
			return
		}
	}
	if len(allow) > 0 {
		w.Header().Set("Allow", strings.Join(allow, ", "))
		http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
		return
	}
	handle404(w, r)
	//http.NotFound(w, r)
}
英文:

I solved it as below, other thoughts are welcomed:

  1. 404.go
package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
)

func handle404(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, &quot;mmm, it looks you are playing around, page is not available :)\n&quot;)
}
  1. getField.go
package main

import &quot;net/http&quot;

type ctxKey struct{}

func getField(r *http.Request, index int) string {
	fields := r.Context().Value(ctxKey{}).([]string)
	return fields[index]
}
  1. routes.go
package main

import (
	&quot;net/http&quot;
	&quot;regexp&quot;
)

type route struct {
	method  string
	regex   *regexp.Regexp
	handler http.HandlerFunc
}

var routes = []route{
	newRoute(&quot;GET&quot;, &quot;/api/tender/(rw|r)/([^/]+)/vendor/([0-9]+)&quot;, apiTenders),
}

func newRoute(method, pattern string, handler http.HandlerFunc) route {
	return route{method, regexp.MustCompile(&quot;^&quot; + pattern + &quot;$&quot;), handler}
}
  1. tendor.go
package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;strconv&quot;
)

type Tender struct {
	tenderReference string
	venderCode      int
}

// Handles GET /api/tender/(rw|r)/([^/]+)/vendor/([0-9]+)
func apiTenders(w http.ResponseWriter, r *http.Request) {
	action := getField(r, 0)
	tenderReference := getField(r, 1)
	venderCode, _ := strconv.Atoi(getField(r, 2))
	tender := Tender{tenderReference, venderCode}
	switch action {
	case &quot;rw&quot;:
		tender.readWrite(w, r) // Display tender and allow vendor to submit feedback
	case &quot;r&quot;:
		tender.readOnly(w, r) // Display readOnly copy of the tender
	default:
		fmt.Fprintf(w, &quot;Tendert ERROR\n&quot;)
	}
}

func (t Tender) readWrite(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, &quot;Tender %s is ready for vendor %d to review and submit\n&quot;, t.tenderReference, t.venderCode)
}

func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, &quot;Tender %s already submitted by vender %d\n&quot;, t.tenderReference, t.venderCode)
}
  1. server.go
package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;strings&quot;
)

type apiHandler struct{}

func main() {
	http.Handle(&quot;/api/&quot;, apiHandler{})
	http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, req *http.Request) {
		// The &quot;/&quot; pattern matches everything, so we need to check
		// that we&#39;re at the root here.
		if req.URL.Path != &quot;/&quot; {
			http.NotFound(w, req)
			return
		}
		fmt.Fprintf(w, &quot;Welcome to the home page!&quot;)
	})
	http.ListenAndServe(&quot;:8000&quot;, nil)
}

func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var allow []string
	for _, route := range routes {
		matches := route.regex.FindStringSubmatch(r.URL.Path)
		if len(matches) &gt; 0 {
			if r.Method != route.method {
				allow = append(allow, route.method)
				continue
			}
			ctx := context.WithValue(r.Context(), ctxKey{}, matches[1:])
			route.handler(w, r.WithContext(ctx))
			return
		}
	}
	if len(allow) &gt; 0 {
		w.Header().Set(&quot;Allow&quot;, strings.Join(allow, &quot;, &quot;))
		http.Error(w, &quot;405 method not allowed&quot;, http.StatusMethodNotAllowed)
		return
	}
	handle404(w, r)
	//http.NotFound(w, r)
}

huangapple
  • 本文由 发表于 2021年11月3日 02:51:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/69815593.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定