GithubHelp home page GithubHelp logo

ant0ine / go-json-rest Goto Github PK

View Code? Open in Web Editor NEW
3.5K 3.5K 382.0 982 KB

A quick and easy way to setup a RESTful JSON API

Home Page: https://ant0ine.github.io/go-json-rest/

License: MIT License

Go 100.00%

go-json-rest's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-json-rest's Issues

Provide ErrorHandler helpers ?

standard helpers from the net/http package are usable:

http.Error(w, err.Error(), 500)
http.NotFound(w, r.Request)

But helpers specialized for JSON REST could be better.

something that returns:

{"error":"resource not found"}

instead of:

Page not found

Path params should allow "."

The path params should allow a "."

Currently this site would break john.doe into two path params, and I think it should just be one: something.com/users/john.doe/updates

What is the best way to route to object methods instead of just function ?

Right now the endpoint function signatures look like that:

func Get(w *rest.ResponseWriter, r *rest.Request) 

what if I want to map to an object method ?

the current solution is:

handler.SetRoutes(
    rest.Route{
        "GET",
        "/countries/:code",
        func(w *rest.ResponseWriter, r *rest.Request) {
            country_handler.Get(w *rest.ResponseWriter, r *rest.Request) 
        }
    }
)

It could be easier !

Allow setting the HTTP status

It'd be nice if there was a way to access the http.Response through the ResponseWriter or otherwise a way to set the http StatusCode for the response.

Modify rest.Error Output?

Heyo! I'm sort of a go newbie, so hopefully this isn't a stupid question -- but how could I go about changing the rest.Error(...) JSON payload?

Right now, if I use rest.Error it returns a JSON dict that looks like this:

{"Error": "..."}

I'd like to make the "Error" string lowercase ("error") to be more consistent with the rest of my API.

Thanks!

Tests

What is the common practice in Go to write net/http tests ?

Missing way to return a JSON with a non-ok code

When an object is POSTed to be added some advocate returning a "201 Created" response. It is currently not possible to do so with go-json-rest as Error only returns a string (and we are not returning a real error) and WriteJson doesn't allow to set a response code and will always return 200 for us. It is also not possible to set the error before calling WriteJson since then the headers are sent and the attempt to set the header in WriteJson will fail.

Indent could add a newline to the response

Currently the output with indentation doesn't include a final newline, this means that when I get the response with curl the command line starts after the response and is slightly messed up.

It is trivial to handle it at the command line by adding an echo command after curl but the point of indentation is to make things completely readable and easy on the api user. Hence it would be nice to add the newline. It might be nice to do it for the non-indented output too.

Have an option to not default a Content-Type response writer header.

Can there be an option to not default a response writer's Content-Type header? In your code, you force call WriteHeader on Write with your implementation of WriteHeader always adding an "application/json" Content-Type: https://github.com/ant0ine/go-json-rest/blob/master/rest/response.go#L61.

I realize the package is meant for json only, but there are some instances where it'd be nice to be able to define a certain route or subroutes as non-JSON. For example, it'd be nice to do:

func handlepprof(w rest.ResponseWriter, r *rest.Request) {
        switch {
                case strings.HasPrefix(r.URL.Path, "/debug/pprof/cmdline"):
                        pprof.Cmdline(w.(http.ResponseWriter), r.Request)
                case strings.HasPrefix(r.URL.Path, "/debug/pprof/profile"):
                        pprof.Profile(w.(http.ResponseWriter), r.Request)
                case strings.HasPrefix(r.URL.Path, "/debug/pprof/symbol"):
                        pprof.Symbol(w.(http.ResponseWriter), r.Request)
                default: // debug/pprof base
                        pprof.Index(w.(http.ResponseWriter), r.Request)
        }
}

with a route {"GET", "/debug/*", handlepprof},

and view the net/http/pprof output in elinks. This could be done with another option in the ResourceHandler type, such as EnableRelaxedResponseType similar to the current EnableRelaxedContentType. Then your WriteHeader could simply not write the Content-Type if this option is set.

What do you think?

arguments in path

Is there a way to define arguments in path? For example:

GIven the url : http://lcoalhost/user?name=John

