Help us understand the problem. What is going on with this article?

Status Codeを下ネタで返すサーバーを書きました。

More than 1 year has passed since last update.

これはTreasure Advent Calendar 2018の24日目の記事です。レポートがやばくて投稿が遅れました、すみません。。

さて、今日はクリスマスですね!クリスマスには下ネタを言ってもいいと法律で決まっているようなのでバンバン綴っていきたいと思います!w

Status Codeとは

HTTPのレスポンス状態を表す番号とメッセージのことです。(メッセージ自体は番号の意味なので厳密には含まないかも)

"404 Not Found"とかのやつですね。誰がクリスマスの「お相手は見つかりませんでした」や!!"201 Created"になれるように頑張ります。おっと、守主語によってはよろしくなさそうですが、、w

で、このStatus Codeって大量にあるんですよね。よく見るやつからほぼお目にかかれないやつまで。それを万国共通語の下ネタで覚えちゃおうって話です。

だいたいエラーを返すサーバーを書きました

だらだら言葉だけで説明していても面白くないので、実際にサーバーを実装してみました。普通に実装していたらほぼ200になるので、ランダムで適当にエラーを返すサーバーをつ作りました。ほんとはプロキシとか挿れてちゃんとエラーハンドリングしている物をつくりたかったんですが、時間の関係で簡易的な実装にしました。

main.go
package main

import (
    "fmt"
    "math/rand"
    "net/http"
    "strconv"
    "time"

    "github.com/Dragon-taro/learn-status-code/db"
    "github.com/Dragon-taro/learn-status-code/status"
)

func getStatusCode() int {
    rand.Seed(time.Now().UnixNano())
    codeArray := []int{200, 200, 200, 200, 200, 200, 201, 205, 301, 400, 401, 402, 403, 404, 405, 413, 414, 429, 502, 503}

    return codeArray[rand.Intn(len(codeArray))]
}

func createBody(code int) string {
    return strconv.Itoa(code) + ": " + status.StatusText(code)
}

func handleStaus() (int, string) {
    code := getStatusCode()
    body := createBody(code)
    return code, body
}

func handleRequest(w http.ResponseWriter, req *http.Request) {
    if req.Method == "POST" {
        code := http.StatusMethodNotAllowed
        body := createBody(code)
        w.WriteHeader(code)
        fmt.Fprint(w, body)
        return
    }

    status, body := handleStaus()
    w.WriteHeader(status)
    fmt.Fprint(w, body)
    return
}



func main() {
    http.HandleFunc("/", handleRequest)
    http.ListenAndServe(":8080", nil)
}

ランダムに配列に格納されたint型のStatus Codeをとってきって、それに対応するメッセージと共に返しています。簡易的にstringで返してますが、本来はjsonとかで返した方がよさそうです。

Status Codeの呼び出しもとに関しては、net/httpパッケージを参考に独自のファイルを作成してそれを呼び出しています。(つまりメッセージを下ネタに変換しています。w)"github.com/Dragon-taro/learn-status-code/status"で呼び出しています。ここに載せるのは気がひけるので、各自僕のリポジトリをみてくださいw

では、実際に叩いてみましょう。まずはGET。

$ curl --dump-header - http://localhost:8080/
HTTP/1.1 200 OK
Date: Mon, 24 Dec 2018 05:28:05 GMT
Content-Length: 23
Content-Type: text/plain; charset=utf-8

200: あ、いいよ♡%

$ curl --dump-header - http://localhost:8080/
HTTP/1.1 429 Too Many Requests
Date: Mon, 24 Dec 2018 05:28:02 GMT
Content-Length: 18
Content-Type: text/plain; charset=utf-8

429: 激しすぎw%

まあこんな感じですね。ちなみに、--dump-header -を付与することでレスポンスのheaderを見ることができます。

次にPOST。

$ curl -X POST --dump-header - http://localhost:8080/
HTTP/1.1 405 Method Not Allowed
Date: Mon, 24 Dec 2018 05:32:35 GMT
Content-Length: 29
Content-Type: text/plain; charset=utf-8

405: 本番は禁止です。%

このサーバーは本番禁止のデリヘルみたいなもんなので、POSTすると怒られます。

適当すぎるUserエンドポイント

さてこれだけでは芸がないので、/userでも作りましょう。

main.go
func handleRequestUser(w http.ResponseWriter, req *http.Request) {
    if req.Method == "POST" {
        _, err := db.Connect()
        if err != nil {
            code := http.StatusInternalServerError
            body := createBody(code)
            w.WriteHeader(code)
            fmt.Fprint(w, body)
            return
        }
    }

    code := http.StatusPaymentRequired
    body := createBody(code)
    w.WriteHeader(code)
    fmt.Fprint(w, body)
    return
}

// ...

func main() {
    http.HandleFunc("/", handleRequest)
    http.HandleFunc("/user", handleRequestUser)
    http.ListenAndServe(":8080", nil)
}
db/handler.go
package db

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
)

func Connect() (*sqlx.DB, error) {
    return sqlx.Connect(
        "mysql",
        fmt.Sprintf(
            "%s:%s@tcp(%s:%s)/%s",
            "root",
            "password",
            "localhost",
            "3306",
            "Xmas",
        ),
    )
}

このエンドポイントはPOSTがくると先ほどとは違ってDBにちゃんとアクセスしようとします。そこまではいいのですが、作ってもいないDBに適当にアクセスしようとするので、当たり前のようにエラーが出ます。

どうせエラーなので、成功したときの処理は書いていません。

では、POSTしてみましょう。

curl -X POST --dump-header - http://localhost:8080/user
HTTP/1.1 500 Internal Server Error
Date: Mon, 24 Dec 2018 05:45:50 GMT
Content-Length: 16
Content-Type: text/plain; charset=utf-8

500: EDおつ。%

内部的なエラーが発生します。いやいや、悪いのお前やんっていう。

そのくせにGETしたら金を請求されます。適当すぎる。

curl --dump-header - http://localhost:8080/user
HTTP/1.1 402 Payment Required
Date: Mon, 24 Dec 2018 05:47:39 GMT
Content-Length: 33
Content-Type: text/plain; charset=utf-8

402: 1時間2万円で〜す。%

値段設定がやっぱりデリヘル。

Chiro

最後に/chiroを作りましょう。

main.go
func handleRequestChiro(w http.ResponseWriter, req *http.Request) {
    code := http.StatusGatewayTimeout
    body := createBody(code)
    w.WriteHeader(code)
    fmt.Fprint(w, body)
    time.Sleep(3 * time.Second)
    return
}

func main() {
    http.HandleFunc("/", handleRequest)
    http.HandleFunc("/user", handleRequestUser)
    http.HandleFunc("/chiro", handleRequestChiro)
    http.ListenAndServe(":8080", nil)
}

満を持してタイムアウトしてきます。

リクエストをしてみましょう。

$ curl --dump-header - http://localhost:8080/chiro
HTTP/1.1 504 Gateway Timeout
Date: Mon, 24 Dec 2018 05:54:10 GMT
Content-Length: 26
Content-Type: text/plain; charset=utf-8

504: 遅漏すぎん?笑%

3秒後にやっとレスポンスが返ってきたかと思ったらめちゃくちゃ煽ってきます。いやいや、遅漏なのはあなたでしょ、と。

詰まったところ

最初は、"github.com/Dragon-taro/learn-status-code/status"をmainパッケージに書いていたのですが、buildは通ってもrunでは実行できませんでした。そこで、時間がなかったのでstatusパッケージを作ったのですが、ここにgoのコンパイルの落とし穴があったようです。

go build <file名>のときは同一パッケージのファイルも同時にコンパイルされるようなのですが、go run <file名>のときは指定されたファイルしかコンパイルがされないようです。

そのため、go run **.goのように複数ファイルを指定する必要があるらしいです。@rky_1011教えてくれてありがとう!

以上、唯一有益?な内容でした。w

終わりに

人生の大切な数分を僕のくだらない記事に捧げてくださりありがとうございます。(しかも貴重な平成最後のクリスマスの数分!)クリスマスに予定がある人もそうでない人も、この記事を読んで少しでも笑っていただけたら幸いです。

では、よき平成最後の性y..おっと聖夜を!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした