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

Introduction of net/http concept #2254

Draft
wants to merge 57 commits into
base: main
Choose a base branch
from
Draft

Conversation

MikaeelMF
Copy link
Contributor

Hi!
I have written the client section for issue #2242 . I am creating a PR early and set it as a draft because I would like to hear ideas on various sections as I write each section.
I would be really happy if you could read it and let me know if any ideas or points come to your mind.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 16, 2022

Dear MikaeelMF

Thank you for contributing to the Go track on Exercism! 💙
You will see some automated feedback below 🤖. It would be great if you can make sure your PR covers those points. This will save your reviewer some time and your change can be merged quicker.

  • 📜 The following files usually contain very similar content.

    • concepts/<concept>/about.md
    • concepts/<concept>/introduction.md
    • exercises/concept/<exercise>/.docs/introduction.md

    Please check whether the changes you made to one of these also need to be applied to the others.

  • 🧦 If you changed the function signature or the function comment in the exemplar file or the stub file (<exercise>.go), make sure the change is applied to both files.

  • 🔗 If your PR fully fixes an issue, please include the text Fixes #issue_no in any line of the PR description. This will make the issue be automatically be closed when the PR is merged. If your PR is related to an existing issue but does not fix it completely, please link the issue anywhere in the description of the PR with #issue_no. You can read more about this in Github: Linking a pull request to an issue

  • ✍️ If your PR is not related to an existing issue (and is not self-explaining like a typo fix), please make sure the description explains why the change you made is necessary.

  • 🔤 If your PR fixes an easy to identify typo, if would be great if you could check for that typo in the whole repo. For example, if you found Unicdoe, use "replace all" in your editor (or command line magic) to fix it consistently.

Dear Reviewer/Maintainer

  • 📏 Make sure you set the appropriate x:size label for the PR. (This also works after merging, in case you forgot about it.)

  • 🔍 Don't be too nit-picky. If the PR is a clear improvement compared to the status quo, it should be approved as clear signal this is good to be merged even if the minor comments you might have are not addressed by the contributor. Further improvement ideas can be captured in issues (if important enough) and implemented via additional PRs.

  • 🤔 After reviewing the diff in the "Files changed" section, take a moment to think about whether there are changes missing from the diff. Does something need to be adjusted in other places so the code or content stays consistent?

Automated comment created by PR Commenter 🤖.

@MikaeelMF
Copy link
Contributor Author

Currently, I am planning to break the concept introduction into two major parts. One part explains how to create HTTP clients and another how to create HTTP servers.
Generally, I don't intend to get into too many details since this is only an introduction. Just enough so newcomers can get their hands dirty so they can read further upon the mentioned topics and for people who know about HTTP to get a little familiarized with what Go can offer using the net/http package.
For the first part, I've decided to explain how to use the three main methods (GET, HEAD, POST) as a client.
I don't have any more sections that I want to add to the client section so please let me know if you think anything can contribute to making this better.

For the server section, I have not really decided yet on what to include and what not to include. I am still doing some reading on this topic to further familiarize myself with it so I can decide on a good set of subsections to explain it well.

Also for the exercise, I am thinking about creating a backstory related to one of the previous exercises used in the roadmap but haven't really gotten into it since I first want to finish the concept introduction.

I will create a To-Do list to show the progress and will update it as I update this PR.

@MikaeelMF
Copy link
Contributor Author

MikaeelMF commented Jun 16, 2022

Concept introduction:

  • Client:
  • explain client.Get
  • explain client.Head
  • explain the need to close response body
  • explain client.Post
  • Server:
  • http.Server attributes (Addr, Handler, IdleTimeout, ReadHeaderTimeout, ReadTimeout)
  • Handlers
  • Multiplexers
  • Add the reference for the code snippet

@MikaeelMF MikaeelMF changed the title Add client section about net/http concept Introduction of net/http concept Jun 21, 2022
@MikaeelMF
Copy link
Contributor Author

Two things that I have left out are discussing Middleware and concurrency. Since middleware is a general concept and it can be written in form of handlers, I did not seem it necessary to include it in this introduction. Also on the concurrency, since it is a bit out of scope for this concept, I did not got into it.

@MikaeelMF MikaeelMF marked this pull request as ready for review June 25, 2022 13:22
@junedev
Copy link
Member

junedev commented Jun 28, 2022

@MikaeelMF I did not read the current content yet, I will need to find some more time for that. I just wanted to comment on what you wrote above regarding middleware and concurrency.

I agree middleware can be left out for now as there is no special support for this in plain Go anyway.

Regarding concurrency, I think it is important to explain in the server part that net/http creates a new Go routines for each incoming request and because of that it is totally fine to have synchronous/blocking code in an http handler. A lot of Go beginners don't understand this properly and kick off additional unnecessary go routines inside the handler.

@junedev
Copy link
Member

junedev commented Jun 28, 2022

You don't need configlet to change the root level config.json file, it won't do that for you anyway. You need to edit the config yourself and add the new concept to the concept array. You can generate the needed uuid with any online tool.

@MikaeelMF
Copy link
Contributor Author

@junedev
Sorry for my late response.
Thank you for your point on the configlet.
Regarding the concurrency, I think It can be explained for advanced users, but since the point of this introduction, as you have stated in your comment is "How to do X in language Y? (for someone that already knows X)" I thought maybe it would be out of scope because it does not have a direct impact on "how to use net/http" from an introductory point-of-view.
But considering it all, it can be explained in a ~~~~exercism/advanced~~~~ section so people who already know about go routines can read it and use it in their programming.

I will update the text and add this section and will start to work on the exercise parts.
For this, I think it would be interesting to continue on the story of weather-forecast exercise story. And currently, I am thinking of creating two separate exercises one for server and one for client. Of course, they will be compatible with each other.

Also I have one question regarding exercism code of conduct. Is it more appropriate if I write both concept and exercise in one PR or I should create a separate PR for the exercise part?

@junedev
Copy link
Member

junedev commented Jul 1, 2022

@MikaeelMF

Re concurrency
Sorry, I did not make the big picture clear enough here. Imagine a future where tehre is a separate concept and exercise that teaches Goroutines (and others for the other Go concurrency primitives). Your exercise will be set up so that the student can only access it after they completed the Goroutines exercise. Your content should be written like this part already exists and when you mention Goroutines there should be a link placed to some article that explains them. Then in a couple of month when we have the Exercism content on this, we will replace the external link with the link to the internal concept. We did not want to hold you back creating the http exercise until we have all the prerequisites concepts done but you should try to act like it so thinks fit together nicely later on. So it would be great if you could write a section on how http handling works assuming the student knows what Goroutines are. I don't think it should be in an "advanced" block tbh because imo it is very important to understand these basics of how a language does this. Do you get the idea? Is that ok with you?

Re splitting server/client exercises
Afaik it is not possible to have 2 exercises teach the same concept. So you have 2 options:
a) If you really think teaching server + client is too much for 1 exercise, you can create two separate concepts as well and then have each exercise teach one of those concepts.
b) If you think the about of content is ok for one concept, then you need to have 1 exercise with tasks for both parts. The description of the task can explain when you switching from one part to the other and also in the introduction part at the top you can tell students that tasks ... will be focused on X and ... will be focused on Y. Be aware though that you cannot add additional sub-headers to the exercise structure. They need to conform the official format so that things work out on the website (linking hints and test results to the correct task etc.).

Re PRs
It is up to you how you want to handle this. Some contributors split it, some do it in one PR.

Sidenote: A Code of Conduct is a special document that explains how to behave, not processes/practices. 🙂 https://exercism.org/docs/using/legal/code-of-conduct

@MikaeelMF
Copy link
Contributor Author