rest.RouteObjectMethod("GET", "/user", &user, "Find")

func (u *User) Find(w *rest.ResponseWriter, req *rest.Request) {
name := req.PathParam("name") // name = John
...
}

Query parameters show up in the PathParams

a path like
"/page/:id"

and a url like

/page/bmw-330?p=1

results in a r.PathParam("id") == "bmw-330?p=1"

Shouldn't the routes be based purely on the paths, so the id would be "bmw-330"

If so then the fix is easy
In router.go
if line 95 was changed from ...

urlObj.RequestURI()
to
urlObj.Path

About url route matching problem

Hello, Antoine.

I have a problem about url route match.

First handler.SetRoutes

// route matching have problem, don't work.
        handler.SetRoutes(
                rest.Route{"GET", "/book/:id", GetBookFromBookId},
                rest.Route{"GET", "/book/isbn/:isbn", GetBookFromBookISBN},
                rest.Route{"GET", "/book/search", GetBookListFromKeyword},
        )

Second handler.SetRoutes

// route matching have no problem, work fine
        handler.SetRoutes(
                rest.Route{"GET", "/book/search", GetBookListFromKeyword},
                rest.Route{"GET", "/book/:id", GetBookFromBookId},
                rest.Route{"GET", "/book/isbn/:isbn", GetBookFromBookISBN},
        )

First handler.SetRoutes has a problem is
GET /book/10112011
GetBookFromBookId handle it.
GET /book/search?q=Java
GetBookFromBookId handle it, so the rest server return not found error.

Second handler.SetRoutes work fine.
GET /book/10112011
GetBookFromBookId handle it.
GET /book/search?q=Java
GetBookListFromKeyword handle it.

Export router

If I understand correctly, you abandoned the go-urlrouter package and its code continues to evolve here.

While this package is great for its use case, I think, it is unfortunate, that now the router is unusable by itself for people who use their own resource handlers.

Thus, could you make the router available again by exporting its values and methods just like in go-urlrouter.

Thanks. Great work.

Non working example

https://github.com/ant0ine/go-json-rest#gorm

handler.SetRoutes(
    &rest.RouteObjectMethod("GET", "/reminders", &api, "GetAllReminders"),
    &rest.RouteObjectMethod("POST", "/reminders", &api, "PostReminder"),
    &rest.RouteObjectMethod("GET", "/reminders/:id", &api, "GetReminder"),
    &rest.RouteObjectMethod("PUT", "/reminders/:id", &api, "PutReminder"),
    &rest.RouteObjectMethod("DELETE", "/reminders/:id", &api, "DeleteReminder"),
)

This does not compile:

./main.go:22: cannot take the address of rest.RouteObjectMethod("GET", "/reminders", &api, "GetAllReminders")
./main.go:22: cannot use &rest.RouteObjectMethod("GET", "/reminders", &api, "GetAllReminders") (type **rest.Route) as type *rest.Route in argument to handler.SetRoutes

Removing & solves it.

Should other encodings be allowed?

This project is called go-json-rest.
Is there some design decision made to supprt only json? I mean, changing encoding to xml is as easy as importing encoding/xml.
What is the attitude about including also xml encoding?

Check that Route.PathExp starts with '/'

I made a mistake:

rest.RouteObjectMethod("GET", "admin/products.json", &api, "GetAllProducts")

Which caused a cryptic panic message:

2014/07/04 09:45:14 http: panic serving 127.0.0.1:43177: runtime error: invalid memory address or nil pointer dereference
goroutine 39 [running]:
net/http.func·011()
        /usr/local/go/src/pkg/net/http/server.go:1100 +0xb7
runtime.panic(0x732e20, 0x943f53)
        /usr/local/go/src/pkg/runtime/panic.c:248 +0x18d
github.com/ant0ine/go-json-rest/rest.(*ResourceHandler).ServeHTTP(0xc208005320, 0x7f4d1449ec28, 0xc208050320, 0xc20802a340)
        /home/vic/projects/go/src/github.com/ant0ine/go-json-rest/rest/handler.go:225 +0x3f
net/http.serverHandler.ServeHTTP(0xc208005380, 0x7f4d1449ec28, 0xc208050320, 0xc20802a340)
        /usr/local/go/src/pkg/net/http/server.go:1673 +0x19f
