emicklei / go-restful Goto Github PK
View Code? Open in Web Editor NEWpackage for building REST-style Web Services using Go
License: MIT License
package for building REST-style Web Services using Go
License: MIT License
Hello,
in order to be able to use the go-restful server more generally we need not only to execute rest commands but also be able to serve static files in a simple manner.
Do you plan to add this feature?
Best regards
Xavier
Hi,
I'm really new to GoLang and really found your efforts helpful.
I probably found a bug in "restful-basic-authentication.go".
In line:19
if len(encoded) == 0 || "YWRzOmFzZA==" != encoded
Since the encoding method is BASE64, and the decoding of "YWRzOmFzZA==" is "ads:asd".
I think line:19 shoud be
if len(encoded) == 0 || "Basic YWRtaW4vYWRtaW4=" != encoded
Perhaps it could save others time who can't run this example.
Regards,
Leo
I'm building a service that will require client authentication on request. Ideally I'd like to be able to validate the user id, query for the user, authenticate and then pass the user object down the chain so each layer doesn't have to re-query.
I think the best description of what I want to do is having a context that can be passed in addition to the request and response.
Is there some mechanism today that can support this?
func authenticateUserFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain, context *restful.FilterContext) {
//Validate User Id
// Query User from DB
user, err := LoadFromDB()
if err != nil {
//Forbidden
}
context.Add("user", user)
chain.ProcessFilter(req, resp, context)
}
when i request from client with a gzip header, it can return gzip data
/mail?folder=Spam
Is there a build in way to Route via QueryParameter?
Thanks
Wouldn't it be better to separate the swagger bits into a separate package?
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)
ws.Route(ws.GET("/login").To(u.loginUser).
Doc("get a user").
Param(ws.QueryParameter("name", "username").DataType("string")).
Param(ws.QueryParameter("pwd", "password").DataType("string")))
//get all user service
//get all team user service
//get all user without password service .....
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")))
restful.Add(ws)
}
func (u UserService) loginUser(request *restful.Request, response *restful.Response) {
log.Println("========================================")
log.Println(request.QueryParameter("name"))
log.Println(request.QueryParameter("pwd"))
}
func (u UserService) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
log.Println("000000000000000000000000000000000000000000000000000")
log.Println(id)
}
When i call findUser function result:
2013/08/14 14:17:33 000000000000000000000000000000000000000000000000000
2013/08/14 14:17:33 asdfasdfasd
When i call loginUser function result:
2013/08/14 14:17:41 000000000000000000000000000000000000000000000000000
2013/08/14 14:17:41 login
how can i create like http:/petstore.swagger.wordnik.com user service?
currenty, the container has a catch all to recover panic situations and return 500 responses.
https://groups.google.com/forum/m/?fromgroups#!topic/golang-nuts/UvUcRAK4rsc triggered me to revise this. Additionally, experiments show a performance penalty for using the recover language feature. I intend to make this behavior configurable (DoNotRecover?) or just remove the construction and let users of the package decide how to handle panics in their Route functions.
My project setup has the base dir, then a dir/package for "model" and a dir/package for "services". I have a user.go file in "services" that handles registration for the users service. This is the code for initializing the user service:
func NewUserService() *restful.WebService {
webService := new(restful.WebService)
webService.Path("/users").
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
webService.Route(webService.GET("/{id}").To(GetUser))
webService.Route(webService.POST("/{id}/device").To(RegisterDevice))
webService.Route(webService.POST("/{id}/changePassword").To(ChangePassword))
webService.Route(webService.POST("/login").To(Login))
return webService
}
Anytime I try to hit /login with a POST while running locally, I get a 405: Method Not Allowed response. Meanwhile, doing a POST to the other 2 endpoints works just fine. And on top of that, if I modify the route to be "/{somePathParam}/login", it works just fine.
Access-Control-Request-Headers can be a comma delimited list. when more than one element is in this list, isValidAccessControlRequestHeader in cors_filter.go will fail for even valid requests.
to test:
change cors_filter_test.go:20
to:
AllowedHeaders: []string{"X-Custom-Header, X-Additional-Header"},
and line 29
to:
httpRequest.Header.Set(HEADER_AccessControlRequestHeaders, "X-Custom-Header, X-Additional-Header")
I would like to avoid having swagger registering file server path to serv swagger-ui.
This is to support machine consumption of the swagger def but not having the actual swagger-ui. If I need it I can later stuff it in nginx or something infront of the services.
It seems to be already possible to mock a restful.Request since it simply wraps a http.Request, which in turn can be hand-crafted while testing. However, it doesn't seem to be possible to wrap a httptest.ResponseRecording in a restful.Response in order to examine what my handlers have done to it.
Hi,
How can i return multiple JSON objects in one response?
type Organisation struct {
Channels []ChannelEntry `bson:"channels"`
}
type ChannelEntry struct {
ChannelId bson.ObjectId `bson:"channel_id`
}
I fetch the attributes of the channel with its ID and then put them in another struct. However, I want to return all of the channels to the client in one response. How do i do this?
I get warnings from the standard library I guess about multiple calls to WriteHeader
2013/07/10 14:03:05 http: multiple response.WriteHeader calls
I think it is because go-restful detects mismatching content types and emits status code 406 (or some other in other cases) but still invokes the service method.
Isn't the idea to abort the call chain in case of an error?
func NewMovieService() *restful.WebService {
ws := new(restful.WebService)
ws.Path("/api/v2/movies").
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("").To(getAllMovies).
Doc("get all movies"))
ws.Route(ws.GET("/{id}").To(getMovie).
Doc("get a movie by its id").
Param(ws.PathParameter("id", "movie id")))
return ws
}
If I request /api/v2/movies from my browser I receive:
<?xml version="1.0" encoding="UTF-8"?>
<File>
<Id>1</Id>
<Path>/media/data/media/movies/1 Harold.&.Kumar.Go.To.White.Castle.2004.avi</Path>
<SearchString>1 Harold & Kumar Go To White Castle 2004</SearchString>
</File>
[...]
It seems like the models
object is located in the wrong place in the json output from go-restful/swagger. From what I have read on the Swagger page, and what swagger-ui expects is that models
should be on the same level as apis
, and not nested under apis
like it is now.
Here is an example with this problem:
https://gist.github.com/viblo/7407666
func TestSelectRoutesSlash2(t *testing.T) {
ws1 := new(WebService).Path("/")
ws1.Route(ws1.GET("/{type}/{id}"))
ws1.Route(ws1.GET("/network/{id}"))
routes := RouterJSR311{}.selectRoutes(ws1, "/network/12")
if routes[0].Path != "/network/{id}" {
t.Error("first is", routes[0].Path)
}
}
Hi,
is there any way i can read a POST request easily - as x-www-urlencoded?
I was looking for a nice way of logging every request but it seems like the explicit calls to http.HandlerFunc (providing it Dispatch) make this a bit tough.
I have a few header parameters that I use and I can get the underlying request to find it but it would be very nice to have it a first class parameter in the same way as body and path parameters.
If you are busy I don't mind contributing a patch if you are willing to accept pull requests and of course if you like the feature. ;)
I am attempting to OPTIONS on a valid web service resource, and it returns a 405.
if a filter needs to read the entity before the actual endpoint does, this will cause an error because you cant reread the entity. it would be much better if ReadEntity cached the buffer on the request so it could be retrieved at a later time.
This is truly a support question, sorry for abusing GH for this. I was wondering how to specify that a certain parameter "subset" can be not set.
Snippet of route definition is:
ws.Route(ws.GET("/datasets/taxonomy/{which}/{subset}").To(an.GetTaxonomyDatasets).
Doc("blah").
Param(ws.PathParameter("which", "blah")).
Param(ws.PathParameter("subset", "blah").Required(false)).
Writes(struct{ Datasets []Dataset }{}))
When calling this Route like
"...datasets/taxonomy/location/New York" it works, when calling like
"...datasets/taxonomy/location/" (There are locations with len(str) = 0) I get an
HTTP/1.1 405 Method Not Allowed
error.
Hi emicklei,
your project is awesome thanks for this impressions.
I've one question: Is it possible to use your project with die google app engine framework ?
Greets,
Moddus
I've been playing around with the examples and run into a snag with the restful-serve-static.go example on IE9. When running the example on FireFox/Chrome everything works as expected I receive the correct static content. However on IE9 I receive a 406: Not Acceptable.
The current Request being generated looks like the following:
Key Value
Request GET /static/FGH.html HTTP/1.1
Accept text/html, application/xhtml+xml, /
Accept-Language en-US
User-Agent Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
UA-CPU AMD64
Accept-Encoding gzip, deflate
Host localhost:8080
If-Modified-Since Wed, 16 Oct 2013 17:16:36 GMT
Connection Keep-Alive
Reading through the restul-options-filter example I see that we can identify the Consumes/Produces and I've tried setting up the Options. Can you point me in the right direction?
Thanks!
Can functions in "web_service_container.go" are encapsulated in a struct, it helpful for unit test, examples as:
func Test1(t *testing.T) {
webContainer := restful.NewContainer()
ws := new(restful.WebService)
.....
webContainer.Add(ws)
restful.WebContainer = webContainer
ts := httptest.NewServer(restful.Dispatch)
defer ts.Close()
......
}
func Test2(t *testing.T) {
webContainer := restful.NewContainer()
ws := new(restful.WebService)
.....
webContainer.Add(ws)
restful.WebContainer = webContainer
ts := httptest.NewServer(restful.Dispatch)
defer ts.Close()
......
}
Hi, sorry i might post this question in the wrong section. but if i may ask. i wonder how to document a model schema, so that it will show up in swagger-ui?
Thanks
Inspired by https://github.com/pilu/traffic-chromelogger
Hi again,
Sorry to bother you in your sweet holidays :) .
Yesterday, I tried to consume a JSON data with cURL:
curl -v -H "Content-Type: application/json" -d '{"Name":"US"}' http://localhost:8080/user/4
It returns:
* About to connect() to localhost port 8080 (#0)
* Trying 127.0.0.1... connected
> POST /user/4 HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8080
> Accept: */*
> Content-Type: application/json
> Content-Length: 13
>
* upload completely sent off: 13out of 13 bytes
< HTTP/1.1 201 Created
< Content-Type: text/plain; charset=utf-8
< Content-Length: 16
< Date: Tue, 23 Jul 2013 08:53:33 GMT
<
{
"Id": "4"
* Connection #0 to host localhost left intact
* Closing connection #0
}
But today, I tried to consume "text/plain", and I change the code:
ws.Path("/user").Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON)
to:
ws.Path("/user").Consumes("text/plain").Produces(restful.MIME_JSON)
and try
curl -v -H "Content-Type:text/plain" -d "Name=US" http://localhost:8080/user/4
* About to connect() to localhost port 8080 (#0)
* Trying 127.0.0.1... connected
> POST /user/4 HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8080
> Accept: */*
> Content-Type:text/plain
> Content-Length: 7
>
* upload completely sent off: 7out of 7 bytes
< HTTP/1.1 500 Internal Server Error
< Content-Type: text/plain; charset=utf-8
< Content-Length: 56
< Date: Tue, 23 Jul 2013 09:01:34 GMT
<
* Connection #0 to host localhost left intact
* Closing connection #0
[restful] Unable to unmarshal content of type:text/plain
The result is obviously not I want :( . I spent a whole day on this and it didn't work out, therefore I push an issue to you.
Hope you enjoy the rest of your holidays.
Regards,
Leo
The clientgen branch is created to work on a tool for generating the Go source for a HTTP client for a given Webservice Api.
in spirit of http.Handle,so that eg. Database access logic etc. can be wrapped inside a handler
Please excuse me opening a GH issue for what I think is really just a question.
I have a use case where each request relies on having it's own *sql.Tx
, and when the request does not err out, that should be COMMIT
ted, when the request errs it should be ROLLBACK
ed.
I had hoped to use filters for this - (as well as authentication), in both cases, I'd need to be able to arbitrarily inject something into the request context.
I couldn't see it documented, if I wouldn't be experimenting with go-restfully, I'd probably use http://www.gorillatoolkit.org/pkg/context
Thanks for a really cool package.
Hello,
Do you plan to add an authentication system like for instance the one we could have in restlets (http://restlet.org/learn/tutorial/2.1/#part09)? It is quite mandatory in inorder to ensure a minimal security..
best regards
Xavier
I think it is really a mandatory swagger element and although my service work fine without it consuming clients probably needs the nickname.
Would something like this make sense:
.....Doc("Create or update the Application node").Nick("myCrudMethod")....?
Swagger doesnt handle structs with json-tags for anything except the basic case. Fields tagged with omitempty, strings or "-" are not handled meaning the swagger output is wrong for those structs.
just run go build,here is the log ,anything wrong ?
leotekiMacBook-Pro:go-restful leo$ go build
./container.go:64: method c.dispatch is not an expression, must be called
./container.go:76: method c.dispatch is not an expression, must be called
./container.go:78: method c.dispatch is not an expression, must be called
I am implementing a global logging filter similar to one in your examples. What I can't figure out is how to log the Status Code of the response. restful.Response is composed of a http.ResponseWriter, there is no method to access the status code once the code is written using WriterHeader(code).
type Response struct { http.ResponseWriter accept string // content-types what the Http Request says it want to receive produces []string // content-types what the Route says it can produce }
This problem has been talked about on the Go mailing list and the solution is to wrap http.ResponseWriter (like you are doing!) and add an additional field to store the status code. Would you consider adding a field here to store that status code?
Thanks!
I'm trying to test out your framework for use with a Go based Google App Engine app but can't get past step 1. When I issue a command
go get github.com/emicklei/go-restful
It pulls it in and spews out several errors:
src/github.com/emicklei/go-restful/container.go:67: method c.dispatch is not an expression, must be called
src/github.com/emicklei/go-restful/container.go:79: method c.dispatch is not an expression, must be called
src/github.com/emicklei/go-restful/container.go:81: method c.dispatch is not an expression, must be called
src/github.com/emicklei/go-restful/curly.go:49: undefined: sort.Reverse
src/github.com/emicklei/go-restful/jsr311.go:109: undefined: sort.Reverse
src/github.com/emicklei/go-restful/jsr311.go:136: undefined: sort.Reverse
src/github.com/emicklei/go-restful/options_filter.go:21: method DefaultContainer.OPTIONSFilter is not an expression, must be called
src/github.com/emicklei/go-restful/request.go:48: r.Request.PostFormValue undefined (type *http.Request has no field or method PostFormValue)
Am I doing it wrong? I'm pretty new to Go and this is the first package outside the standard ones I am trying to use...
I'd like to kick off using this for a large project I am planning, so appreciate any assistance you can provide.
p.s. I found the fork by Moddus - but it does exactly the same thing.
I have implemented Filter (see below) and added it to the Container and I'm seeing an unexpected result.
In my log I see the following:
2013/10/05 21:45:22 Pre Filter
2013/10/05 21:45:22 Post Filter
2013/10/05 21:45:22 Returning from MyHandler
When I would have expected to see:
2013/10/05 21:45:22 Pre Filter
2013/10/05 21:45:22 Returning from MyHandler
2013/10/05 21:45:22 Post Filter
If I instead add the Filter to a Webservice my expected result. (Similar to the measureTime example).
I was thinking of Filters similar to Django middleware (They are executed like a stack) but it does not seem to be the case.
Is it possible to implement a Container filter that wraps a request. It would be nice to only have to add logging and instrumentation filters once to the container. Why do Container and Webservice filter behave differently?
Thanks for you time!
func PerformanceFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
log.Println("Pre Filter")
chain.ProcessFilter(req, resp)
log.Println("Post Filter")
}
BTW: Love the swagger support!
in net/http I would do the following:
if origin := req.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
Where is the right place to enable CORS in go-restful? I would assume a filter ...
It would be really nice to implement API versioning. Not sure what the design would be, but this is a feature I think would be extremely beneficial to this project.
I've been using go-restful for a few weeks and so far have been really impressed. I'm having one issue that I can not figure out. (Go newbie here)
I have created a WebService specifying it Consumes and Produces JSON, however the response I receive from the server has the Content-Type as "text/plain".
I have read and re-read the documentation and can't solve this issue. I have tried adding an Accept header to the Request but "text/plain" is still returned. The documentation seems implies that if Produces set to only product JSON all responses from the server will have the Content-Type as "application/json"
Any ideas how to solve this issue?
ws := new(restful.WebService)
ws.Path("/internal")
ws.Consumes(restful.MIME_JSON)
ws.Produces(restful.MIME_JSON)
ws.Route(ws.GET("/category/{user_id}").To(r.getCategories).
Doc("Return available categories").
Param(ws.PathParameter("user_id", "Id of user").Required(true)).
Writes(model.ApiResponse{}))
My handler function return the results as follows.
func (r MyResource) getCategories(req *restful.Request, resp *restful.Response) {
...
resp.WriteHeader(http.StatusOK)
resp.WriteAsJson(data)
return
}
Hi emicklei, really love your library!
Is there any way to serve a zip file in one of my web services instead of returning a JSON or XML response?
What I want is to be able to call:
http.ServeFile(response, request, path.Join(rootdir, filename))
But I don't know how to extract the http.ResponseWriter and http.Request from your restful.Request and restful.Response.
Thanks in advance.
'''go lang
ws.Route(ws.PUT("").To(u.createUser).
Doc("create user").
Param(ws.BodyParameter("User", "User").DataType("User")).
Reads(model.User{})) // from the request
func (u *UserService) createUser(request *restful.Request, response *restful.Response) {
usr := new(model.User)
err := request.ReadEntity(&usr)
if err == nil {
_, err = u.UserLogic.SaveUser(usr)
log.Println(err)
} else {
log.Println("Create user err:", err)
}
}
'''
when I use swagger put data test is ok
when I use chrome rest console tool test is ok
but use chrome postman test report error:
415: Unsupported Media Type
Is there a way to register restful's handlers on something other than '/'? For example having restful attach to '/api/' so that I can serve a UI from '/'?
Or is the recommended route to run the webservice on another port and then reverse proxy from ':80/api/' -> ':8080/' ?
Because Firefox sends a Content-Type Header together with encoding information (Content-Type: application/json; charset=UTF-8
) the webserver always responds with HTTP 415 Unsupported Media Type
.
The code looks good to me at the first glance but I'll have a look tomorrow and try to find a patch.
Currently the models
field of an API declaration is not generated. Vestigial support is included in the RouteBuilder
and swagger.Api
types. The models
field could be populated inside getDeclarations()
.
The hard part is generating the json-schema description of the models. Probably best to implement that as a separate library. It can work kinda similarly to json.Marshal()
.
I define a struct File include file array.Cpu and memory is 100% when run this program.
I find if File struct not include []File the program is right.But I can't find proberlem point :(
type File struct {
Id, Name string
HitoryFile []File
}
type FileService struct {
// normally one would use DAO (data access object)
files map[string]File
}
func (f FileService) Register() {
ws := new(restful.WebService)
ws.
Path("/files").
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("/{file-id}").To(f.findFile).
// docs
Doc("get a file").
Param(ws.PathParameter("file-id", "identifier of the file").DataType("string")).
Writes(File{})) // on the response
restful.Add(ws)
}
// GET http://localhost:8080/users/1
//
func (f FileService) findFile(request *restful.Request, response *restful.Response) {
id := request.PathParameter("file-id")
file := f.files[id]
if len(file.Id) == 0 {
response.WriteErrorString(http.StatusNotFound, "File could not be found.")
} else {
response.WriteEntity(file)
}
}
func main() {
f := FileService{map[string]File{}}
f.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 http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field.
config := swagger.Config{
WebServices: restful.RegisteredWebServices(), // you control what services are visible
WebServicesUrl: "http://localhost:8080",
ApiPath: "/apidocs.json",
// Optionally, specifiy where the UI is located
SwaggerPath: "/apidocs/",
SwaggerFilePath: "./apiview"}
swagger.InstallSwaggerService(config)
log.Printf("start listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.