Coder Social home page Coder Social logo

cloudgo-io's Introduction

简单web 应用与服务——Golang

本次选择使用较轻量级组件negroni + gorilla/mux实现简单web应用,该应用实现以下功能:

  • 支持静态文件服务
  • 支持简单 js 访问
  • 提交表单,并输出一个表格
  • /unknown 给出开发中的提示,返回码 5xx

前言

实现简单web应用于服务的基础是golang官方提供的http包,可以通过Golang web 应用开发进行学习。

框架选择

项目结构

  • controller
  • public
    • css
    • img
    • js
    • template
  • router

controller用于管理handler

public为静态文件托管的目录

router为路由控制

功能实现

接下来介绍功能的实现

支持静态文件服务

支持静态文件服务功能的方法:

  • 通过negroni内置中间件实现
package main

import (
  "fmt"
  "net/http"

  "github.com/urfave/negroni"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Welcome to the home page!")
  })

  // http.FileServer的使用例子, 若你预期要"像伺服器"而非"中间件"的行为
  // mux.Handle("/public", http.FileServer(http.Dir("/home/public")))

  n := negroni.New()
  n.Use(negroni.NewStatic(http.Dir("/public")))
  n.UseHandler(mux)

  http.ListenAndServe(":3002", n)
}
  • 通过mux路由router的Handler
r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir(dir))))

实现了静态文件服务之后,可以通过localhost:port/public/访问。

支持简单js访问

访问首页时首先使用ajax向服务发送一个路径为/time的请求,并且将返回的数据显示到页面中。

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link href="../css/index.css" rel="stylesheet" type="text/css" />
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="../js/hello.js"></script>
    <title>CloudIO-GOLANG</title>
</head>
<body>
    <div>
        <img id="image" src="../img/background.jpg">
    </div>
    <div id="title">Welcome to CookiesChen's MiaoMiao House</div>
    <p class="time">Now time is {{.Time}}</p>
    <form action="/form" method="post">
        <div id="username">
            username <input type="text" name="username">
        </div>
        <div id="password">
            password <input type="password" name="password">
        </div>
        <div id="login">
            <input type="submit" value="登录">
        </div>
    </form>
</body>
</html>
/* hello.js */
$(document).ready(function() {
    $.ajax({
        url: "/time"
    }).then(function(data) {
        $('.time').append(data.time);
    });
});

在服务器端,首先注册路由

R.HandleFunc("/time", controller.TimeHandler).Methods("GET")

然后是Handler的实现,是处理请求的时候,获取当前系统的时间,然后使用render将JSON写入到ResponseWriter

func TimeHandler(w http.ResponseWriter, r *http.Request)  {
	formatter := render.New(render.Options{
		IndentJSON: true,
	})
	formatter.JSON(w, http.StatusOK, struct {
		Time string `json:"time"`
	}{Time: time.Now().String()})
}

处理表单输入

表单提交如下

<form action="/form" method="post">
    <div id="username">
        username <input type="text" name="username">
    </div>
    <div id="password">
        password <input type="password" name="password">
    </div>
    <div id="login">
        <input type="submit" value="登录">
    </div>
</form>
if r.Method == "POST" {
    r.ParseForm()
    fmt.Println("username:", r.Form["username"])
    fmt.Println("password:", r.Form["password"])
    t, _ := template.ParseFiles("./public/template/form.html")
    log.Println(t.Execute(w, struct {
        Name      string `json:"name"`
        Password string `json:"password"`
    }{Name: r.Form["username"][0], Password: r.Form["password"][0]}))
}

处理表单的时候,需要显式解析表单r.ParseForm(),默认不会解析,解析之后通过键值可以获取到对应的slice。最后使用模板进行输出即可。

对于未注册路径

在handler中简单的输出错误即可。

http.Error(w, "no such directory", 500)

Negroni中间件源码阅读

本质上来说Negroni是一个HTTP Handler,因为他实现了HTTP Handler接口,所以他可以被http.ListenAndServe使用,其次Negroni本身内部又有一套自己的Handler处理链,通过他们可以达到处理http请求的目的,这些Handler处理链中的处理器,就是一个个中间件。

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

再来看看Negroni的Handler,可以看到,与http.Handler唯一不同的就是多了一个next,而这就是中间件实现的基础。

type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

构建中间件处理链,Negroni有一个handlerslice,通过调用Use方法,可以将handler依次保存在该slice中。

type Negroni struct {
	middleware middleware
	handlers   []Handler
}

func (n *Negroni) Use(handler Handler) {
	if handler == nil {
		panic("handler cannot be nil")
	}

	n.handlers = append(n.handlers, handler)
	n.middleware = build(n.handlers)
}

当handler的数量大于1时, 通过递归构造,这样先添加的handler会排在前面执行。当等于0时,返回空的中间件。当等于1时,没有next,将next设置为空的中间件即可。

type middleware struct {
	handler Handler
	next    *middleware
}

func build(handlers []Handler) middleware {
	var next middleware

	if len(handlers) == 0 {
		return voidMiddleware()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddleware()
	}

	return middleware{handlers[0], &next}
}

func voidMiddleware() middleware {
	return middleware{
		HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middleware{},
	}
}

编写自己的中间件

package main

import (
	"fmt"
	"github.com/CookiesChen/cloudgo-io/router"
	"github.com/urfave/negroni"
	"net/http"
)

const port = "9090"

func main() {
	r := router.R

	n := negroni.Classic()

	n.UseFunc(sayhi)
	n.UseFunc(sayhello)

	n.UseHandler(r)
	n.Run(": " + port)
}

func sayhi(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc){
	fmt.Println("Hi")
	next(rw,r)
}

func sayhello(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc){
	fmt.Println("Hello")
	next(rw,r)
}

以上每次访问服务器时,都会链式执行中间件,在本次实验中并没有进行有效的中间件的编写,在实际应用时,可以通过中间件起到filter的作用,用于白名单和用户身份验证等方面,具体实现还需要根据应用要求。

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.