vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

View File

@ -0,0 +1 @@
ignore

View File

@ -0,0 +1 @@
ignore

View File

@ -0,0 +1,20 @@
#
# Include your application ID here
#
application: <your_app_id>
version: 1
runtime: go
api_version: go1
handlers:
#
# Regex for all swagger files to make as static content.
# You should create the folder static/swagger and copy
# swagger-ui into it.
#
- url: /apidocs/(.*?)/(.*\.(js|html|css))
static_files: static/swagger/\1/\2
upload: static/swagger/(.*?)/(.*\.(js|html|css))
- url: /.*
script: _go_app

View File

@ -0,0 +1 @@
ignore

View File

@ -0,0 +1,18 @@
application: <your_app_id>
version: 1
runtime: go
api_version: go1
handlers:
# Regex for all swagger files to make as static content.
# You should create the folder static/swagger and copy
# swagger-ui into it.
#
- url: /apidocs/(.*?)/(.*\.(js|html|css))
static_files: static/swagger/\1/\2
upload: static/swagger/(.*?)/(.*\.(js|html|css))
# Catch all.
- url: /.*
script: _go_app
login: required

View File

@ -0,0 +1,267 @@
package main
import (
"net/http"
"time"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-swagger12"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/user"
)
// This example demonstrates a reasonably complete suite of RESTful operations backed
// by DataStore on Google App Engine.
// Our simple example struct.
type Profile struct {
LastModified time.Time `json:"-" xml:"-"`
Email string `json:"-" xml:"-"`
FirstName string `json:"first_name" xml:"first-name"`
NickName string `json:"nick_name" xml:"nick-name"`
LastName string `json:"last_name" xml:"last-name"`
}
type ProfileApi struct {
Path string
}
func gaeUrl() string {
if appengine.IsDevAppServer() {
return "http://localhost:8080"
} else {
// Include your URL on App Engine here.
// I found no way to get AppID without appengine.Context and this always
// based on a http.Request.
return "http://federatedservices.appspot.com"
}
}
func init() {
u := ProfileApi{Path: "/profiles"}
u.register()
// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
// Open <your_app_id>.appspot.com/apidocs and enter
// Place the Swagger UI files into a folder called static/swagger if you wish to use Swagger
// http://<your_app_id>.appspot.com/apidocs.json in the api input field.
// For testing, you can use http://localhost:8080/apidocs.json
config := swagger.Config{
// You control what services are visible
WebServices: restful.RegisteredWebServices(),
WebServicesUrl: gaeUrl(),
ApiPath: "/apidocs.json",
// Optionally, specify where the UI is located
SwaggerPath: "/apidocs/",
// GAE support static content which is configured in your app.yaml.
// This example expect the swagger-ui in static/swagger so you should place it there :)
SwaggerFilePath: "static/swagger"}
swagger.InstallSwaggerService(config)
}
func (u ProfileApi) register() {
ws := new(restful.WebService)
ws.
Path(u.Path).
// You can specify consumes and produces per route as well.
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON, restful.MIME_XML)
ws.Route(ws.POST("").To(u.insert).
// Swagger documentation.
Doc("insert a new profile").
Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
Reads(Profile{}))
ws.Route(ws.GET("/{profile-id}").To(u.read).
// Swagger documentation.
Doc("read a profile").
Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
Writes(Profile{}))
ws.Route(ws.PUT("/{profile-id}").To(u.update).
// Swagger documentation.
Doc("update an existing profile").
Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")).
Param(ws.BodyParameter("Profile", "representation of a profile").DataType("main.Profile")).
Reads(Profile{}))
ws.Route(ws.DELETE("/{profile-id}").To(u.remove).
// Swagger documentation.
Doc("remove a profile").
Param(ws.PathParameter("profile-id", "identifier for a profile").DataType("string")))
restful.Add(ws)
}
// POST http://localhost:8080/profiles
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) insert(r *restful.Request, w *restful.Response) {
c := appengine.NewContext(r.Request)
// Marshall the entity from the request into a struct.
p := new(Profile)
err := r.ReadEntity(&p)
if err != nil {
w.WriteError(http.StatusNotAcceptable, err)
return
}
// Ensure we start with a sensible value for this field.
p.LastModified = time.Now()
// The profile belongs to this user.
p.Email = user.Current(c).String()
k, err := datastore.Put(c, datastore.NewIncompleteKey(c, "profiles", nil), p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Let them know the location of the newly created resource.
// TODO: Use a safe Url path append function.
w.AddHeader("Location", u.Path+"/"+k.Encode())
// Return the resultant entity.
w.WriteHeader(http.StatusCreated)
w.WriteEntity(p)
}
// GET http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u ProfileApi) read(r *restful.Request, w *restful.Response) {
c := appengine.NewContext(r.Request)
// Decode the request parameter to determine the key for the entity.
k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Retrieve the entity from the datastore.
p := Profile{}
if err := datastore.Get(c, k, &p); err != nil {
if err.Error() == "datastore: no such entity" {
http.Error(w, err.Error(), http.StatusNotFound)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
// Check we own the profile before allowing them to view it.
// Optionally, return a 404 instead to help prevent guessing ids.
// TODO: Allow admins access.
if p.Email != user.Current(c).String() {
http.Error(w, "You do not have access to this resource", http.StatusForbidden)
return
}
w.WriteEntity(p)
}
// PUT http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
// {"first_name": "Ivan", "nick_name": "Socks", "last_name": "Hawkes"}
//
func (u *ProfileApi) update(r *restful.Request, w *restful.Response) {
c := appengine.NewContext(r.Request)
// Decode the request parameter to determine the key for the entity.
k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Marshall the entity from the request into a struct.
p := new(Profile)
err = r.ReadEntity(&p)
if err != nil {
w.WriteError(http.StatusNotAcceptable, err)
return
}
// Retrieve the old entity from the datastore.
old := Profile{}
if err := datastore.Get(c, k, &old); err != nil {
if err.Error() == "datastore: no such entity" {
http.Error(w, err.Error(), http.StatusNotFound)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
// Check we own the profile before allowing them to update it.
// Optionally, return a 404 instead to help prevent guessing ids.
// TODO: Allow admins access.
if old.Email != user.Current(c).String() {
http.Error(w, "You do not have access to this resource", http.StatusForbidden)
return
}
// Since the whole entity is re-written, we need to assign any invariant fields again
// e.g. the owner of the entity.
p.Email = user.Current(c).String()
// Keep track of the last modification date.
p.LastModified = time.Now()
// Attempt to overwrite the old entity.
_, err = datastore.Put(c, k, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Let them know it succeeded.
w.WriteHeader(http.StatusNoContent)
}
// DELETE http://localhost:8080/profiles/ahdkZXZ-ZmVkZXJhdGlvbi1zZXJ2aWNlc3IVCxIIcHJvZmlsZXMYgICAgICAgAoM
//
func (u *ProfileApi) remove(r *restful.Request, w *restful.Response) {
c := appengine.NewContext(r.Request)
// Decode the request parameter to determine the key for the entity.
k, err := datastore.DecodeKey(r.PathParameter("profile-id"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Retrieve the old entity from the datastore.
old := Profile{}
if err := datastore.Get(c, k, &old); err != nil {
if err.Error() == "datastore: no such entity" {
http.Error(w, err.Error(), http.StatusNotFound)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
// Check we own the profile before allowing them to delete it.
// Optionally, return a 404 instead to help prevent guessing ids.
// TODO: Allow admins access.
if old.Email != user.Current(c).String() {
http.Error(w, "You do not have access to this resource", http.StatusForbidden)
return
}
// Delete the entity.
if err := datastore.Delete(c, k); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// Success notification.
w.WriteHeader(http.StatusNoContent)
}

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/mjibson/appstats"
)
func stats(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
c := appstats.NewContext(req.Request)
chain.ProcessFilter(req, resp)
c.Stats.Status = resp.StatusCode()
c.Save()
}

View File

@ -0,0 +1,162 @@
package main
import (
"net/http"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-swagger12"
"google.golang.org/appengine"
"google.golang.org/appengine/memcache"
)
// This example is functionally the same as ../restful-user-service.go
// but it`s supposed to run on Goole App Engine (GAE)
//
// contributed by ivanhawkes
type User struct {
Id, Name string
}
type UserService struct {
// normally one would use DAO (data access object)
// but in this example we simple use memcache.
}
func (u UserService) Register() {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
ws.Route(ws.GET("/{user-id}").To(u.findUser).
// docs
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
Writes(User{})) // on the response
ws.Route(ws.PATCH("").To(u.updateUser).
// docs
Doc("update a user").
Reads(User{})) // from the request
ws.Route(ws.PUT("/{user-id}").To(u.createUser).
// docs
Doc("create a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
Reads(User{})) // from the request
ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
// docs
Doc("delete a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))
restful.Add(ws)
}
// GET http://localhost:8080/users/1
//
func (u UserService) findUser(request *restful.Request, response *restful.Response) {
c := appengine.NewContext(request.Request)
id := request.PathParameter("user-id")
usr := new(User)
_, err := memcache.Gob.Get(c, id, &usr)
if err != nil || len(usr.Id) == 0 {
response.WriteErrorString(http.StatusNotFound, "User could not be found.")
} else {
response.WriteEntity(usr)
}
}
// PATCH http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserService) updateUser(request *restful.Request, response *restful.Response) {
c := appengine.NewContext(request.Request)
usr := new(User)
err := request.ReadEntity(&usr)
if err == nil {
item := &memcache.Item{
Key: usr.Id,
Object: &usr,
}
err = memcache.Gob.Set(c, item)
if err != nil {
response.WriteError(http.StatusInternalServerError, err)
return
}
response.WriteEntity(usr)
} else {
response.WriteError(http.StatusInternalServerError, err)
}
}
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserService) createUser(request *restful.Request, response *restful.Response) {
c := appengine.NewContext(request.Request)
usr := User{Id: request.PathParameter("user-id")}
err := request.ReadEntity(&usr)
if err == nil {
item := &memcache.Item{
Key: usr.Id,
Object: &usr,
}
err = memcache.Gob.Add(c, item)
if err != nil {
response.WriteError(http.StatusInternalServerError, err)
return
}
response.WriteHeader(http.StatusCreated)
response.WriteEntity(usr)
} else {
response.WriteError(http.StatusInternalServerError, err)
}
}
// DELETE http://localhost:8080/users/1
//
func (u *UserService) removeUser(request *restful.Request, response *restful.Response) {
c := appengine.NewContext(request.Request)
id := request.PathParameter("user-id")
err := memcache.Delete(c, id)
if err != nil {
response.WriteError(http.StatusInternalServerError, err)
}
}
func getGaeURL() string {
if appengine.IsDevAppServer() {
return "http://localhost:8080"
} else {
/**
* Include your URL on App Engine here.
* I found no way to get AppID without appengine.Context and this always
* based on a http.Request.
*/
return "http://<your_app_id>.appspot.com"
}
}
func init() {
u := UserService{}
u.Register()
// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
// Open <your_app_id>.appspot.com/apidocs and enter http://<your_app_id>.appspot.com/apidocs.json in the api input field.
config := swagger.Config{
WebServices: restful.RegisteredWebServices(), // you control what services are visible
WebServicesUrl: getGaeURL(),
ApiPath: "/apidocs.json",
// Optionally, specify where the UI is located
SwaggerPath: "/apidocs/",
// GAE support static content which is configured in your app.yaml.
// This example expect the swagger-ui in static/swagger so you should place it there :)
SwaggerFilePath: "static/swagger"}
swagger.InstallSwaggerService(config)
}

View File

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<h1>{{.Text}}</h1>
</body>
</html>

View File

@ -0,0 +1,34 @@
package restPack
import (
restful "github.com/emicklei/go-restful"
"gopkg.in/vmihailenco/msgpack.v2"
)
const MIME_MSGPACK = "application/x-msgpack" // Accept or Content-Type used in Consumes() and/or Produces()
// NewEntityAccessorMPack returns a new EntityReaderWriter for accessing MessagePack content.
// This package is not initialized with such an accessor using the MIME_MSGPACK contentType.
func NewEntityAccessorMsgPack() restful.EntityReaderWriter {
return entityMsgPackAccess{}
}
// entityOctetAccess is a EntityReaderWriter for Octet encoding
type entityMsgPackAccess struct {
}
// Read unmarshalls the value from byte slice and using msgpack to unmarshal
func (e entityMsgPackAccess) Read(req *restful.Request, v interface{}) error {
return msgpack.NewDecoder(req.Request.Body).Decode(v)
}
// Write marshals the value to byte slice and set the Content-Type Header.
func (e entityMsgPackAccess) Write(resp *restful.Response, status int, v interface{}) error {
if v == nil {
resp.WriteHeader(status)
// do not write a nil representation
return nil
}
resp.WriteHeader(status)
return msgpack.NewEncoder(resp).Encode(v)
}

View File

@ -0,0 +1,160 @@
package restPack
import (
"bytes"
"errors"
"log"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"io/ioutil"
restful "github.com/emicklei/go-restful"
)
func TestMsgPack(t *testing.T) {
// register msg pack entity
restful.RegisterEntityAccessor(MIME_MSGPACK, NewEntityAccessorMsgPack())
type Tool struct {
Name string
Vendor string
}
// Write
httpWriter := httptest.NewRecorder()
mpack := &Tool{Name: "json", Vendor: "apple"}
resp := restful.NewResponse(httpWriter)
resp.SetRequestAccepts("application/x-msgpack,*/*;q=0.8")
err := resp.WriteEntity(mpack)
if err != nil {
t.Errorf("err %v", err)
}
// Read
bodyReader := bytes.NewReader(httpWriter.Body.Bytes())
httpRequest, _ := http.NewRequest("GET", "/test", bodyReader)
httpRequest.Header.Set("Content-Type", MIME_MSGPACK)
request := restful.NewRequest(httpRequest)
readMsgPack := new(Tool)
err = request.ReadEntity(&readMsgPack)
if err != nil {
t.Errorf("err %v", err)
}
if equal := reflect.DeepEqual(mpack, readMsgPack); !equal {
t.Fatalf("should not be error")
}
}
func TestWithWebService(t *testing.T) {
serverURL := "http://127.0.0.1:8090"
go func() {
runRestfulMsgPackRouterServer()
}()
if err := waitForServerUp(serverURL); err != nil {
t.Errorf("%v", err)
}
// send a post request
userData := user{Id: "0001", Name: "Tony"}
msgPackData, err := msgpack.Marshal(userData)
req, err := http.NewRequest("POST", serverURL+"/test/msgpack", bytes.NewBuffer(msgPackData))
req.Header.Set("Content-Type", MIME_MSGPACK)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Errorf("unexpected error in sending req: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("unexpected response: %v, expected: %v", resp.StatusCode, http.StatusOK)
}
ur := &userResponse{}
expectMsgPackDocument(t, resp, ur)
if ur.Status != statusActive {
t.Fatalf("should not error")
}
log.Printf("user response:%v", ur)
}
func expectMsgPackDocument(t *testing.T, r *http.Response, doc interface{}) {
data, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
t.Errorf("ExpectMsgPackDocument: unable to read response body :%v", err)
return
}
// put the body back for re-reads
r.Body = ioutil.NopCloser(bytes.NewReader(data))
err = msgpack.Unmarshal(data, doc)
if err != nil {
t.Errorf("ExpectMsgPackDocument: unable to unmarshal MsgPack:%v", err)
}
}
func runRestfulMsgPackRouterServer() {
container := restful.NewContainer()
register(container)
log.Print("start listening on localhost:8090")
server := &http.Server{Addr: ":8090", Handler: container}
log.Fatal(server.ListenAndServe())
}
func waitForServerUp(serverURL string) error {
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
_, err := http.Get(serverURL + "/")
if err == nil {
return nil
}
}
return errors.New("waiting for server timed out")
}
var (
statusActive = "active"
)
type user struct {
Id, Name string
}
type userResponse struct {
Status string
}
func register(container *restful.Container) {
restful.RegisterEntityAccessor(MIME_MSGPACK, NewEntityAccessorMsgPack())
ws := new(restful.WebService)
ws.
Path("/test").
Consumes(restful.MIME_JSON, MIME_MSGPACK).
Produces(restful.MIME_JSON, MIME_MSGPACK)
// route user api
ws.Route(ws.POST("/msgpack").
To(do).
Reads(user{}).
Writes(userResponse{}))
container.Add(ws)
}
func do(request *restful.Request, response *restful.Response) {
u := &user{}
err := request.ReadEntity(u)
if err != nil {
log.Printf("should be no error, got:%v", err)
}
log.Printf("got:%v", u)
ur := &userResponse{Status: statusActive}
response.SetRequestAccepts(MIME_MSGPACK)
response.WriteEntity(ur)
}

View File

@ -0,0 +1,68 @@
package main
import (
"io"
"log"
"net/http"
"github.com/emicklei/go-restful"
)
// Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page
// to make XMLHttpRequests to another domain, not the domain the JavaScript originated from.
//
// http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
// http://enable-cors.org/server.html
//
// GET http://localhost:8080/users
//
// GET http://localhost:8080/users/1
//
// PUT http://localhost:8080/users/1
//
// DELETE http://localhost:8080/users/1
//
// OPTIONS http://localhost:8080/users/1 with Header "Origin" set to some domain and
type UserResource struct{}
func (u UserResource) RegisterTo(container *restful.Container) {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes("*/*").
Produces("*/*")
ws.Route(ws.GET("/{user-id}").To(u.nop))
ws.Route(ws.POST("").To(u.nop))
ws.Route(ws.PUT("/{user-id}").To(u.nop))
ws.Route(ws.DELETE("/{user-id}").To(u.nop))
container.Add(ws)
}
func (u UserResource) nop(request *restful.Request, response *restful.Response) {
io.WriteString(response.ResponseWriter, "this would be a normal response")
}
func main() {
wsContainer := restful.NewContainer()
u := UserResource{}
u.RegisterTo(wsContainer)
// Add container filter to enable CORS
cors := restful.CrossOriginResourceSharing{
ExposeHeaders: []string{"X-My-Header"},
AllowedHeaders: []string{"Content-Type", "Accept"},
AllowedMethods: []string{"GET", "POST"},
CookiesAllowed: false,
Container: wsContainer}
wsContainer.Filter(cors.Filter)
// Add container filter to respond to OPTIONS
wsContainer.Filter(wsContainer.OPTIONSFilter)
log.Print("start listening on localhost:8080")
server := &http.Server{Addr: ":8080", Handler: wsContainer}
log.Fatal(server.ListenAndServe())
}

View File

@ -0,0 +1,54 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
// This example shows how to create a filter that produces log lines
// according to the Common Log Format, also known as the NCSA standard.
//
// kindly contributed by leehambley
//
// GET http://localhost:8080/ping
var logger *log.Logger = log.New(os.Stdout, "", 0)
func NCSACommonLogFormatLogger() restful.FilterFunction {
return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
var username = "-"
if req.Request.URL.User != nil {
if name := req.Request.URL.User.Username(); name != "" {
username = name
}
}
chain.ProcessFilter(req, resp)
logger.Printf("%s - %s [%s] \"%s %s %s\" %d %d",
strings.Split(req.Request.RemoteAddr, ":")[0],
username,
time.Now().Format("02/Jan/2006:15:04:05 -0700"),
req.Request.Method,
req.Request.URL.RequestURI(),
req.Request.Proto,
resp.StatusCode(),
resp.ContentLength(),
)
}
}
func main() {
ws := new(restful.WebService)
ws.Filter(NCSACommonLogFormatLogger())
ws.Route(ws.GET("/ping").To(hello))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "pong")
}

View File

@ -0,0 +1,35 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows how to create a (Route) Filter that performs Basic Authentication on the Http request.
//
// GET http://localhost:8080/secret
// and use admin,admin for the credentials
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/secret").Filter(basicAuthenticate).To(secret))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func basicAuthenticate(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
// usr/pwd = admin/admin
u, p, ok := req.Request.BasicAuth()
if !ok || u != "admin" || p != "admin" {
resp.AddHeader("WWW-Authenticate", "Basic realm=Protected Area")
resp.WriteErrorString(401, "401: Not Authorized")
return
}
chain.ProcessFilter(req, resp)
}
func secret(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "42")
}

View File

@ -0,0 +1,65 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"os"
"runtime/pprof"
)
// ProfilingService is a WebService that can start/stop a CPU profile and write results to a file
// GET /{rootPath}/start will activate CPU profiling
// GET /{rootPath}/stop will stop profiling
//
// NewProfileService("/profiler", "ace.prof").AddWebServiceTo(restful.DefaultContainer)
//
type ProfilingService struct {
rootPath string // the base (root) of the service, e.g. /profiler
cpuprofile string // the output filename to write profile results, e.g. myservice.prof
cpufile *os.File // if not nil, then profiling is active
}
func NewProfileService(rootPath string, outputFilename string) *ProfilingService {
ps := new(ProfilingService)
ps.rootPath = rootPath
ps.cpuprofile = outputFilename
return ps
}
// Add this ProfileService to a restful Container
func (p ProfilingService) AddWebServiceTo(container *restful.Container) {
ws := new(restful.WebService)
ws.Path(p.rootPath).Consumes("*/*").Produces(restful.MIME_JSON)
ws.Route(ws.GET("/start").To(p.startProfiler))
ws.Route(ws.GET("/stop").To(p.stopProfiler))
container.Add(ws)
}
func (p *ProfilingService) startProfiler(req *restful.Request, resp *restful.Response) {
if p.cpufile != nil {
io.WriteString(resp.ResponseWriter, "[restful] CPU profiling already running")
return // error?
}
cpufile, err := os.Create(p.cpuprofile)
if err != nil {
log.Fatal(err)
}
// remember for close
p.cpufile = cpufile
pprof.StartCPUProfile(cpufile)
io.WriteString(resp.ResponseWriter, "[restful] CPU profiling started, writing on:"+p.cpuprofile)
}
func (p *ProfilingService) stopProfiler(req *restful.Request, resp *restful.Response) {
if p.cpufile == nil {
io.WriteString(resp.ResponseWriter, "[restful] CPU profiling not active")
return // error?
}
pprof.StopCPUProfile()
p.cpufile.Close()
p.cpufile = nil
io.WriteString(resp.ResponseWriter, "[restful] CPU profiling stopped, closing:"+p.cpuprofile)
}
func main() {} // exists for example compilation only

View File

@ -0,0 +1,107 @@
package main
import (
"log"
"net/http"
"github.com/emicklei/go-restful"
)
// This example has the same service definition as restful-user-resource
// but uses a different router (CurlyRouter) that does not use regular expressions
//
// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
// GET http://localhost:8080/users/1
//
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
// DELETE http://localhost:8080/users/1
//
type User struct {
Id, Name string
}
type UserResource struct {
// normally one would use DAO (data access object)
users map[string]User
}
func (u UserResource) Register(container *restful.Container) {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
ws.Route(ws.GET("/{user-id}").To(u.findUser))
ws.Route(ws.POST("").To(u.updateUser))
ws.Route(ws.PUT("/{user-id}").To(u.createUser))
ws.Route(ws.DELETE("/{user-id}").To(u.removeUser))
container.Add(ws)
}
// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
usr := u.users[id]
if len(usr.Id) == 0 {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusNotFound, "User could not be found.")
} else {
response.WriteEntity(usr)
}
}
// POST http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
usr := new(User)
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.Id] = *usr
response.WriteEntity(usr)
} else {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusInternalServerError, err.Error())
}
}
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
usr := User{Id: request.PathParameter("user-id")}
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.Id] = usr
response.WriteHeaderAndEntity(http.StatusCreated, usr)
} else {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusInternalServerError, err.Error())
}
}
// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
delete(u.users, id)
}
func main() {
wsContainer := restful.NewContainer()
wsContainer.Router(restful.CurlyRouter{})
u := UserResource{map[string]User{}}
u.Register(wsContainer)
log.Print("start listening on localhost:8080")
server := &http.Server{Addr: ":8080", Handler: wsContainer}
log.Fatal(server.ListenAndServe())
}

