Improve Request to be more idiomatic

This commit is contained in:
Frédéric Guillot 2017-11-21 18:14:45 -08:00
parent 4fc18647ca
commit 25cbd65777
20 changed files with 80 additions and 77 deletions

View file

@ -12,7 +12,7 @@ import (
// CreateCategory is the API handler to create a new category.
func (c *Controller) CreateCategory(ctx *core.Context, request *core.Request, response *core.Response) {
category, err := payload.DecodeCategoryPayload(request.GetBody())
category, err := payload.DecodeCategoryPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -35,13 +35,13 @@ func (c *Controller) CreateCategory(ctx *core.Context, request *core.Request, re
// UpdateCategory is the API handler to update a category.
func (c *Controller) UpdateCategory(ctx *core.Context, request *core.Request, response *core.Response) {
categoryID, err := request.GetIntegerParam("categoryID")
categoryID, err := request.IntegerParam("categoryID")
if err != nil {
response.Json().BadRequest(err)
return
}
category, err := payload.DecodeCategoryPayload(request.GetBody())
category, err := payload.DecodeCategoryPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -77,7 +77,7 @@ func (c *Controller) GetCategories(ctx *core.Context, request *core.Request, res
// RemoveCategory is the API handler to remove a category.
func (c *Controller) RemoveCategory(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
categoryID, err := request.GetIntegerParam("categoryID")
categoryID, err := request.IntegerParam("categoryID")
if err != nil {
response.Json().BadRequest(err)
return

View file

@ -14,13 +14,13 @@ import (
// GetEntry is the API handler to get a single feed entry.
func (c *Controller) GetEntry(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
}
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Json().BadRequest(err)
return
@ -47,13 +47,13 @@ func (c *Controller) GetEntry(ctx *core.Context, request *core.Request, response
// GetFeedEntries is the API handler to get all feed entries.
func (c *Controller) GetFeedEntries(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
}
status := request.GetQueryStringParam("status", "")
status := request.QueryStringParam("status", "")
if status != "" {
if err := model.ValidateEntryStatus(status); err != nil {
response.Json().BadRequest(err)
@ -61,20 +61,20 @@ func (c *Controller) GetFeedEntries(ctx *core.Context, request *core.Request, re
}
}
order := request.GetQueryStringParam("order", "id")
order := request.QueryStringParam("order", "id")
if err := model.ValidateEntryOrder(order); err != nil {
response.Json().BadRequest(err)
return
}
direction := request.GetQueryStringParam("direction", "desc")
direction := request.QueryStringParam("direction", "desc")
if err := model.ValidateDirection(direction); err != nil {
response.Json().BadRequest(err)
return
}
limit := request.GetQueryIntegerParam("limit", 100)
offset := request.GetQueryIntegerParam("offset", 0)
limit := request.QueryIntegerParam("limit", 100)
offset := request.QueryIntegerParam("offset", 0)
builder := c.store.GetEntryQueryBuilder(userID, ctx.GetUserTimezone())
builder.WithFeedID(feedID)
@ -103,19 +103,19 @@ func (c *Controller) GetFeedEntries(ctx *core.Context, request *core.Request, re
func (c *Controller) SetEntryStatus(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
}
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Json().BadRequest(err)
return
}
status, err := payload.DecodeEntryStatusPayload(request.GetBody())
status, err := payload.DecodeEntryStatusPayload(request.Body())
if err != nil {
response.Json().BadRequest(errors.New("Invalid JSON payload"))
return

View file

@ -13,7 +13,7 @@ import (
// CreateFeed is the API handler to create a new feed.
func (c *Controller) CreateFeed(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedURL, categoryID, err := payload.DecodeFeedCreationPayload(request.GetBody())
feedURL, categoryID, err := payload.DecodeFeedCreationPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -31,7 +31,7 @@ func (c *Controller) CreateFeed(ctx *core.Context, request *core.Request, respon
// RefreshFeed is the API handler to refresh a feed.
func (c *Controller) RefreshFeed(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
@ -49,13 +49,13 @@ func (c *Controller) RefreshFeed(ctx *core.Context, request *core.Request, respo
// UpdateFeed is the API handler that is used to update a feed.
func (c *Controller) UpdateFeed(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
}
newFeed, err := payload.DecodeFeedModificationPayload(request.GetBody())
newFeed, err := payload.DecodeFeedModificationPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -95,7 +95,7 @@ func (c *Controller) GetFeeds(ctx *core.Context, request *core.Request, response
// GetFeed is the API handler to get a feed.
func (c *Controller) GetFeed(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return
@ -118,7 +118,7 @@ func (c *Controller) GetFeed(ctx *core.Context, request *core.Request, response
// RemoveFeed is the API handler to remove a feed.
func (c *Controller) RemoveFeed(ctx *core.Context, request *core.Request, response *core.Response) {
userID := ctx.GetUserID()
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Json().BadRequest(err)
return

View file

@ -14,7 +14,7 @@ import (
// GetSubscriptions is the API handler to find subscriptions.
func (c *Controller) GetSubscriptions(ctx *core.Context, request *core.Request, response *core.Response) {
websiteURL, err := payload.DecodeURLPayload(request.GetBody())
websiteURL, err := payload.DecodeURLPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return

View file

@ -17,7 +17,7 @@ func (c *Controller) CreateUser(ctx *core.Context, request *core.Request, respon
return
}
user, err := payload.DecodeUserPayload(request.GetBody())
user, err := payload.DecodeUserPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -50,13 +50,13 @@ func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, respon
return
}
userID, err := request.GetIntegerParam("userID")
userID, err := request.IntegerParam("userID")
if err != nil {
response.Json().BadRequest(err)
return
}
user, err := payload.DecodeUserPayload(request.GetBody())
user, err := payload.DecodeUserPayload(request.Body())
if err != nil {
response.Json().BadRequest(err)
return
@ -110,7 +110,7 @@ func (c *Controller) GetUser(ctx *core.Context, request *core.Request, response
return
}
userID, err := request.GetIntegerParam("userID")
userID, err := request.IntegerParam("userID")
if err != nil {
response.Json().BadRequest(err)
return
@ -137,7 +137,7 @@ func (c *Controller) RemoveUser(ctx *core.Context, request *core.Request, respon
return
}
userID, err := request.GetIntegerParam("userID")
userID, err := request.IntegerParam("userID")
if err != nil {
response.Json().BadRequest(err)
return

View file

@ -15,36 +15,34 @@ import (
"github.com/gorilla/mux"
)
// Request is a thin wrapper around "http.Request".
type Request struct {
writer http.ResponseWriter
request *http.Request
}
func (r *Request) GetRequest() *http.Request {
// Request returns the raw Request struct.
func (r *Request) Request() *http.Request {
return r.request
}
func (r *Request) GetBody() io.ReadCloser {
// Body returns the request body.
func (r *Request) Body() io.ReadCloser {
return r.request.Body
}
func (r *Request) GetHeaders() http.Header {
return r.request.Header
}
func (r *Request) GetScheme() string {
return r.request.URL.Scheme
}
func (r *Request) GetFile(name string) (multipart.File, *multipart.FileHeader, error) {
// File returns uploaded file properties.
func (r *Request) File(name string) (multipart.File, *multipart.FileHeader, error) {
return r.request.FormFile(name)
}
// IsHTTPS returns if the request is made over HTTPS.
func (r *Request) IsHTTPS() bool {
return r.request.URL.Scheme == "https"
}
func (r *Request) GetCookie(name string) string {
// Cookie returns the cookie value.
func (r *Request) Cookie(name string) string {
cookie, err := r.request.Cookie(name)
if err == http.ErrNoCookie {
return ""
@ -53,7 +51,8 @@ func (r *Request) GetCookie(name string) string {
return cookie.Value
}
func (r *Request) GetIntegerParam(param string) (int64, error) {
// IntegerParam returns an URL parameter as integer.
func (r *Request) IntegerParam(param string) (int64, error) {
vars := mux.Vars(r.request)
value, err := strconv.Atoi(vars[param])
if err != nil {
@ -68,7 +67,8 @@ func (r *Request) GetIntegerParam(param string) (int64, error) {
return int64(value), nil
}
func (r *Request) GetStringParam(param, defaultValue string) string {
// StringParam returns an URL parameter as string.
func (r *Request) StringParam(param, defaultValue string) string {
vars := mux.Vars(r.request)
value := vars[param]
if value == "" {
@ -77,7 +77,8 @@ func (r *Request) GetStringParam(param, defaultValue string) string {
return value
}
func (r *Request) GetQueryStringParam(param, defaultValue string) string {
// QueryStringParam returns a querystring parameter as string.
func (r *Request) QueryStringParam(param, defaultValue string) string {
value := r.request.URL.Query().Get(param)
if value == "" {
value = defaultValue
@ -85,7 +86,8 @@ func (r *Request) GetQueryStringParam(param, defaultValue string) string {
return value
}
func (r *Request) GetQueryIntegerParam(param string, defaultValue int) int {
// QueryIntegerParam returns a querystring parameter as string.
func (r *Request) QueryIntegerParam(param string, defaultValue int) int {
value := r.request.URL.Query().Get(param)
if value == "" {
return defaultValue
@ -103,6 +105,7 @@ func (r *Request) GetQueryIntegerParam(param string, defaultValue int) int {
return val
}
// NewRequest returns a new Request struct.
func NewRequest(w http.ResponseWriter, r *http.Request) *Request {
return &Request{writer: w, request: r}
}

View file

@ -38,7 +38,7 @@ func (c *Controller) ShowCategories(ctx *core.Context, request *core.Request, re
// ShowCategoryEntries shows all entries for the given category.
func (c *Controller) ShowCategoryEntries(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
offset := request.GetQueryIntegerParam("offset", 0)
offset := request.QueryIntegerParam("offset", 0)
args, err := c.getCommonTemplateArgs(ctx)
if err != nil {
@ -102,7 +102,7 @@ func (c *Controller) SaveCategory(ctx *core.Context, request *core.Request, resp
return
}
categoryForm := form.NewCategoryForm(request.GetRequest())
categoryForm := form.NewCategoryForm(request.Request())
if err := categoryForm.Validate(); err != nil {
response.Html().Render("create_category", args.Merge(tplParams{
"errorMessage": err.Error(),
@ -165,7 +165,7 @@ func (c *Controller) UpdateCategory(ctx *core.Context, request *core.Request, re
return
}
categoryForm := form.NewCategoryForm(request.GetRequest())
categoryForm := form.NewCategoryForm(request.Request())
args, err := c.getCategoryFormTemplateArgs(ctx, user, category, categoryForm)
if err != nil {
response.Html().ServerError(err)
@ -216,7 +216,7 @@ func (c *Controller) RemoveCategory(ctx *core.Context, request *core.Request, re
}
func (c *Controller) getCategoryFromURL(ctx *core.Context, request *core.Request, response *core.Response) (*model.Category, error) {
categoryID, err := request.GetIntegerParam("categoryID")
categoryID, err := request.IntegerParam("categoryID")
if err != nil {
response.Html().BadRequest(err)
return nil, err

View file

@ -18,13 +18,13 @@ func (c *Controller) ShowFeedEntry(ctx *core.Context, request *core.Request, res
user := ctx.GetLoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Html().BadRequest(err)
return
}
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Html().BadRequest(err)
return
@ -111,13 +111,13 @@ func (c *Controller) ShowCategoryEntry(ctx *core.Context, request *core.Request,
user := ctx.GetLoggedUser()
sortingDirection := model.DefaultSortingDirection
categoryID, err := request.GetIntegerParam("categoryID")
categoryID, err := request.IntegerParam("categoryID")
if err != nil {
response.Html().BadRequest(err)
return
}
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Html().BadRequest(err)
return
@ -205,7 +205,7 @@ func (c *Controller) ShowUnreadEntry(ctx *core.Context, request *core.Request, r
user := ctx.GetLoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Html().BadRequest(err)
return
@ -292,7 +292,7 @@ func (c *Controller) ShowReadEntry(ctx *core.Context, request *core.Request, res
user := ctx.GetLoggedUser()
sortingDirection := model.DefaultSortingDirection
entryID, err := request.GetIntegerParam("entryID")
entryID, err := request.IntegerParam("entryID")
if err != nil {
response.Html().BadRequest(err)
return
@ -369,7 +369,7 @@ func (c *Controller) ShowReadEntry(ctx *core.Context, request *core.Request, res
func (c *Controller) UpdateEntriesStatus(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
entryIDs, status, err := payload.DecodeEntryStatusPayload(request.GetBody())
entryIDs, status, err := payload.DecodeEntryStatusPayload(request.Body())
if err != nil {
log.Println(err)
response.Json().BadRequest(nil)

View file

@ -39,7 +39,7 @@ func (c *Controller) ShowFeedsPage(ctx *core.Context, request *core.Request, res
// ShowFeedEntries shows all entries for the given feed.
func (c *Controller) ShowFeedEntries(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
offset := request.GetQueryIntegerParam("offset", 0)
offset := request.QueryIntegerParam("offset", 0)
args, err := c.getCommonTemplateArgs(ctx)
if err != nil {
@ -108,7 +108,7 @@ func (c *Controller) UpdateFeed(ctx *core.Context, request *core.Request, respon
return
}
feedForm := form.NewFeedForm(request.GetRequest())
feedForm := form.NewFeedForm(request.Request())
args, err := c.getFeedFormTemplateArgs(ctx, user, feed, feedForm)
if err != nil {
response.Html().ServerError(err)
@ -136,7 +136,7 @@ func (c *Controller) UpdateFeed(ctx *core.Context, request *core.Request, respon
// RemoveFeed delete a subscription from the database and redirect to the list of feeds page.
func (c *Controller) RemoveFeed(ctx *core.Context, request *core.Request, response *core.Response) {
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Html().ServerError(err)
return
@ -153,7 +153,7 @@ func (c *Controller) RemoveFeed(ctx *core.Context, request *core.Request, respon
// RefreshFeed refresh a subscription and redirect to the feed entries page.
func (c *Controller) RefreshFeed(ctx *core.Context, request *core.Request, response *core.Response) {
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Html().BadRequest(err)
return
@ -168,7 +168,7 @@ func (c *Controller) RefreshFeed(ctx *core.Context, request *core.Request, respo
}
func (c *Controller) getFeedFromURL(request *core.Request, response *core.Response, user *model.User) (*model.Feed, error) {
feedID, err := request.GetIntegerParam("feedID")
feedID, err := request.IntegerParam("feedID")
if err != nil {
response.Html().BadRequest(err)
return nil, err

View file

@ -12,7 +12,7 @@ import (
// ShowHistoryPage renders the page with all read entries.
func (c *Controller) ShowHistoryPage(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
offset := request.GetQueryIntegerParam("offset", 0)
offset := request.QueryIntegerParam("offset", 0)
args, err := c.getCommonTemplateArgs(ctx)
if err != nil {

View file

@ -10,7 +10,7 @@ import (
)
func (c *Controller) ShowIcon(ctx *core.Context, request *core.Request, response *core.Response) {
iconID, err := request.GetIntegerParam("iconID")
iconID, err := request.IntegerParam("iconID")
if err != nil {
response.Html().BadRequest(err)
return

View file

@ -26,7 +26,7 @@ func (c *Controller) ShowLoginPage(ctx *core.Context, request *core.Request, res
}
func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, response *core.Response) {
authForm := form.NewAuthForm(request.GetRequest())
authForm := form.NewAuthForm(request.Request())
tplParams := tplParams{
"errorMessage": "Invalid username or password.",
"csrf": ctx.GetCsrfToken(),
@ -46,8 +46,8 @@ func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, respon
sessionToken, err := c.store.CreateSession(
authForm.Username,
request.GetHeaders().Get("User-Agent"),
realip.RealIP(request.GetRequest()),
request.Request().UserAgent(),
realip.RealIP(request.Request()),
)
if err != nil {
response.Html().ServerError(err)
@ -71,7 +71,7 @@ func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, respon
func (c *Controller) Logout(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
sessionCookie := request.GetCookie("sessionID")
sessionCookie := request.Cookie("sessionID")
if err := c.store.RemoveSessionByToken(user.ID, sessionCookie); err != nil {
log.Printf("[UI:Logout] %v", err)
}

View file

@ -34,7 +34,7 @@ func (c *Controller) Import(ctx *core.Context, request *core.Request, response *
}
func (c *Controller) UploadOPML(ctx *core.Context, request *core.Request, response *core.Response) {
file, fileHeader, err := request.GetFile("file")
file, fileHeader, err := request.File("file")
if err != nil {
log.Println(err)
response.Redirect(ctx.GetRoute("import"))

View file

@ -16,7 +16,7 @@ import (
)
func (c *Controller) ImageProxy(ctx *core.Context, request *core.Request, response *core.Response) {
encodedURL := request.GetStringParam("encodedURL", "")
encodedURL := request.StringParam("encodedURL", "")
if encodedURL == "" {
response.Html().BadRequest(errors.New("No URL provided"))
return

View file

@ -23,7 +23,7 @@ func (c *Controller) ShowSessions(ctx *core.Context, request *core.Request, resp
return
}
sessionCookie := request.GetCookie("sessionID")
sessionCookie := request.Cookie("sessionID")
response.Html().Render("sessions", args.Merge(tplParams{
"sessions": sessions,
"currentSessionToken": sessionCookie,
@ -34,7 +34,7 @@ func (c *Controller) ShowSessions(ctx *core.Context, request *core.Request, resp
func (c *Controller) RemoveSession(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
sessionID, err := request.GetIntegerParam("sessionID")
sessionID, err := request.IntegerParam("sessionID")
if err != nil {
response.Html().BadRequest(err)
return

View file

@ -27,7 +27,7 @@ func (c *Controller) ShowSettings(ctx *core.Context, request *core.Request, resp
func (c *Controller) UpdateSettings(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
settingsForm := form.NewSettingsForm(request.GetRequest())
settingsForm := form.NewSettingsForm(request.Request())
args, err := c.getSettingsFormTemplateArgs(ctx, user, settingsForm)
if err != nil {
response.Html().ServerError(err)

View file

@ -13,7 +13,7 @@ import (
)
func (c *Controller) Stylesheet(ctx *core.Context, request *core.Request, response *core.Response) {
stylesheet := request.GetStringParam("name", "white")
stylesheet := request.StringParam("name", "white")
body := static.Stylesheets["common"]
etag := static.StylesheetsChecksums["common"]

View file

@ -33,7 +33,7 @@ func (c *Controller) SubmitSubscription(ctx *core.Context, request *core.Request
return
}
subscriptionForm := form.NewSubscriptionForm(request.GetRequest())
subscriptionForm := form.NewSubscriptionForm(request.Request())
if err := subscriptionForm.Validate(); err != nil {
response.Html().Render("add_subscription", args.Merge(tplParams{
"form": subscriptionForm,
@ -89,7 +89,7 @@ func (c *Controller) ChooseSubscription(ctx *core.Context, request *core.Request
return
}
subscriptionForm := form.NewSubscriptionForm(request.GetRequest())
subscriptionForm := form.NewSubscriptionForm(request.Request())
if err := subscriptionForm.Validate(); err != nil {
response.Html().Render("add_subscription", args.Merge(tplParams{
"form": subscriptionForm,

View file

@ -12,7 +12,7 @@ import (
// ShowUnreadPage render the page with all unread entries.
func (c *Controller) ShowUnreadPage(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.GetLoggedUser()
offset := request.GetQueryIntegerParam("offset", 0)
offset := request.QueryIntegerParam("offset", 0)
builder := c.store.GetEntryQueryBuilder(user.ID, user.Timezone)
builder.WithStatus(model.EntryStatusUnread)

View file

@ -72,7 +72,7 @@ func (c *Controller) SaveUser(ctx *core.Context, request *core.Request, response
return
}
userForm := form.NewUserForm(request.GetRequest())
userForm := form.NewUserForm(request.Request())
if err := userForm.ValidateCreation(); err != nil {
response.Html().Render("create_user", args.Merge(tplParams{
"menu": "settings",
@ -153,7 +153,7 @@ func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, respon
return
}
userForm := form.NewUserForm(request.GetRequest())
userForm := form.NewUserForm(request.Request())
if err := userForm.ValidateModification(); err != nil {
response.Html().Render("edit_user", args.Merge(tplParams{
"menu": "settings",
@ -210,7 +210,7 @@ func (c *Controller) RemoveUser(ctx *core.Context, request *core.Request, respon
}
func (c *Controller) getUserFromURL(ctx *core.Context, request *core.Request, response *core.Response) (*model.User, error) {
userID, err := request.GetIntegerParam("userID")
userID, err := request.IntegerParam("userID")
if err != nil {
response.Html().BadRequest(err)
return nil, err