In the last step, we show our service layer consuming the repository layer but we don't see the data.
In this post, we will keep covering the code to show you how we consume the service layer in the handler and mount the response.
Before demonstrating the handler code, we need to make some changes in the container/container.go. Now we need to instantiate the repository layer that we talk in the last step and pass this new repository into the service layer in the constructor
// ...
func Inject() Container {
//stores - this is new
bookStore := repository.NewBookStoreInMemory()
//init services
bs := service.NewBookService(bookStore)
// ...
}
Now, as you can see, we instantiate the repository and delivery the implementation to the service layer, Now our Container with services will have a service that communicates with a persistence layer (in memory at the moment).
Looking to our handler/book.go you will see new methods and our constructor with a new required information, the service layer.
package handler
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi"
"github.com/maaarkin/hero-api-golang/internal/service"
)
type BookHandler struct {
bookService service.BookService
}
func NewBookHandler(bs service.BookService) *BookHandler {
return &BookHandler{bs}
}
func (b *BookHandler) Route(r chi.Router) {
r.Get("/hello", b.hello)
r.Get("/", b.getAll)
}
func (*BookHandler) hello(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello World"))
}
func (h *BookHandler) getAll(w http.ResponseWriter, r *http.Request) {
if results, err := h.bookService.FindAll(); err != nil {
addDefaultHeaders(w)
writeData(
w,
map[string]interface{}{
"Status": http.StatusInternalServerError,
"Description": "Internal error, please report to admin",
},
)
} else {
if results != nil && len(*results) > 0 {
addDefaultHeaders(w)
w.WriteHeader(http.StatusOK)
writeData(w, results)
} else {
addDefaultHeaders(w)
w.WriteHeader(http.StatusNoContent)
}
}
}
func addDefaultHeaders(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
}
func writeData(w http.ResponseWriter, data interface{}) {
if bytes, e := json.Marshal(data); e != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} else {
if _, e := w.Write(bytes); e != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}
}
Now you will see three new method (getAll, addDefaultHeaders and writeData). The getAll is our new method to show all books that our service will provide to us. The addDefaultHeaders is just a helper function to group some headers necessary to our response being formated in json and the writeData is a helper function too that will helper us to write our data into the response.
The getAll is too simple, we call our service layer and provide a specific HTTP error code and a message error if our service returns an error. If our service does not throw any error we just validate if the result has some content and delivery a 200 Http Code (OK) with the content or delivery a 204 Http Code (No Content).
Now if you start the application and get the endpoint /v1/books, you will receive a json with two objects.
λ curl http://localhost:8080/v1/books
[{"Id":1,"Title":"Title 1","Author":"MarkMark","NumberPages":101},{"Id":2,"Title":"Title 2","Author":"MarkMark 2","NumberPages":203}]
But... I think our handler is too verbose at the moment.. in the next step we will refactor the code to be more readable.
See ya!
Top comments (0)