View File

@ -0,0 +1,149 @@
package main
import (
"bytes"
"errors"
"log"
"net/http"
"testing"
"time"
"github.com/emicklei/go-restful"
)
type User struct {
Id, Name string
}
type UserResource struct {
users map[string]User
}
func (u UserResource) Register(container *restful.Container) {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
ws.Route(ws.GET("/{user-id}").To(u.findUser))
ws.Route(ws.POST("").To(u.updateUser))
ws.Route(ws.PUT("/{user-id}").To(u.createUser))
ws.Route(ws.DELETE("/{user-id}").To(u.removeUser))
container.Add(ws)
}
// GET http://localhost:8090/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
usr := u.users[id]
if len(usr.Id) == 0 {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusNotFound, "User could not be found.")
} else {
response.WriteEntity(usr)
}
}
// POST http://localhost:8090/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
usr := new(User)
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.Id] = *usr
response.WriteEntity(usr)
} else {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusInternalServerError, err.Error())
}
}
// PUT http://localhost:8090/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
usr := User{Id: request.PathParameter("user-id")}
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.Id] = usr
response.WriteHeader(http.StatusCreated)
response.WriteEntity(usr)
} else {
response.AddHeader("Content-Type", "text/plain")
response.WriteErrorString(http.StatusInternalServerError, err.Error())
}
}
// DELETE http://localhost:8090/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
delete(u.users, id)
}
func RunRestfulCurlyRouterServer() {
wsContainer := restful.NewContainer()
wsContainer.Router(restful.CurlyRouter{})
u := UserResource{map[string]User{}}
u.Register(wsContainer)
log.Print("start listening on localhost:8090")
server := &http.Server{Addr: ":8090", Handler: wsContainer}
log.Fatal(server.ListenAndServe())
}
func waitForServerUp(serverURL string) error {
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
_, err := http.Get(serverURL + "/")
if err == nil {
return nil
}
}
return errors.New("waiting for server timed out")
}
func TestServer(t *testing.T) {
serverURL := "http://localhost:8090"
go func() {
RunRestfulCurlyRouterServer()
}()
if err := waitForServerUp(serverURL); err != nil {
t.Errorf("%v", err)
}
// GET should give a 405
resp, err := http.Get(serverURL + "/users/")
if err != nil {
t.Errorf("unexpected error in GET /users/: %v", err)
}
if resp.StatusCode != http.StatusMethodNotAllowed {
t.Errorf("unexpected response: %v, expected: %v", resp.StatusCode, http.StatusOK)
}
// Send a POST request.
var jsonStr = []byte(`{"id":"1","name":"user1"}`)
req, err := http.NewRequest("POST", serverURL+"/users/", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", restful.MIME_JSON)
client := &http.Client{}
resp, err = client.Do(req)
if err != nil {
t.Errorf("unexpected error in sending req: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("unexpected response: %v, expected: %v", resp.StatusCode, http.StatusOK)
}
// Test that GET works.
resp, err = http.Get(serverURL + "/users/1")
if err != nil {
t.Errorf("unexpected error in GET /users/1: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("unexpected response: %v, expected: %v", resp.StatusCode, http.StatusOK)
}
}

View File

@ -0,0 +1,61 @@
package main
import (
"github.com/emicklei/go-restful"
"log"
"net/http"
)
type User struct {
Id, Name string
}
type UserList struct {
Users []User
}
//
// This example shows how to use the CompressingResponseWriter by a Filter
// such that encoding can be enabled per WebService or per Route (instead of per container)
// Using restful.DefaultContainer.EnableContentEncoding(true) will encode all responses served by WebServices in the DefaultContainer.
//
// Set Accept-Encoding to gzip or deflate
// GET http://localhost:8080/users/42
// and look at the response headers
func main() {
restful.Add(NewUserService())
log.Print("start listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func NewUserService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
// install a response encoding filter
ws.Route(ws.GET("/{user-id}").Filter(encodingFilter).To(findUser))
return ws
}
// Route Filter (defines FilterFunction)
func encodingFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Printf("[encoding-filter] %s,%s\n", req.Request.Method, req.Request.URL)
// wrap responseWriter into a compressing one
compress, _ := restful.NewCompressingResponseWriter(resp.ResponseWriter, restful.ENCODING_GZIP)
resp.ResponseWriter = compress
defer func() {
compress.Close()
}()
chain.ProcessFilter(req, resp)
}
// GET http://localhost:8080/users/42
//
func findUser(request *restful.Request, response *restful.Response) {
log.Print("findUser")
response.WriteEntity(User{"42", "Gandalf"})
}

View File

@ -0,0 +1,114 @@
package main
import (
"github.com/emicklei/go-restful"
"log"
"net/http"
"time"
)
type User struct {
Id, Name string
}
type UserList struct {
Users []User
}
// This example show how to create and use the three different Filters (Container,WebService and Route)
// When applied to the restful.DefaultContainer, we refer to them as a global filter.
//
// GET http://localhost:8080/users/42
// and see the logging per filter (try repeating this request)
func main() {
// install a global (=DefaultContainer) filter (processed before any webservice in the DefaultContainer)
restful.Filter(globalLogging)
restful.Add(NewUserService())
log.Print("start listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func NewUserService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
// install a webservice filter (processed before any route)
ws.Filter(webserviceLogging).Filter(measureTime)
// install a counter filter
ws.Route(ws.GET("").Filter(NewCountFilter().routeCounter).To(getAllUsers))
// install 2 chained route filters (processed before calling findUser)
ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser))
return ws
}
// Global Filter
func globalLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Printf("[global-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL)
chain.ProcessFilter(req, resp)
}
// WebService Filter
func webserviceLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Printf("[webservice-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL)
chain.ProcessFilter(req, resp)
}
// WebService (post-process) Filter (as a struct that defines a FilterFunction)
func measureTime(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
now := time.Now()
chain.ProcessFilter(req, resp)
log.Printf("[webservice-filter (timer)] %v\n", time.Now().Sub(now))
}
// Route Filter (defines FilterFunction)
func routeLogging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Printf("[route-filter (logger)] %s,%s\n", req.Request.Method, req.Request.URL)
chain.ProcessFilter(req, resp)
}
// Route Filter (as a struct that defines a FilterFunction)
// CountFilter implements a FilterFunction for counting requests.
type CountFilter struct {
count int
counter chan int // for go-routine safe count increments
}
// NewCountFilter creates and initializes a new CountFilter.
func NewCountFilter() *CountFilter {
c := new(CountFilter)
c.counter = make(chan int)
go func() {
for {
c.count += <-c.counter
}
}()
return c
}
// routeCounter increments the count of the filter (through a channel)
func (c *CountFilter) routeCounter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
c.counter <- 1
log.Printf("[route-filter (counter)] count:%d", c.count)
chain.ProcessFilter(req, resp)
}
// GET http://localhost:8080/users
//
func getAllUsers(request *restful.Request, response *restful.Response) {
log.Print("getAllUsers")
response.WriteEntity(UserList{[]User{{"42", "Gandalf"}, {"3.14", "Pi"}}})
}
// GET http://localhost:8080/users/42
//
func findUser(request *restful.Request, response *restful.Response) {
log.Print("findUser")
response.WriteEntity(User{"42", "Gandalf"})
}