net/http.(*conn).serve(0xc20804e180)
        /usr/local/go/src/pkg/net/http/server.go:1174 +0xa7e
created by net/http.(*Server).Serve
        /usr/local/go/src/pkg/net/http/server.go:1721 +0x313

It turned out, I forgot to start the path with /. It would be nice to ensure that the path is valid.

Allow Multiple SetRoutes Calls

I have some optional bits of code in my app that enable or disable groups or routes dynamically. To realize this, I have to make my plugins generate lists of routes everywhere and then concatenate them in my initialization code.

While I can easily continue to maintain lists of routes (right now, I build a global list), I'd much rather consider the handler to be the "container" of the routes (much like net/http does with a ServeMux and handlers). This is pretty trivial, and I'd gladly write it if you'll accept the patch.

logging examples?

Hi!

Ive been starting to use go-json-rest and thanks Antoine for the great API!

But I would like to know how I can easier can control the logging, for example to add a new custom key/value to the json logger. It seems also that the "remoteuser" IP is not working anywhere. Some examples on how to make some custom logger would be great, or how to separate access logs from error logs (apache style).

gzip support

The ResourceHandler should support transparent gzip encoding, configured by a the flag EnableGzip bool in the struct.

route for dotted string

I specified my route like this:
rest.RouteObjectMethod("GET", "/ip/:ip", &api, "IpGet"),

This route works for this url /ip/ab.

But not for /ip/a.b (dotted string).

How to create route for dotted string?

Thanks.

API change to introduce rest.Helper ?

In order to be as close as possible to the standard net/http, here is a possible API change:

Instead of subclassing http.Request and http.ResponseWriter, the framework can provide a third object rest.Helper

The signature of a endpoint method would be:

func Get(w http.ResponseWriter, r http.Request, h rest.Helper) {

}

Use of standard net/http methods would be very easy, example:

NotFound(w, r) // unchanged

The implementation would look like this:

type Helper struct {
    w http.ResponseWriter
    r http.Request
    PathParams map[string]string
}

func (self *Helper) WriteJson(v interface{}) error {

}

func (self *Helper) PathParam(name string) string {

}

func (self *Helper) DecodeJsonPayload(v interface{}) error {

}

PathParam with a dot returns 404

If route is &Route{"GET", "/r/:id/", ...}

Having a dot in the path param returns a 404

$ curl "http://localhost:8080/r/123.456/"

I can submit a pull request if you can point me in the right direction on how to fix this please.

Make the framework supports CORS

Goal

CORS is now essential to any REST framework.
If it's possible to implement it right on top of Go-Json-Rest, but it is a lot of work.
The framework should make that easy.

Problems

  1. How to serve the OPTIONS method requests (Preflight requests) for all the endpoints ?
  2. How to make it easy to send the same headers over and over again in each endpoint ?
  3. How to let the user enter some logic (code) to determine the CORS headers for each request. (necessary to workaround some CORS limitations (find ref about "credentials"))
  4. Make it possible to reject all the requests without the Origin header.

Ideas

for 1 serving the OPTIONS requests
a) intercept all OPTIONS requests and respond
+ simple
- sucks to not have any 404 on OPTIONS
b) loop over the Routes and create the OPTIONS routes in the router
- additional complexity
c) use the pathMatched result from the router
+ already there
+ no additional router complexity
+ still all the 404
=> lets go with c)

for 2, 3, 4, code idea

type CorsHeaders struct {
AccessControlAllowOrigin string
AccessControlAllowCredentials string
AccessControlExposeHeaders string
AccessControlMaxAge string
AccessControlAllowMethods string
AccessControlAllowHeaders string
}

type ResourceHandler struct {
...
// if nil is returned an error response is created to reject the request
EnableCors func(request *Request) *CorsHeaders
}

