Hello, this is Yamauchi! A fat can was held at our Fukuoka branch on November 14, 2020. It's a name that seems to be something like staying overnight, but although I've been doing it for at least one month for about a year and a half, I still only have a day trip. .. .. Well, let's put it aside, and I'll introduce the tool I made this time! By the way, I'm a super beginner about Go language, and it's the first time I've made something like a tool, so I think there are various problems with the code.
For this tool, I created the API and the side that hits it separately. This is the repository. API:https://github.com/y-keisuke/pokemon Hitting side: https://github.com/y-keisuke/pokemon_command Please look at it with the feeling that you will review it w
I really wanted to put them together in one repository, but I didn't know if it was possible, so I divided the repositories, so only the commands I used later have commands in the repository. It doesn't matter.
That's why it's a rough introduction, so the movement is like this ↓ (Sorry for the sluggish movement, I hit a total of 4 times)
It's pretty rough, but it looks like this.
First of all, on the API side, I borrowed json from the repository here ↓. https://github.com/fanzeyi/pokemon.json/blob/master/pokedex.json
db.go
I really wanted to prepare a DB, but it was troublesome, so I used json as it is ←
That is db.go
.
package db
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type PokemonCollection struct {
Pokemons []PokemonData
}
type PokemonData struct {
Id int `json:"id"`
Name PokemonName `json:"name"`
Type []string `json:"type"`
Base PokemonBase `json:"base"`
}
type PokemonName struct {
English string `json:"english"`
Japanese string `json:"japanese"`
Chinese string `json:"chinese"`
French string `json:"french"`
}
type PokemonBase struct {
HP int `json:"hp"`
Attack int `json:"attack"`
Defense int `json:"defense"`
SpAttack int `json:"spattack"`
SpDefense int `json:"spdefense"`
Speed int `json:"speed"`
}
func GetPokemonCollection() PokemonCollection {
raw, err := ioutil.ReadFile("./pokedex.json")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
var pokemonCollection PokemonCollection
json.Unmarshal(raw, &pokemonCollection)
return pokemonCollection
}
Some of the structures defined above are exactly the same as the json structure.
The structure has changed a little from the borrowed json.
The GetPokemonCollection ()
defined here puts the json data into the structure.
In particular
json.Unmarshal(raw, &pokemonCollection)
This part is packed in the structure. This structure is used as a DB.
main.go
Next is main.go
.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
pokemon2 "pokemon/pokemon"
)
func pokemonToJson(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
pokemon, err := pokemon2.GetPokemonBy(name)
//I didn't know how to return the error information json nicely here
if err != nil {
log.Writer()
http.Error(w, fmt.Sprintf("{\"err\":\"%s\"}", err), 200)
return
}
pokemonJson, _ := json.Marshal(pokemon)
fmt.Fprint(w, fmt.Sprintf("%+v", string(pokemonJson)))
}
func handleRequests() {
http.HandleFunc("/", pokemonToJson)
log.Fatal(http.ListenAndServe(":18888", nil))
}
func main() {
handleRequests()
}
What we are doing here is "listen for access on port 18888, look at the parameters of that access, get the Pokemon data, and return the race value in json".
First, the part waiting at port 18888
//Listen to port 18888
http.ListenAndServe(":18888", nil)
Then look at the access parameters
//Get the parameter of key called name
name := r.FormValue("name")
In addition, get Pokemon data
//Get Pokemon with GetPokemonBy in pokemon2 (details below)
pokemon, err := pokemon2.GetPokemonBy(name)
Finally, json returns the race value
//Convert struct to json and then return
pokemonJson, _ := json.Marshal(pokemon)
fmt.Fprint(w, fmt.Sprintf("%+v", string(pokemonJson)))
Go? What can you eat it? I'm likely to hear a voice saying "I'm printing without returning!", So I'll add that Fprint is a function that allows you to specify the writing destination, and this time I'm writing to w (http.ResponseWriter). , It feels like it will be returned as a result response.
pokemon.go
This is the one that says pokemon2. It is 2 because I covered it with the project name. It's a pretty NG name. goland did a good job ...
package pokemon
import (
"errors"
"pokemon/db"
)
type Pokemon struct {
Name string `json:"name"`
HP int `json:"hp"`
Attack int `json:"attack"`
Defense int `json:"defense"`
SpAttack int `json:"sp_attack"`
SpDefense int `json:"sp_defense"`
Speed int `json:"speed"`
}
func GetPokemonBy(name string) (*Pokemon, error) {
pokemonCollection := getPokemonCollection()
for _, pokemon := range pokemonCollection.Pokemons {
if pokemon.Name.Japanese == name {
return getPokemonStruct(pokemon), nil
}
}
return nil, errors.New("Pokemon not found")
}
func getPokemonCollection() db.PokemonCollection {
return db.GetPokemonCollection()
}
func getPokemonStruct(pokemon db.PokemonData) *Pokemon {
return &Pokemon{
Name: pokemon.Name.Japanese,
HP: pokemon.Base.HP,
Attack: pokemon.Base.Attack,
Defense: pokemon.Base.Defense,
SpAttack: pokemon.Base.SpAttack,
SpDefense: pokemon.Base.SpDefense,
Speed: pokemon.Base.Speed}
}
For the time being, I will only explain the function called GetPokemonBy that was called from main.go
.
//I'm getting a DB
pokemonCollection := getPokemonCollection()
I get it via a private function, but I don't remember why.
for _, pokemon := range pokemonCollection.Pokemons {
if pokemon.Name.Japanese == name {
return getPokemonStruct(pokemon), nil
}
}
If the name of the acquired DB Pokemon and the name of the Pokemon received in the parameter match, that Pokemon is packed in a new structure and returned.
Roughly speaking, the API side looks like this.
Next is the side that hits the API.
main.go
For the time being, the source code.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"pokemon_command/input"
"pokemon_command/pokemon"
)
func main() {
url := input.CreateUrl()
resp, _ := http.Get(url)
defer resp.Body.Close()
byteArray, _ := ioutil.ReadAll(resp.Body)
var errCheck map[string]string
json.Unmarshal(byteArray, &errCheck)
if val, ok := errCheck["err"]; ok {
fmt.Println(val)
return
}
pokemonStruct := pokemon.JsonToPokemon(byteArray)
pokemon.PrintPokemon(pokemonStruct)
}
This is an explanation of the contents of the main function.
//Details of input will be described later
url := input.CreateUrl()
Here, the standard input is received and the URL for hitting the API is generated based on it.
resp, _ := http.Get(url)
defer resp.Body.Close()
Then access the generated URL and receive the result.
byteArray, _ := ioutil.ReadAll(resp.Body)
Gets the Body of the received response.
var errCheck map[string]string
json.Unmarshal(byteArray, &errCheck)
if val, ok := errCheck["err"]; ok {
fmt.Println(val)
return
}
I'm doing error handling here, but I don't know the optimal solution. .. ..
//pokemon will be described later
pokemonStruct := pokemon.JsonToPokemon(byteArray)
pokemon.PrintPokemon(pokemonStruct)
The last received json is stored in the structure and then output.
main.go
looks like this.
input.go
package input
import (
"bufio"
"fmt"
"os"
)
func CreateUrl() string {
fmt.Print("Please enter the name of the Pokemon.\n>> ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
return "http://localhost:18888/?name=" + scanner.Text()
}
It's not a big deal here, it accepts standard input and generates a URL based on that input.
fmt.Print("Please enter the name of the Pokemon.\n>> ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
These three lines display the text that prompts you to enter, and the standard input is obtained by the function of the bufio package.
return "http://localhost:8081/?name=" + scanner.Text()
The URL is generated by combining the acquired character strings. I want to write something a little better.
pokemon.go
package pokemon
import (
"encoding/json"
"fmt"
"log"
)
type Pokemon struct {
Name string `json:"name"`
HP int `json:"hp"`
Attack int `json:"attack"`
Defense int `json:"defense"`
SpAttack int `json:"sp_attack"`
SpDefense int `json:"sp_defense"`
Speed int `json:"speed"`
}
func JsonToPokemon(pokemonJson []byte) *Pokemon {
pokemon := new(Pokemon)
err := json.Unmarshal(pokemonJson, pokemon)
if err != nil {
log.Fatal(err)
}
return pokemon
}
func PrintPokemon(pokemon *Pokemon) {
fmt.Println("Name: ", pokemon.Name)
fmt.Println("HP : ", pokemon.HP)
fmt.Println("Attack: ", pokemon.Attack)
fmt.Println("Bougyo: ", pokemon.Defense)
fmt.Println("Tokukou: ", pokemon.SpAttack)
fmt.Println("Tokubo: ", pokemon.SpDefense)
fmt.Println("Quickness: ", pokemon.Speed)
}
Again, it's not a big deal, just a function that packs the received json into a struct and a function that outputs based on that struct. I haven't written anything really important, so I'll omit the details.
It became an article that felt quite awkward, but ← By trying to make something with GO, I wanted to do something like "accept input with standard input" this time, so for the time being, I'm glad that I could implement it like that. Ideally, create something like the pokemon command, and the display will change depending on the option. I want to make something like that, but I will do my best at another time.
I wanted to hit the API anytime, anywhere, not just locally, so I signed up for Sakura VPS and put the API there, so I'll write about that in the article next time.
That's it!
Recommended Posts