@junedev
Sorry for my misunderstanding. I have written a brief section on the subject, I would really appreciate it if you could read it and let me know if it needs a more technical explanation or if what I've written is enough.
On your point about having two exercises, from what I have seen in python roadmap on the website, there are several exercises for one concept. By having "two separate exercises" I meant something like that.
Regarding the PR, that's great to hear! Also, thank you for your explanation about the meaning of Code of Conduct! I did not know that! 😅

@MikaeelMF MikaeelMF marked this pull request as draft July 1, 2022 13:28
@junedev
Copy link
Member

junedev commented Jul 1, 2022

@MikaeelMF A concept can have many practice exercises that allow the student to use the learned concept later on but a concept can only have have one learning/concept exercise that introduces/teaches that concept. If you are referring to those bubbles in the Python concept boxes, those are practice exercises. If you click on one concept, there is always one exercise to "Learn X" on the top right.

In the context of the issue you are working on, we don't consider practice exercises yet, we are referring to the one concept exercise. We can talk about creating more practice exercises later but for know we should focus on the task at hand.

I know there is a lot to learn when creating the first concept/exercise here. It also took me quite a long time to wrap my head around things when I started with that. Just continue to ask questions if something is unclear.

@MikaeelMF
Copy link
Contributor Author

@junedev
Oh, thanks for the clarification! So I will try to make things work in one exercise and see how things go.
Also, thanks for your patience and helping me out! I will ask you for your advice if anything new comes up!

@MikaeelMF
Copy link
Contributor Author

MikaeelMF commented Jul 9, 2022

So I am at the final stages of writing this concept's exercise, I have some questions regarding how to implement some parts of it:

  1. What is the difference between introduction.md vs the concept introduction.md?
  2. What should be the package name for example solutions?

I would really appreciate it if I can get any help with these.

@andrerfcsantos
Copy link
Member

What is the difference between introduction.md vs the concept introduction.md?

The concept's introduction.md appears on the concept page for the exercise, while the exercise's introduction.md appears on the online editor before the instructions. Usually, they have the same content. The reason for 2 files is that for the some exercise's introduction.md we might want to tweak the concept's introduction a little bit. Or if an exercise teaches more than one concept, in the exercise's introduction.md we might want to "merge" the introduction.md of the concepts it teaches. But as a start, feel free to have the same content on both. This content must have all the information necessary for the student to solve the exercise, and. as much as possible it should avoid explaining things that won't be needed in the exercise.

You can read more about this in:

What should be the package name for example solutions?

The same as the module name specified in go.mod and in the stubs/tests provided. This exemplar solution is used for the CI tests, which copies the file to the same folder as the tests before actually running them. If you are curious how this works:

echo "copy base files and tests to $tmpstub"
cp $dir/*.go $tmpstub
echo "copy exemplar $exemplar to $tmpstub"
cp $exemplar "${tmpstub}/${stub}.go"
echo "copy module $mod to $tmpstub"
cp $mod $tmpstub
echo "put everything in the same module"
original_module=`head -n1 $tmpstub/go.mod | awk '{ print $2 }'`
sed -i'.bak' "s|package .*|package ${stub}|" $tmpstub/*.go
sed -i'.bak' "s|// Package ${original_module}|// Package ${stub}|" $tmpstub/*.go
sed -i'.bak' "s|module .*|module ${stub}|" $tmpstub/go.mod

Copy link
Member

@junedev junedev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the client concept. I will continue the review other parts some other time but wanted to already share my findings on the first bit. Feel free to already continue working on the client concept based on the comments but please refrain from making changes to the client exercise for now. I will share more thoughts on the exercise once I get there.

concepts/http-client/.meta/config.json Outdated Show resolved Hide resolved
concepts/http-client/.meta/config.json Outdated Show resolved Hide resolved
concepts/http-client/about.md Outdated Show resolved Hide resolved
concepts/http-client/about.md Outdated Show resolved Hide resolved
concepts/http-client/about.md Outdated Show resolved Hide resolved
concepts/http-client/about.md Show resolved Hide resolved
concepts/http-client/about.md Outdated Show resolved Hide resolved
@MikaeelMF
Copy link
Contributor Author

@junedev
Sorry for my late response. I will edit the mentioned files based on your feedback. Please let me know if any other files need to get changed.

@junedev junedev added the x:rep/large Large amount of reputation label Aug 17, 2022
Copy link
Member

@junedev junedev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next review batch, this time for the server concept.

I really like it overall, it touches on a lot of good points imo.
Potentially, we could add a sentence on how to serve a static file as this is a common use case.

concepts/http-server/.meta/config.json Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Outdated Show resolved Hide resolved
concepts/http-server/about.md Show resolved Hide resolved

~~~~exercism/caution
Draining the request body before closing it:
Unlike the client that drains the body before closing it, in the server you need to manually drain the request body. A good way to do so is writing a middleware that closes the request after the server job is done (I have seen this pattern for the first time in [Adam Woodbeck's mux_test.go][awoodbeck-gnp-chapter09-mux_test.go]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never heard of this and I don't think it is correct.

The official documentation https://pkg.go.dev/net/http#Request states

	// For server requests, the Request Body is always non-nil
	// but will return EOF immediately when no body is present.
	// The Server will close the request body. The ServeHTTP
	// Handler does not need to.

The behavior here is different then on the client side where you need to close the response body on your own.

Copy link
Contributor Author

@MikaeelMF MikaeelMF Nov 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junedev
Ok so after a lot of digging I did not find an answer that satisfies me regarding this issue.
First please take a look at this:
http://tleyden.github.io/blog/2016/11/21/tuning-the-go-http-client-library-for-load-testing/
I have tested it with go version 1.19.3 and it still behaves the same.
The response.Body.Close() will not drain the body therefore not allowing the same connection to get reused.
This is also another post discussing the same issue in 2017:
https://forum.golangbridge.org/t/do-i-need-to-read-the-body-before-close-it/5594
Also please take a look at here:
https://pkg.go.dev/net/http#Response.Close

	// Body represents the response body.
	//
	// The response body is streamed on demand as the Body field
	// is read. If the network connection fails or the server
	// terminates the response, Body.Read calls return an error.
	//
	// The http Client and Transport guarantee that Body is always
	// non-nil, even on responses without a body or responses with
	// a zero-length body. It is the caller's responsibility to
	// close Body. The default HTTP client's Transport may not
	// reuse HTTP/1.x "keep-alive" TCP connections if the Body is
	// not read to completion and closed.

The same principal I think applies for the r *http.Request since both bodies are the same type.
I don't know if I am missing something here but if this is true, then we should drain the client body as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are two things here, whether you need to drain and whether you need to close.

I was referring to manually closing the request body bit which you don't need to do.
The text about the response body does not apply to the request body. They might have the same type but that does not mean the same things happen to them in the lifecycle of an HTTP call.
When making a call as a client and reading the response, Go does not know when you are done with that so you need to manually close. When receiving a call as a server, the whole round trip ends when the handler function returns. At that point at latest, the request body can automatically be closed.

Regarding the draining I have also now read this multiple times that it is recommended to drain. I don't think we have to go into the details of this, stating that is is important to drain, maybe saying how to do that if you don't need the data and then providing some link should be good enough for now. Also I would consider this "about" content as it is not needed for the exercise as we don't have this edge case there that someone sends data that we don't want to read. (In real life, even in error cases you usually read the data to log the error message.)

concepts/http-server/about.md Show resolved Hide resolved
Copy link
Member

@junedev junedev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also had a look at the exercises now and left my thoughts there as mentioned earlier.

As you can guess from the comments, there is still some work to be done here but you are on a good track. As we might have said before, if at any point you feel like you don't want to continue working on this, we can always merge this with "wip" status and create follow up issues to tackle the rest. Just let us know.

If you can manage to finish these two sets, I would probably set this so it awards 100 rep + the rep from the authoring because it was a huge effort and much more than one standard exercise + concept.

config.json Outdated Show resolved Hide resolved
config.json Outdated Show resolved Hide resolved
config.json Show resolved Hide resolved
@@ -0,0 +1,37 @@
package weatherforecastserver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general setup in this package should be similar to what I described for the client. You have a custom server struct (one field of which is the standard server) with a New constructor and then that struct should have a Handle method that takes a path and a Handler function as parameters and adds those to the multiplexer.
(Imagine you are building your own, very lightweight web framework. You can see a more elaborate example of this pattern here: https://github.com/ardanlabs/service/blob/08f7a8eb7b9c374becc38ded3261255cd10b2dca/foundation/web/web.go)

So the tasks for the student would be to set up the struct, constructor and Handle function (probably it makes sense to make this one task), then the next task is adding a GET route via the handle method and then the last task is adding the POST route via the handle method. You probably avoid issues by using different route names for the GET and POST route even though that is not realistic in a standard REST scenario.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junedev
To be honest I am having a bit of trouble completing this part.
So let me go through your comment step by step so I can make sure I understand every bit clearly:

  1. The general setup in this package should be similar to what I described for the client. You have a custom server struct (one field of which is the standard server):

type WeatherServer struct{httpServer *http.Server, ...}

  1. with a New constructor:

func NewWeatherServer() WeatherServer

  1. and then that struct should have a Handle method that takes a path and a Handler function as parameters:

func (ws *WeatherServer) Handle(path string, handler http.Handler)

  1. adds those to the multiplexer:

So for this part I have a little bit of problem. First of all, since we want to pass this as the Handler for the server, this process should be done before defining the server in the NewWeatherServer function. The solution that comes to my mind is to keep an array of handlers in the WeatherServer structure and use the Handle to add to this array, and use the array to create the mux during initializing of the server. The problem with this approach is that we cannot use Handle before creating a WeatherServer, unless we do not initializ the httpServer in NewWeatherServer. I have looked at the example but they use *httptreemux.ContextMux as their mux type so that is not applicable to this solution. Did I understand this currectly and does my solution make sense for it?

  1. So the tasks for the student would be to set up the struct, constructor and Handle function (probably it makes sense to make this one task)

  2. then the next task is adding a GET route via the handle method:

Which is basically to compose a handler to feed to Handle function;

  1. and then the last task is adding the POST route via the handle method.

Same as 6.

I have looked for building web frameworks in Go, these are the best results that I came along, but none implemented things in the same manner:
https://go.dev/doc/articles/wiki/
https://semaphoreci.com/community/tutorials/building-go-web-applications-and-microservices-using-gin
https://blog.logrocket.com/creating-a-web-server-with-golang/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why you think our mux is so different from the one I provided in the example and I am not sure why you think we cannot add handlers later on.

  1. For the scope of the exercise, let us please do the normal way of simply reading the request in the handler function (or discarding it there). There is already so much too take in here for students, the middleware makes this harder to see through.
  2. As for the struct, that was a bit confusing. The important part is wrapping the mux but I see that it is still hard to construct an example that resembles real code here. Taking a step back, the important thing to learn here is how to write HTTP handler functions, how to get the data from the request etc. Passing this address to a server is rather specific to our setup here.

So what about changing it so the student basically only has to do line 16-26 (plus the part with the post route). We would ask the student to write a Setup function that accepts a mux (http.Handler) and adds a GET route. Then in task 2 we would ask the student to extend the Setup function with the post route. The test code would call Setup to get the completed mux and then set up the server by itself.

Even better plan:

  • Task 1 is to write only the handler function for the GET route. It can easily be tested invidiually with the httptest package.
  • Task 2 is to write only the handler funtion for the POST route.
  • Task 3 is to write the Setup function that adds the above functions to a mux with the correct path as described above.

That would pretty much resemble what you do in a real app.

@junedev junedev added the status/awaiting-contributor This pull request is waiting on the contributor. label Aug 19, 2022
@MikaeelMF
Copy link
Contributor Author

I also had a look at the exercises now and left my thoughts there as mentioned earlier.

As you can guess from the comments, there is still some work to be done here but you are on a good track. As we might have said before, if at any point you feel like you don't want to continue working on this, we can always merge this with "wip" status and create follow up issues to tackle the rest. Just let us know.

If you can manage to finish these two sets, I would probably set this so it awards 100 rep + the rep from the authoring because it was a huge effort and much more than one standard exercise + concept.

Thank you for letting me know. To be honest, I love to keep keep working on this concept, but recently I have been really busy and even had problems getting access to the Internet. I understand it might be inconvenient to have a concept hanging this long, and I totally understand if you would like to merge it with "wip" status. But in both cases, I will work on it and try to complete it as soon as possible.

@MikaeelMF MikaeelMF marked this pull request as draft August 26, 2022 03:48
@junedev
Copy link
Member

junedev commented Sep 7, 2022

In that case, feel free to chip away at your own pace. Just wanted to inform you about that possibility with the wip merge.

Copy link
Member

@junedev junedev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally had time to look at your comments/questions.

In the light of this https://exercism.org/blog/freeing-our-maintainers, I would like to go back to the other plan of merging this as WIP soon-ish. So if you take another pass at this, try to get it to a state where CI is green (comment out things if necessary) and then I will merge it.

w.Write([]byte("Hello World!"))
})
return serveMux
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not mean the handler function, I meant why is the example code not just

 serveMux := http.NewServeMux()
 serveMux := http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
    w.Write([]byte("Hello World!"))
 })


~~~~exercism/caution
Draining the request body before closing it:
Unlike the client that drains the body before closing it, in the server you need to manually drain the request body. A good way to do so is writing a middleware that closes the request after the server job is done (I have seen this pattern for the first time in [Adam Woodbeck's mux_test.go][awoodbeck-gnp-chapter09-mux_test.go]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are two things here, whether you need to drain and whether you need to close.

I was referring to manually closing the request body bit which you don't need to do.
The text about the response body does not apply to the request body. They might have the same type but that does not mean the same things happen to them in the lifecycle of an HTTP call.
When making a call as a client and reading the response, Go does not know when you are done with that so you need to manually close. When receiving a call as a server, the whole round trip ends when the handler function returns. At that point at latest, the request body can automatically be closed.

Regarding the draining I have also now read this multiple times that it is recommended to drain. I don't think we have to go into the details of this, stating that is is important to drain, maybe saying how to do that if you don't need the data and then providing some link should be good enough for now. Also I would consider this "about" content as it is not needed for the exercise as we don't have this edge case there that someone sends data that we don't want to read. (In real life, even in error cases you usually read the data to log the error message.)


func multiplexer() http.Handler{
serveMux := http.NewServeMux()
serveMux := http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serveMux := appears twice here, that doesn't look right

@@ -0,0 +1,37 @@
package weatherforecastserver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why you think our mux is so different from the one I provided in the example and I am not sure why you think we cannot add handlers later on.

  1. For the scope of the exercise, let us please do the normal way of simply reading the request in the handler function (or discarding it there). There is already so much too take in here for students, the middleware makes this harder to see through.
  2. As for the struct, that was a bit confusing. The important part is wrapping the mux but I see that it is still hard to construct an example that resembles real code here. Taking a step back, the important thing to learn here is how to write HTTP handler functions, how to get the data from the request etc. Passing this address to a server is rather specific to our setup here.

So what about changing it so the student basically only has to do line 16-26 (plus the part with the post route). We would ask the student to write a Setup function that accepts a mux (http.Handler) and adds a GET route. Then in task 2 we would ask the student to extend the Setup function with the post route. The test code would call Setup to get the completed mux and then set up the server by itself.

Even better plan:

  • Task 1 is to write only the handler function for the GET route. It can easily be tested invidiually with the httptest package.
  • Task 2 is to write only the handler funtion for the POST route.
  • Task 3 is to write the Setup function that adds the above functions to a mux with the correct path as described above.

That would pretty much resemble what you do in a real app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/awaiting-contributor This pull request is waiting on the contributor. x:rep/large Large amount of reputation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants