Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filters to modify request? #71

Closed
leehambley opened this issue Oct 26, 2013 · 6 comments
Closed

Filters to modify request? #71

leehambley opened this issue Oct 26, 2013 · 6 comments

Comments

@leehambley
Copy link

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 COMMITted, when the request errs it should be ROLLBACKed.

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.

@emicklei
Copy link
Owner

You can used request attributes for this purpose.
A restfult.Request has the following methods:

http://godoc.org/github.com/emicklei/go-restful#Request.SetAttribute
http://godoc.org/github.com/emicklei/go-restful#Request.Attribute

Attributes values are of type interface{} so you should cast to *sql.Tx after accesing it.

Let me know it this works for you.

@leehambley
Copy link
Author

Thanks for the confirmation @emicklei - that looks quite clean!

@stefreak
Copy link
Contributor

Wow, nice, I think we should document this better somehow.. I wanted to do this too, but ended up not finding a solution.

I think I'll try to write an example for this.

@leehambley
Copy link
Author

Here's some sample code from my app @stefreak:

I happened to import github.com/gorilla/context to do this, but now that I know it's possible without, I'll be refactoring it out.

//
// Extracts a TX from the request
//
func getRequestTx(r *restful.Request) *sql.Tx {
    if rv := context.Get(r.Request, RequestTxKey); rv != nil {
        return rv.(*sql.Tx)
    }
    return nil
}

//
// Used in testing to inject a TX which we can control
//
func InjectTx(tx *sql.Tx) restful.FilterFunction {
    return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
        // Inject the open Tx into the request, and hope that things
        // run smoothly.
        context.Set(req.Request, RequestTxKey, tx)
        // Launch the request up the stack
        chain.ProcessFilter(req, resp)
    }
}

//
// Used in the real world, to let the request drive the opening of a transaction
// for which we defer committing, or rolling back if things go wrong.
// This doesn't stop the handler committing, or rolling back the transaction, 
// it it would, then the deferred callback will simply do nothing.
//
func InjectDeferredDbTx(db *sql.DB) restful.FilterFunction {
    return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
        tx, err := db.Begin()
        if err != nil {
            panic(err)
        }
        fmt.Println("Handling Request, injecting a TX mofo")
        // Refer something to run later, we try and
        // catch any panics to stop the pq driver getting
        // into a weird internal state, and issue a ROLLBACK
        // if things went bad, otherwise commit the transaction
        // and go on with our lives.
        defer func(tx *sql.Tx) {
            if r := recover(); r != nil {
                tx.Rollback()
                panic(r)
            } else {
                tx.Commit()
            }
        }(tx)
        // Inject the open Tx into the request, and hope that things
        // run smoothly.
        context.Set(req.Request, RequestTxKey, tx)
        // Launch the request up the stack
        chain.ProcessFilter(req, resp)
    }
}

The usage in my handler is quite neat, too:

func (sch sessionCreateHandler) ServeHTTP(req *restful.Request, res *restful.Response) {

    tx := getRequestTx(req)
    userStore := stores.NewDbUserStore(tx, sch.c)
    sessionStore := stores.NewDbSessionStore(tx)

    params := new(createSessionParams)
    if err := req.ReadEntity(&params); err != nil {
        res.WriteHeader(http.StatusBadRequest)
        return
    }

}

@emicklei
Copy link
Owner

Can this issue be closed? I will create an example from your snippet that uses request attributes when time permits.

@leehambley
Copy link
Author

Yes, can absolutely be closed :-) perhaps if you have time (or if I do) we/you/I could contribute injecting something, and logging as examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants