// 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
// if rootPath was not set then lazy initialize it iflen(service.rootPath) == 0 { service.Path("/") }
// cannot have duplicate root paths for _, each := range c.webServices { if each.RootPath() == service.RootPath() { log.Printf("WebService with duplicate root path detected:['%v']", each) os.Exit(1) } }
// If not registered on root then add specific mapping if !c.isRegisteredOnRoot { c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux) } c.webServices = append(c.webServices, service) return c }
// Find best match Route ; err is non nil if no match was found var webService *WebService var route *Route var err error func() { c.webServicesLock.RLock() defer c.webServicesLock.RUnlock() webService, route, err = c.router.SelectRoute( c.webServices, httpRequest) }()
func(c *Container) Handle(pattern string, handler http.Handler) { c.ServeMux.Handle(pattern, http.HandlerFunc(func(httpWriter http.ResponseWriter, httpRequest *http.Request) { // Skip, if httpWriter is already an CompressingResponseWriter if _, ok := httpWriter.(*CompressingResponseWriter); ok { handler.ServeHTTP(httpWriter, httpRequest) return }
writer := httpWriter
// CompressingResponseWriter should be closed after all operations are done deferfunc() { if compressWriter, ok := writer.(*CompressingResponseWriter); ok { compressWriter.Close() } }()
if c.contentEncodingEnabled { doCompress, encoding := wantsCompressedResponse(httpRequest) if doCompress { var err error writer, err = NewCompressingResponseWriter(httpWriter, encoding) if err != nil { log.Print("unable to install compressor: ", err) httpWriter.WriteHeader(http.StatusInternalServerError) return } } }
// SelectRoute finds a Route given the input HTTP Request and a list of WebServices. // It returns a selected Route and its containing WebService or an error indicating // a problem. SelectRoute( webServices []*WebService, httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) }
funcNewWebService(group string, version string) *restful.WebService { ws := new(restful.WebService) ws.Path("/apis/" + group + "/" + version) ws.Doc("API at /apis/apps/v1") ws.Consumes(restful.MIME_XML, restful.MIME_JSON) ws.Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well return ws }
nameParam := ws.PathParameter("name", "name of the resource").DataType("string") namespaceParam := ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string")
// Director is here so that we can properly handle fall through and proxy cases. // This looks a bit bonkers, but here's what's happening. We need to have /apis handling registered in gorestful in order to have // swagger generated for compatibility. Doing that with `/apis` as a webservice, means that it forcibly 404s (no defaulting allowed) // all requests which are not /apis or /apis/. We need those calls to fall through behind goresful for proper delegation. Trying to // register for a pattern which includes everything behind it doesn't work because gorestful negotiates for verbs and content encoding // and all those things go crazy when gorestful really just needs to pass through. In addition, openapi enforces unique verb constraints // which we don't fit into and it still muddies up swagger. Trying to switch the webservices into a route doesn't work because the // containing webservice faces all the same problems listed above. // This leads to the crazy thing done here. Our mux does what we need, so we'll place it in front of gorestful. It will introspect to // decide if the route is likely to be handled by goresful and route there if needed. Otherwise, it goes to PostGoRestful mux in // order to handle "normal" paths and delegation. Hopefully no API consumers will ever have to deal with this level of detail. I think // we should consider completely removing gorestful. // Other servers should only use this opaquely to delegate to an API server. Director http.Handler