Go HTTP handler - handling more than two form actions - go

I have a webapp where I have code like:
func handler (w res, r req) {
if req.Method == POST {
// create html form 2 with a submit button2
return ;
}
// create html form 1 with a submit button1
return
}
func main() {
handle("/", handler)
}
Now, the root / is registered with the handler func. In the first request (GET), I create a form and send it to the user. If the user submits this form, I handle it under the "POST" method. Now, I create a different form in the post method handler, and now I want a way to do some operations based on what the user typed in this form2, when [s]he submits form2.
What is the standard way with go to handle the form2 form submission ? I have done some asp programming earlier and we use form action to submit to a different asp file. How can I do some actions based on parsing the form2 submission request ?

If I'm understanding you correctly, you want a way of routing the same URL to different handlers based on the request method rather than just the path? If that's the case...
For comparison, using Python + Django, the way you're doing this is pretty standard:
def my_django_view(request):
if request.method == "POST":
try_to_process_posted_data()
elif request.method == "GET":
show_a_form_to_user()
If you are trying to do fancier things like URL routing based on path and request method (GET, POST, DELETE, ...), then you might be interested in something like Gorilla.mux
It provides some friendly URL routing methods:
func main() {
router := mux.NewRouter()
router.HandleFunc("/", YourGETHandlerFunc).Methods("GET")
router.HandleFunc("/", YourPOSTHandlerFunc).Methods("POST")
http.Handle("/", router)
}
If you're looking for more resources for web development...
Mango: http://paulbellamy.com/2011/05/introducing-mango/
Web.go: http://www.getwebgo.com/tutorial
Twister: https://github.com/garyburd/twister/blob/master/examples/hello/main.go

Related

Getting JWT data in Gorilla CustomLoggingHandler

I am using a custom logging handler in my Go web server like this:
func main() {
// ... Set up everything
router = mux.NewRouter()
router.Handle("/apilookup",
raven.Recoverer(
jwtMiddleware.Handler(
http.HandlerFunc(
doApiLookup)))).Methods("GET")
loggedRouter := handlers.CustomLoggingHandler(os.Stdout, router, writeLog)
http.ListenAndServe(listenAddr, loggedRouter)
}
In the writeLog function, I have made my own version of the Gorilla handlers.LoggingHandler, which logs a lot of additional information.
One thing I would like to do is log the user for authenticated requests. Users authenticate to this server using JWT (using the Authorization: Bearer ... header). I am using Auth0's go-jwt-middleware to parse the token and set its value in the Request's context.
I tried to log the user's email address (one of the claims in the JWT) like this, based on the middleware's documentation:
func writeLog(writer io.Writer, params handlers.LogFormatterParams) {
// ... SNIP
// If we can't identify the user
username := "-"
if userJwt := params.Request.Context().Value("user"); userJwt != nil {
claims := userJwt.(*jwt.Token).Claims.(*jwtClaims)
username = claims.Email
}
// ... SNIP
}
The problem is that username is always the initial value - and not the expected value from the JWT.
By adding log.Printf("%+v\n", params.Request.Context()) above the if, I see that the context doesn't actually contain the parsed JWT data here.
As far as I can tell, the reason this is not working is because the middleware creates a new Request with the updated context, so only middleware further down the chain can see it. Because the logging middleware is above the JWT middleware, it does not have that same context.
I know that I can re-parse the JWT in the logging handler (because I do have access to all the headers), but that seems like a lot of overhead for logging.
Is there a better way to do this that will allow me to have access to this data where I want it?

Go gin response middleware

I need to manipulate response data in a middleware function. Assume I have product handlers and customer handlers. Product handler returns a list of products and customer returns a list of customers. In the middleware function, I want to convert these responses into ApiResponse struct.
type ApiResponse struct{
Data interface{}
Status ApiStatus{}
}
func someMiddleware(c *gin.Context){
//before handlers
c.Next()
//I need to access response and manipulate it
// apiResponse := ApiResponse{}
// apiResponse.Data = returnedData
// apiResponse.Status = ApiStatus{}
}
I don't want to fill ApiResponse in all handlerFunctions.
Probably a bit too late, but anyway.
The easiest way is usually to use Get and Set methods of gin.Context to pass data between your middleware and your handlers.
But if you really need to intercept responses, see my answer about logging response in gin. The only difference is what you do with intercepted response, but everything said there about intercepting it stays true.

