/liga
que retorne uma lista contendo todos os jogadores armazenados. Ela gostaria que isto fosse retornado como um JSON.liga
.ArmazenamentoJogador
falso para usar./liga
e obter um OK
de retorno.ServidorJogador
deve estar sendo abortado por um panic como acima. Vá para a linha de código que está apontando para servidor.go
no stack trace./liga
e então, isto nos dá um slice bounds out of range
.ServeMux
(requisição multiplexadora) que nos permite atracar um http.Handler
para caminhos de uma requisição em específico.x
usar o handler y
.http.HandlerFunc
e uma função anônima para w.WriteHeader(http.StatusOK)
quando /liga
é requisitada para fazer nosso novo teste passar./jogadores/
nós somente recortamos e colamos nosso código dentro de outro http.HandlerFunc
.ServeHTTP
(notou como ServeMux
é também um http.Handler
?)ServeHTTP
parece um pouco grande, nós podemos separar as coisas um pouco refatorando nossos handlers em métodos separados.NovoServidorJogador
que pegará nossas dependências e ao ser chamada, irá fazer a configuração única da criação do roteador. Desta forma, cada requisição pode usar somente uma instância do nosso roteador.ServidorJogador
agora precisa armazenar um roteador.ServeHTTP
e colocamos dentro do nosso NovoServidorJogador
, então isto só será feito uma vez, não por requisição.ServidorJogador{&armazenamento}
por NovoServidorJogador(&armazenamento)
.func (s *ServidorJogador) ServeHTTP(w http.ResponseWriter, r *http.Request)
por não ser mais necessária!ServidorJogador
removendo a propriedade nomeada roteador http.ServeMux
e substituindo por http.Handler
; isto é chamado de incorporar.O Go não provê a noção típica de subclasses orientada por tipo, mas tem a habilidade de "emprestar" partes de uma implementação por incorporar tipos dentro de uma struct ou interface.
ServidorJogador
agora tem todos os métodos que http.Handler
têm, que é somente o ServeHTTP
.http.Handler
nós atribuímos ele para o roteador
que nós criamos em NovoServidorJogador
. Nós podemos fazer isso porque http.ServeMux
tem o método ServeHTTP
.ServeHTTP
, pois nós já estamos expondo um via o tipo incorporado.http.Handler
).http.ServeMux
(o tipo concreto) por exemplo, também funcionaria porém os usuários de ServidorJogador
seriam capazes de adicionar novas rotas ao nosso servidor porque o método Handle(path, handler)
seria público./liga
. Agora precisamos fazê-lo retornar algumas informações úteis.Jogador
com alguns campos, sendo assim nós criaremos um novo tipo para capturarmos isso.Decoder
do pacote encoding/json
e então chamamos seu método Decode
. Para criar um Decoder
é necessário ler de um io.Reader
, que em nosso caso é nossa própria resposta Body
.Decode
pega o endereço da coisa que nós estamos tentando decodificar, e é por isso que nós declaramos um slice vazio de Jogador
na linha anterior.Decode
pode retornar um error
. Não há ponto de continuidade para o teste se isto acontecer, então nós checamos o erro e paramos o teste com t.Fatalf
. Note que nós exibimos o não foi possível ouvir na porta 5000 junto do erro, pois é importante para qualquer outra pessoa que esteja rodando os testes ver que o texto não pôde ser analisado.Encoder
você precisa de um io.Writer
que é o que http.ResponseWriter
implementa.Decoder
você precisa de um io.Reader
que o campo Body
da nossa resposta implementa.io.Writer
. Isso é uma outra demonstração desta prevalência nas bibliotecas padrões e de como várias bibliotecas facilmente trabalham em conjunto com elas.tabelaDaLiga
. Como sabemos, nós não vamos codificar isso por agora.EsbocoArmazenamentoJogador
para permitir que ele armazene uma liga, que é apenas um slice de Jogador
. Nós vamos armazenar nossos dados esperados lá.EsbocoArmazenamentoJogador
; ponha-o como nulo para os outros testes.EsbocoArmazenamentoJogador
e nós abstraímos esses dados para uma interface ArmazenamentoJogador
. Nós precisamos atualizar isto então qualquer um passando-nos um ArmazenamentoJogador
pode prover-nos com dados para as ligas.obterTabelaDaLiga()
e então atualize manipulaLiga
para chamar ObterLiga()
.ArmazenamentoDeJogadorNaMemoria
e EsbocoArmazenamentoJogador
não tem os novos métodos que nós adicionamos em nossa interface.EsbocoArmazenamentoJogador
isto é bem fácil, apenas retorne o campo liga
que nós adicionamos anteriormente.InMemoryStore
é implementado:ObterLiga
"propriamente", iterando sobre o map, lembre que nós estamos apenas tentando escrever o mínimo de código para fazer os testes passarem.InMemoryStore
.content-type
correto na resposta, então as máquinas podem reconhecer que nós estamos retornando um JSON
.manipulaLiga
verificaTipoDoConteudo
.ServidorJogador
, por agora podemos mudar nossa atenção para ArmazenamentoDeJogadorNaMemoria
porque no momento se nós tentarmos demonstrá-lo para o gerente de produto, /liga
não vai funcionar./liga
.t.Run
para parar este teste um pouco e então reusar os helpers dos testes do nosso servidor - novamente mostrando a importância de refatoração dos testes.ArmazenamentoDeJogadorNaMemoria
is returning nil
when you call ObterLiga()
so we'll need to fix that.Jogador
http.Handler
nela, tanto que você pode atribuir rotas para Handler
s e a rota em si também é um Handler
. Ela não tem alguns recursos que você pode esperar, como caminhos para variáveis (ex. /users/{id}
). Você pode facilmente analisar esta informação por si mesmo porém você pode querer considerar olhar para outras bibliotecas de roteamento se isso se tornar um fardo. Muitas das mais populares seguem a filosofia das bibliotecas padrões e também implementam http.Handler
.