View File

@ -0,0 +1,63 @@
package main
import (
"fmt"
"github.com/emicklei/go-restful"
"github.com/gorilla/schema"
"io"
"log"
"net/http"
)
// This example shows how to handle a POST of a HTML form that uses the standard x-www-form-urlencoded content-type.
// It uses the gorilla web tool kit schema package to decode the form data into a struct.
//
// GET http://localhost:8080/profiles
//
type Profile struct {
Name string
Age int
}
var decoder *schema.Decoder
func main() {
decoder = schema.NewDecoder()
ws := new(restful.WebService)
ws.Route(ws.POST("/profiles").Consumes("application/x-www-form-urlencoded").To(postAdddress))
ws.Route(ws.GET("/profiles").To(addresssForm))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func postAdddress(req *restful.Request, resp *restful.Response) {
err := req.Request.ParseForm()
if err != nil {
resp.WriteErrorString(http.StatusBadRequest, err.Error())
return
}
p := new(Profile)
err = decoder.Decode(p, req.Request.PostForm)
if err != nil {
resp.WriteErrorString(http.StatusBadRequest, err.Error())
return
}
io.WriteString(resp.ResponseWriter, fmt.Sprintf("<html><body>Name=%s, Age=%d</body></html>", p.Name, p.Age))
}
func addresssForm(req *restful.Request, resp *restful.Response) {
io.WriteString(resp.ResponseWriter,
`<html>
<body>
<h1>Enter Profile</h1>
<form method="post">
<label>Name:</label>
<input type="text" name="Name"/>
<label>Age:</label>
<input type="text" name="Age"/>
<input type="Submit" />
</form>
</body>
</html>`)
}

View File

@ -0,0 +1,23 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows the minimal code needed to get a restful.WebService working.
//
// GET http://localhost:8080/hello
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "world")
}