// in ServeHTTP
if pathMatched {
if method == OPTIONS {
ServePreFlightHTTP
...
}

func ServePreFlightHTTP {
// CORS is enabled, just do nothing, and let the responseWriter
// write the cors headers
}

Links:
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control
http://www.w3.org/TR/cors/

Provide a way to control the access to /.status ?

This feature is disabled by default. It is useful mostly in PROD. But you probably don't want to share these info with the world.

Should this framework provide a way to enable AND restrict the access to /.status ?
Or is it something that should be handled outside ?

Runtime error with vhost routes

Including a vhost (hostname and port) in the routes results in a nil pointer dereference when requests, any request, arrive. For example:

package main

import (
        "net/http"
        "github.com/ant0ine/go-json-rest"
)

type User struct {
        Id   string
        Name string
}

func GetUser(w *rest.ResponseWriter, req *rest.Request) {
        user := User{
                Id:   req.PathParam("id"),
                Name: "Antoine",
        }
        w.WriteJson(&user)
}

func main() {
        handler := rest.ResourceHandler{}
        handler.SetRoutes(
                rest.Route{"GET", "/users/:id", GetUser},
                rest.Route{"GET", "europa.loc:8080/people/:id", GetUser},
        )
        http.ListenAndServe(":8080", &handler)
}

results in:

2014/02/18 16:14:02 http: panic serving 127.0.0.1:41373: runtime error: invalid memory address or nil pointer dereference
goroutine 3 [running]:
net/http.func·009()
        /usr/local/go/src/pkg/net/http/server.go:1093 +0xae
runtime.panic(0x629c80, 0x8f02c8)
        /usr/local/go/src/pkg/runtime/panic.c:248 +0x106
github.com/ant0ine/go-json-rest.(*env).setVar(0x0, 0xc210059270, 0x68a470, 0xa, 0x5cade0, ...)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/env.go:15 +0x186
github.com/ant0ine/go-json-rest.func·006(0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/recorder.go:36 +0xdc
github.com/ant0ine/go-json-rest.func·009(0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/timer.go:14 +0x83
github.com/ant0ine/go-json-rest.func·007(0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/status.go:15 +0x4b
github.com/ant0ine/go-json-rest.func·001(0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/gzip.go:42 +0x155
github.com/ant0ine/go-json-rest.func·005(0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/log.go:45 +0x51
github.com/ant0ine/go-json-rest.(*ResourceHandler).ServeHTTP(0xc2100379c0, 0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /home/vagrant/go/ext/src/github.com/ant0ine/go-json-rest/handler.go:254 +0xc6
net/http.serverHandler.ServeHTTP(0xc21001e730, 0x7f0db8501500, 0xc21000f5a0, 0xc210059270)
        /usr/local/go/src/pkg/net/http/server.go:1597 +0x16e
net/http.(*conn).serve(0xc210058280)
        /usr/local/go/src/pkg/net/http/server.go:1167 +0x7b7
created by net/http.(*Server).Serve
        /usr/local/go/src/pkg/net/http/server.go:1644 +0x28b

when you access it via curl:

curl -X GET http://europa.loc:8080/people/bob

Package name breaks Go convention

github.com/ant0ine/go-json-rest is the import path, but the package name is rest. This breaks the following convention:

Go's convention is that the package name is the last element of the import path: the package imported as "crypto/rot13" should be named rot13.

A particular fallout of this is that the handy goimports tool cannot be used with code employing go-json-rest, as it sees no uses of the go-json-rest package and removes the import.

Does not support filtering with "?" plus parameters at end of REST URL

Filtering in REST is usually done by adding parameters at the end of the URL with "?foo=bar&a=b"
Example: http://company.com/person?age=65&sex=male
Reference: http://stackoverflow.com/a/8807065/459050

This does not seem to work:

I have tried:
&rest.Route{"GET", "/person?age=:age", GetPersonsByAge},
But this causes a panic.

I have tried:
&rest.Route{"GET", "/person?age=:age", GetFilterPersons},
Does not cause a panic, but in the Handler req.PathParam("age") is always nil for URL: /person?age=42

Empty arrays and maps are written as null

The following returns null when the endpoint is called:

func (t *WebAppData) testRest(w rest.ResponseWriter, r *rest.Request) {
    var array []int
    if err := w.WriteJson(&array); err != nil {
        log.Printf("Failed to write json: %v", err)
    }
    return
}

It shouldn't - it should return []. Similarly, an initialized map should return {} instead of null.

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.