英文:
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)
}
我希望我的路由是这样的:
-
/api/tender/readWrite/{tenderReference}/vendor/{venderCode}
调用func (t Tender) readWrite(){}
-
/api/tender/readOnly/{tenderReference}/vendor/{venderCode}
调用func (t Tender) readOnly(){}
我需要构建多少个路由处理程序?
英文:
In rocket.rs, we've this simple route code:
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
format!("Hello, {} year old named {}!", 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("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)
}
And want my routes to be something like:
-
/api/tender/readWrite/{tenderReference}/vendor/{venderCode}
that is callingfunc (t Tender) readWrite(){}
-
/api/tender/readOnly/{tenderReference}/vendor/{venderCode}
that is callingfunc (t Tender) readOnly(){}
How many route handler do I have to build?
答案1
得分: 0
我按照以下方式解决了问题,欢迎提出其他想法:
404.go
package main
import (
"fmt"
"net/http"
)
func handle404(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "嗯,看起来你在玩耍,页面不可用 :)\n")
}
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]
}
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}
}
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)
}
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:
404.go
package main
import (
"fmt"
"net/http"
)
func handle404(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "mmm, it looks you are playing around, page is not available :)\n")
}
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]
}
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}
}
tendor.go
package main
import (
"fmt"
"net/http"
"strconv"
)
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 "rw":
tender.readWrite(w, r) // Display tender and allow vendor to submit feedback
case "r":
tender.readOnly(w, r) // Display readOnly copy of the tender
default:
fmt.Fprintf(w, "Tendert ERROR\n")
}
}
func (t Tender) readWrite(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "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.Fprintf(w, "Tender %s already submitted by vender %d\n", t.tenderReference, t.venderCode)
}
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, "Welcome to the home page!")
})
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论