View File

@ -0,0 +1,35 @@
package main
import (
"log"
"net/http"
"text/template"
"github.com/emicklei/go-restful"
)
// This example shows how to serve a HTML page using the standard Go template engine.
//
// GET http://localhost:8080/
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/").To(home))
restful.Add(ws)
print("open browser on http://localhost:8080/\n")
log.Fatal(http.ListenAndServe(":8080", nil))
}
type Message struct {
Text string
}
func home(req *restful.Request, resp *restful.Response) {
p := &Message{"restful-html-template demo"}
// you might want to cache compiled templates
t, err := template.ParseFiles("home.html")
if err != nil {
log.Fatalf("Template gave: %s", err)
}
t.Execute(resp.ResponseWriter, p)
}

View File

@ -0,0 +1,43 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows how to have a program with 2 WebServices containers
// each having a http server listening on its own port.
//
// The first "hello" is added to the restful.DefaultContainer (and uses DefaultServeMux)
// For the second "hello", a new container and ServeMux is created
// and requires a new http.Server with the container being the Handler.
// This first server is spawn in its own go-routine such that the program proceeds to create the second.
//
// GET http://localhost:8080/hello
// GET http://localhost:8081/hello
func main() {
ws := new(restful.WebService)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
go func() {
log.Fatal(http.ListenAndServe(":8080", nil))
}()
container2 := restful.NewContainer()
ws2 := new(restful.WebService)
ws2.Route(ws2.GET("/hello").To(hello2))
container2.Add(ws2)
server := &http.Server{Addr: ":8081", Handler: container2}
log.Fatal(server.ListenAndServe())
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "default world")
}
func hello2(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "second world")
}

View File

@ -0,0 +1,25 @@
package main
import (
"io"
"log"
"net/http"
"github.com/emicklei/go-restful"
)
// This example shows how to use a WebService filter that passed the Http headers to disable browser cacheing.
//
// GET http://localhost:8080/hello
func main() {
ws := new(restful.WebService)
ws.Filter(restful.NoBrowserCacheFilter)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "world")
}

View File

@ -0,0 +1,51 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows how to use the OPTIONSFilter on a Container
//
// OPTIONS http://localhost:8080/users
//
// OPTIONS http://localhost:8080/users/1
type UserResource struct{}
func (u UserResource) RegisterTo(container *restful.Container) {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes("*/*").
Produces("*/*")
ws.Route(ws.GET("/{user-id}").To(u.nop))
ws.Route(ws.POST("").To(u.nop))
ws.Route(ws.PUT("/{user-id}").To(u.nop))
ws.Route(ws.DELETE("/{user-id}").To(u.nop))
container.Add(ws)
}
func (u UserResource) nop(request *restful.Request, response *restful.Response) {
io.WriteString(response.ResponseWriter, "this would be a normal response")
}
func main() {
wsContainer := restful.NewContainer()
u := UserResource{}
u.RegisterTo(wsContainer)
// Add container filter to respond to OPTIONS
wsContainer.Filter(wsContainer.OPTIONSFilter)
// For use on the default container, you can write
// restful.Filter(restful.OPTIONSFilter())
log.Print("start listening on localhost:8080")
server := &http.Server{Addr: ":8080", Handler: wsContainer}
log.Fatal(server.ListenAndServe())
}