Render a template upon failed POST request [golang]

I want to handle the errors of a POST request and re-render the form with the errors displayed above it, but the only solution to handling errors I see is http.Error() but this returns a plaintext response, not an HTML page. Is there a way to executeTemplate() and re-render the html page with the form? Am I supposed to redirect the user to the same page? If so, how do I pass the error information to that redirected page?
Edit: So, when I use this code, and try to executeTemplate, the Post request returns a 200 status code (which is wrong) and it re-renders blank page, not the template I specified.
func PostSignup(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
return
}
usr := users.User{}
usr.Username = r.FormValue("username")
usr.Email = r.FormValue("email")
usr.Hash = r.FormValue("password")
errors := CredErrors{}
errors.Error = "Username cannot be blank"
if usr.Username == "" {
// http.Error(w, "Username cannot be blank.", 400)
tpl.ExecuteTemplate(w, "signup.gothml", errors)
return
}
The answer is in the question:
Is there a way to executeTemplate() and re-render the html page with the form?
Yes, use executeTemplate to re-render the form. http.Error() is for returning HTTP errors, not form validation errors. If the form fails validation and you want to redisplay it, do just that - render the form out to the browser again, with whatever validation errors/prepopulation/whatever you want to display.

Testing handlers that use Gorilla/context

I'm using gorilla/context in a web app. The example in the docs looks like:
func MyHandler(w http.ResponseWriter, r *http.Request) {
//...
val := context.Get(r, foo.MyKey)
//...
}
How can I unit test a handler that works like this? The only way I've managed so far is to use the context package inside my tests. I'm thinking at the moment that I could inject a context parameter into the handler but then I'm not conforming to the HandlerFunc interface.
This is a classic cross-cutting concerns example.
You are using a 3rd party to magically handle input params for your unit under test. By that very definition, you are going to have to do some extra setup to prep the context for the state you want.
When it comes to Go http handlers (which the convention is to KISS it), you shouldn't need to "reach out of context" of your func to get extra data: keep all the data you need within your single func.
Personally, I try to avoid corrupting my handlers like this. I think I've only used gorilla's context once out of the dozens of big sites I've built. And that was basically to get around a cached response, just to keep the data refesh to the end user. Of which I simply ignored in my unit tests, as it was out-of-scrope of what I was testing.
Instead, I use middle-ware wrappers to setup the basics I want in my handles and modify the handler's signature accordingly.
caching
logging
authentication and authorization
json marshaling
context (e.g. expected User{} object loaded from DB)
...etc. I would create a middle-ware that wraps your handler when you register it with mux that uses gorilla/context to lookup your cookie or userid or something, hidrates that user object from cache, DB, redis, etc, and then calls your handler that would have a signature like:
func MyHandler(u User, p Page, w http.ResponseWriter, r *http.Request) {
// u is the User object loaded from middle-ware
// p is your global Page object... etc
}
That way, your unit tests inject only the items you need under test.
And you can integration test your middle-ware on a QA server with expected User and Page objects in a datastore.
The way my team does it is to add a name to the route handler and then in the tests we call that route by name.
This is how to add a route:
r.HandleFunc("/<route>", MyHandler).Methods("GET").Name("MyHandlerByName")
Then this is how to test it
r.Get("MyHandlerByName")
One way to test handles is modify the way in which they are created. for example, Creating a function that return a http.HandlerFunc, this function can have parameters. You can mock the values that you send to the function
Without parameters
func State() http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code
})
}
With Parameters
func State(pParam1,pParam2,pParam3 ...) http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code using pParam1,pParam2,pParam3
})
}
The mapping will be
http.HandleFunc("/State", State())
or
http.HandleFunc("/State", State(value1,value2,value3 ....))

How to apply single page application functionality on parts of asp.NET MVC3 projects?

So I'm creating a asp.NET MVC3 application and want to apply single page application functionality to parts of the application. I think the easiest way to explain is with an example:
The app consists of an admin area and a public area and is built using ordinary link-structure. I want to convert the admin area to an single page application reusing view and models from the existing application. Is it possible to do this and in that case how?
You have to face two main problems, which makes the difference between SPA and standard application:
Links: In standard application, each link redirects you to a different page.
Forms: When a form is been submitted, a request is been issued with the HTTP method you've specified in the post (usually POST) and it contains in the payload the data the user has entered.
In order to solve that problems, you have to take action both in client-side and server-side.
For explaining propose, lets take the following code:
HomeController.cs:
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
public ActionResult Contact() {
return View(new ContactUsViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(ContactUsViewModel model) {
if (ModelState.IsValid) {
/* Send mail / Save in DB etc. */
return Redirect("Index");
}
return View(model);
}
}
Index.cshtml:
<p>This is a simple page.</p>
<p>#Html.ActionLink("Click here to contact us", "Contact")
Client-Side:
We should fix up linking between pages, as well as forms submittions.
Links: You can wire up an event in JS (jQuery if you'd like) that'll observe for each link click in the areas you'd like to apply on SPA - then, instead of redirecting the user - you'll load the content via AJAX.
For instance, take a look at this sample:
$("a").click(function(e) {
e.preventDefault(); // Disable standard redirecting
var href = $(e.currentTarget).attr("href");
$.get(href, function(responseText) {
$("#main-content-wrapper").html(responseText);
});
});
Forms: Just like in the approch we've used for links, we can wire up an observer to the form submit event in JS and then transfer the data using AJAX.
For instance:
$("form").submit(function(e) {
e.preventDefault(); // Disable standard submittion
var data = $(e.currentTarget).serialize(); // Serializing the form data
var method = $(e.currentTarget).attr("method");
if (typeof (method) == "undefined") { method = "POST"; }
$.ajax({
method: $(e.currentTarget).attr("method"),
parameters: data,
statusCodes: {
404: function() { /* Handle it somehow */ }
403: function() { /* Handle it... */ }
200: function(response) {
/* Since we've done a form submittion, usurally if we're getting standard OK (200) status code - we've transffered a data - such as JSON data - indicating if the request success or we got errors etc. The code you're writing here depends on how your current application works. */
},
});
});
Server-Side:
Since you don't wish to break your current application logic - you have to still be able to use standard ASP.NET MVC methods - such as View(), Redirect() and so on.
In this case, I recommend to create your own custom base Controller class - which will override ASP.NET basic implementation.
For instance, this is a starting point:
public class MyController : System.Web.Mvc.Controller {
public override View(string viewName) {
if (Request.IsAjaxRequest()) {
return PartialView(viewName); // If this is an AJAX request, we must return a PartialView.
}
return base.View(viewName);
}
}
Few things you have to keep in mind:
You have to somehow distinguish between standard requests and AJAX requests - the way I've used with Request.IsAjaxRequest() is a great way to do so.
Many times when you're handling a form, In the form submittion action, after you finish with the form logic, you're using Redirect() to redirect the user to another page. As you may have guessed, you can't take this approch when developing SPA. However, I can think of few solutions for this problem:
You can create a status handler in the JS code so when redirecting is been issued by the server - you can load the content via AJAX / display a message and so on.
You can override Redirect() and add a specific logic to perform in case of redirection when the request was done by AJAX - for instance, you can request from ASP.NET to perform the action that you're going to be transfered into and then return its content etc.
You can decide that although its an SPA app - when a redirect was issued - you allows the server to perform this redirection.
As you can see - there're many approches you can take, and they depends on the way you've developed your site, how you wish it to work and what is the basic rules you're defining (e.g. "No redirection is permitted never - even after submitting a form", "After form submittion - always in case that the operation success - I'm displaying a message or performing other JS action. Because of that, I can override Redirect() and if this is an AJAX request I can return a JSON object." etc.)

Resources