View File

@ -0,0 +1,27 @@
package main
import (
. "github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows how to create a Route matching the "tail" of a path.
// Requires the use of a CurlyRouter and the star "*" path parameter pattern.
//
// GET http://localhost:8080/basepath/some/other/location/test.xml
func main() {
DefaultContainer.Router(CurlyRouter{})
ws := new(WebService)
ws.Route(ws.GET("/basepath/{resource:*}").To(staticFromPathParam))
Add(ws)
println("[go-restful] serve path tails from http://localhost:8080/basepath")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func staticFromPathParam(req *Request, resp *Response) {
io.WriteString(resp, "Tail="+req.PathParameter("resource"))
}

View File

@ -0,0 +1,98 @@
package main
import (
"github.com/emicklei/go-restful"
"io"
"log"
"net/http"
)
// This example shows how the different types of filters are called in the request-response flow.
// The call chain is logged on the console when sending an http request.
//
// GET http://localhost:8080/1
// GET http://localhost:8080/2
var indentLevel int
func container_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Printf("url path:%v\n", req.Request.URL)
trace("container_filter_A: before", 1)
chain.ProcessFilter(req, resp)
trace("container_filter_A: after", -1)
}
func container_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
trace("container_filter_B: before", 1)
chain.ProcessFilter(req, resp)
trace("container_filter_B: after", -1)
}
func service_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
trace("service_filter_A: before", 1)
chain.ProcessFilter(req, resp)
trace("service_filter_A: after", -1)
}
func service_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
trace("service_filter_B: before", 1)
chain.ProcessFilter(req, resp)
trace("service_filter_B: after", -1)
}
func route_filter_A(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
trace("route_filter_A: before", 1)
chain.ProcessFilter(req, resp)
trace("route_filter_A: after", -1)
}
func route_filter_B(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
trace("route_filter_B: before", 1)
chain.ProcessFilter(req, resp)
trace("route_filter_B: after", -1)
}
func trace(what string, delta int) {
indented := what
if delta < 0 {
indentLevel += delta
}
for t := 0; t < indentLevel; t++ {
indented = "." + indented
}
log.Printf("%s", indented)
if delta > 0 {
indentLevel += delta
}
}
func main() {
restful.Filter(container_filter_A)
restful.Filter(container_filter_B)
ws1 := new(restful.WebService)
ws1.Path("/1")
ws1.Filter(service_filter_A)
ws1.Filter(service_filter_B)
ws1.Route(ws1.GET("").To(doit1).Filter(route_filter_A).Filter(route_filter_B))
ws2 := new(restful.WebService)
ws2.Path("/2")
ws2.Filter(service_filter_A)
ws2.Filter(service_filter_B)
ws2.Route(ws2.GET("").To(doit2).Filter(route_filter_A).Filter(route_filter_B))
restful.Add(ws1)
restful.Add(ws2)
log.Print("go-restful example listing on http://localhost:8080/1 and http://localhost:8080/2")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func doit1(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "nothing to see in 1")
}
func doit2(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "nothing to see in 2")
}

View File

@ -0,0 +1,63 @@
package main
import (
"github.com/emicklei/go-restful"
"log"
"net/http"
)
// This example shows how to use methods as RouteFunctions for WebServices.
// The ProductResource has a Register() method that creates and initializes
// a WebService to expose its methods as REST operations.
// The WebService is added to the restful.DefaultContainer.
// A ProductResource is typically created using some data access object.
//
// GET http://localhost:8080/products/1
// POST http://localhost:8080/products
// <Product><Id>1</Id><Title>The First</Title></Product>
type Product struct {
Id, Title string
}
type ProductResource struct {
// typically reference a DAO (data-access-object)
}
func (p ProductResource) getOne(req *restful.Request, resp *restful.Response) {
id := req.PathParameter("id")
log.Println("getting product with id:" + id)
resp.WriteEntity(Product{Id: id, Title: "test"})
}
func (p ProductResource) postOne(req *restful.Request, resp *restful.Response) {
updatedProduct := new(Product)
err := req.ReadEntity(updatedProduct)
if err != nil { // bad request
resp.WriteErrorString(http.StatusBadRequest, err.Error())
return
}
log.Println("updating product with id:" + updatedProduct.Id)
}
func (p ProductResource) Register() {
ws := new(restful.WebService)
ws.Path("/products")
ws.Consumes(restful.MIME_XML)
ws.Produces(restful.MIME_XML)
ws.Route(ws.GET("/{id}").To(p.getOne).
Doc("get the product by its id").
Param(ws.PathParameter("id", "identifier of the product").DataType("string")))
ws.Route(ws.POST("").To(p.postOne).
Doc("update or create a product").
Param(ws.BodyParameter("Product", "a Product (XML)").DataType("main.Product")))
restful.Add(ws)
}
func main() {
ProductResource{}.Register()
log.Fatal(http.ListenAndServe(":8080", nil))
}

View File

@ -0,0 +1,39 @@
package main
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/emicklei/go-restful"
)
var (
Result string
)
func TestRouteExtractParameter(t *testing.T) {
// setup service
ws := new(restful.WebService)
ws.Consumes(restful.MIME_XML)
ws.Route(ws.GET("/test/{param}").To(DummyHandler))
restful.Add(ws)
// setup request + writer
bodyReader := strings.NewReader("<Sample><Value>42</Value></Sample>")
httpRequest, _ := http.NewRequest("GET", "/test/THIS", bodyReader)
httpRequest.Header.Set("Content-Type", restful.MIME_XML)
httpWriter := httptest.NewRecorder()
// run
restful.DefaultContainer.ServeHTTP(httpWriter, httpRequest)
if Result != "THIS" {
t.Fatalf("Result is actually: %s", Result)
}
}
func DummyHandler(rq *restful.Request, rp *restful.Response) {
Result = rq.PathParameter("param")
}

View File

@ -0,0 +1,29 @@
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/emicklei/go-restful"
)
// This example show how to test one particular RouteFunction (getIt)
// It uses the httptest.ResponseRecorder to capture output
func getIt(req *restful.Request, resp *restful.Response) {
resp.WriteHeader(204)
}
func TestCallFunction(t *testing.T) {
httpReq, _ := http.NewRequest("GET", "/", nil)
req := restful.NewRequest(httpReq)
recorder := new(httptest.ResponseRecorder)
resp := restful.NewResponse(recorder)
getIt(req, resp)
if recorder.Code != 204 {
t.Fatalf("Missing or wrong status code:%d", recorder.Code)
}
}

View File

@ -0,0 +1,47 @@
package main
import (
"fmt"
"net/http"
"path"
"github.com/emicklei/go-restful"
)
// This example shows how to define methods that serve static files
// It uses the standard http.ServeFile method
//
// GET http://localhost:8080/static/test.xml
// GET http://localhost:8080/static/
//
// GET http://localhost:8080/static?resource=subdir/test.xml
var rootdir = "/tmp"
func main() {
restful.DefaultContainer.Router(restful.CurlyRouter{})
ws := new(restful.WebService)
ws.Route(ws.GET("/static/{subpath:*}").To(staticFromPathParam))
ws.Route(ws.GET("/static").To(staticFromQueryParam))
restful.Add(ws)
println("[go-restful] serving files on http://localhost:8080/static from local /tmp")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func staticFromPathParam(req *restful.Request, resp *restful.Response) {
actual := path.Join(rootdir, req.PathParameter("subpath"))
fmt.Printf("serving %s ... (from %s)\n", actual, req.PathParameter("subpath"))
http.ServeFile(
resp.ResponseWriter,
req.Request,
actual)
}
func staticFromQueryParam(req *restful.Request, resp *restful.Response) {
http.ServeFile(
resp.ResponseWriter,
req.Request,
path.Join(rootdir, req.QueryParameter("resource")))
}

View File

@ -0,0 +1,61 @@
package main
import (
"log"
"net/http"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-swagger12"
)
type Book struct {
Title string
Author string
}
func main() {
ws := new(restful.WebService)
ws.Path("/books")
ws.Consumes(restful.MIME_JSON, restful.MIME_XML)
ws.Produces(restful.MIME_JSON, restful.MIME_XML)
restful.Add(ws)
ws.Route(ws.GET("/{medium}").To(noop).
Doc("Search all books").
Param(ws.PathParameter("medium", "digital or paperback").DataType("string")).
Param(ws.QueryParameter("language", "en,nl,de").DataType("string")).
Param(ws.HeaderParameter("If-Modified-Since", "last known timestamp").DataType("datetime")).
Do(returns200, returns500))
ws.Route(ws.PUT("/{medium}").To(noop).
Doc("Add a new book").
Param(ws.PathParameter("medium", "digital or paperback").DataType("string")).
Reads(Book{}))
// You can install the Swagger Service which provides a nice Web UI on your REST API
// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
// Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
config := swagger.Config{
WebServices: restful.DefaultContainer.RegisteredWebServices(), // you control what services are visible
WebServicesUrl: "http://localhost:8080",
ApiPath: "/apidocs.json",
// Optionally, specify where the UI is located
SwaggerPath: "/apidocs/",
SwaggerFilePath: "/Users/emicklei/xProjects/swagger-ui/dist"}
swagger.RegisterSwaggerService(config, restful.DefaultContainer)
log.Print("start listening on localhost:8080")
server := &http.Server{Addr: ":8080", Handler: restful.DefaultContainer}
log.Fatal(server.ListenAndServe())
}
func noop(req *restful.Request, resp *restful.Response) {}
func returns200(b *restful.RouteBuilder) {
b.Returns(http.StatusOK, "OK", Book{})
}
func returns500(b *restful.RouteBuilder) {
b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil)
}

View File

@ -0,0 +1,170 @@
package main
import (
"log"
"net/http"
"github.com/emicklei/go-restful"
restfulspec "github.com/emicklei/go-restful-openapi"
"github.com/go-openapi/spec"
)
// UserResource is the REST layer to the User domain
type UserResource struct {
// normally one would use DAO (data access object)
users map[string]User
}
// WebService creates a new service that can handle REST requests for User resources.
func (u UserResource) WebService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
tags := []string{"users"}
ws.Route(ws.GET("/").To(u.findAllUsers).
// docs
Doc("get all users").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes([]User{}).
Returns(200, "OK", []User{}))
ws.Route(ws.GET("/{user-id}").To(u.findUser).
// docs
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("integer").DefaultValue("1")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(User{}). // on the response
Returns(200, "OK", User{}).
Returns(404, "Not Found", nil))
ws.Route(ws.PUT("/{user-id}").To(u.updateUser).
// docs
Doc("update a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(User{})) // from the request
ws.Route(ws.PUT("").To(u.createUser).
// docs
Doc("create a user").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(User{})) // from the request
ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
// docs
Doc("delete a user").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))
return ws
}
// GET http://localhost:8080/users
//
func (u UserResource) findAllUsers(request *restful.Request, response *restful.Response) {
list := []User{}
for _, each := range u.users {
list = append(list, each)
}
response.WriteEntity(list)
}
// GET http://localhost:8080/users/1
//
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
usr := u.users[id]
if len(usr.ID) == 0 {
response.WriteErrorString(http.StatusNotFound, "User could not be found.")
} else {
response.WriteEntity(usr)
}
}
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
usr := new(User)
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.ID] = *usr
response.WriteEntity(usr)
} else {
response.WriteError(http.StatusInternalServerError, err)
}
}
// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
usr := User{ID: request.PathParameter("user-id")}
err := request.ReadEntity(&usr)
if err == nil {
u.users[usr.ID] = usr
response.WriteHeaderAndEntity(http.StatusCreated, usr)
} else {
response.WriteError(http.StatusInternalServerError, err)
}
}
// DELETE http://localhost:8080/users/1
//
func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
delete(u.users, id)
}
func main() {
u := UserResource{map[string]User{}}
restful.DefaultContainer.Add(u.WebService())
config := restfulspec.Config{
WebServices: restful.RegisteredWebServices(), // you control what services are visible
WebServicesURL: "http://localhost:8080",
APIPath: "/apidocs.json",
PostBuildSwaggerObjectHandler: enrichSwaggerObject}
restful.DefaultContainer.Add(restfulspec.NewOpenAPIService(config))
// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
// Open http://localhost:8080/apidocs/?url=http://localhost:8080/apidocs.json
http.Handle("/apidocs/", http.StripPrefix("/apidocs/", http.FileServer(http.Dir("/Users/emicklei/Projects/swagger-ui/dist"))))
log.Printf("start listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func enrichSwaggerObject(swo *spec.Swagger) {
swo.Info = &spec.Info{
InfoProps: spec.InfoProps{
Title: "UserService",
Description: "Resource for managing Users",
Contact: &spec.ContactInfo{
Name: "john",
Email: "john@doe.rp",
URL: "http://johndoe.org",
},
License: &spec.License{
Name: "MIT",
URL: "http://mit.org",
},
Version: "1.0.0",
},
}
swo.Tags = []spec.Tag{spec.Tag{TagProps: spec.TagProps{
Name: "users",
Description: "Managing users"}}}
}
// User is just a sample type
type User struct {
ID string `json:"id" description:"identifier of the user"`
Name string `json:"name" description:"name of the user" default:"john"`
Age int `json:"age" description:"age of the user" default:"21"`
}