【Go】Echo エラー処理を実装する
apiサーバーの実装でとりあえずのエラー処理中。とりあえず以下をハンドリングするような想定で。
・ビジネスエラー
・システムエラー
・404エラー
・その他サーバーエラー
ディレクトリ構成はこんな感じ
$ tree -L 3 . └── app ├── controller │ └── traking.go ├── error │ ├── business.go │ └── system.go ├── handler │ └── handler.go ├── main.go
独自エラーの実装
Messageはユーザーに伝えるメッセージ。
LogMessageはログとかに残すメッセージ(後で実装する予定)
app/error/system.go
package error import( "fmt" ) type SystemError struct { Message string LogMessage string } func (err *SystemError) Error() string { return fmt.Sprintf("%s", err.Message) }
エラーハンドリング
とりあえずJSONをかえすところまで実装。
app/handler/handler.go
package handler import( AppErr "app/error" "net/http" "github.com/labstack/echo" ) type ApiError struct { Status int `json:status` Message string `json:message` } func JSONErrorHandler(err error, c echo.Context) { switch e := err.(type) { case *AppErr.BusinessError: // Business Error c.JSON( http.StatusOK, ApiError{ Status: 200, Message: e.Message, }) case *AppErr.SystemError: // System Error c.JSON(http.StatusOK, ApiError{ Status: 500, Message: e.Message, }) default: if he, ok := err.(*echo.HTTPError); ok { if he.Code == 404 { // 404 c.JSON(he.Code, ApiError{ Status: he.Code, Message: "Not Found", }) } else { // その他サーバーエラー c.JSON(he.Code, ApiError{ Status: he.Code, Message: "System Error", }) } } } }
main.go
package main import ( AppErr "app/error" "app/handler" ) func main() { e := echo.New() e.GET("/users", func(c echo.Contet) error { return &AppErr.System{ Message: "システムエラーだよ" } // return echo.NewHTTPError(http.StatusInternalServerError, "Server Error") // return echo.NewHTTPError(http.StatusNotFound, "Not Found") }) e.HTTPErrorHandler = handler.JSONErrorHandler e.Start(":8080") }
とりあえずやりたいことはできた。色々実装したら全然足りなそうだけど一旦はこれで。以上です。
【Go】Echo Controllerを別Packageに分割する
細かい話だけどecho的には特にルールは決まっていなそう。
main.go
package main import ( "github.com/labstack/echo" "app/controller" ) func main() { e := echo.New() e.GET("/members", controller.GetMember()) e.POST("/members", controller.PostMember()) }
controller/members.go
package controller import( "net/http" "github.com/labstack/echo" ) func GetMember() echo.HandlerFunc { return func(c echo.Context) error { ・・・ } } func PostMember() echo.HandlerFunc { return func(c echo.Context) error { ・・・ } }
こんな感じでよいのかな。。以上です。
【Go】Echo JSONを返す
echoでjsonを返すサンプル
package main import( "net/http" "github.com/labstack/echo" ) // レスポンスを表す構造体を定義 type Member struct { Id int `json:"id"` Name string `json:"name"` } func main() { e := echo.New() e.GET("/members", func(c echo.Context) error { // Member型のリスト members := []Member{ {Id: 1, Name: "test1"}, {Id: 2, Name: "test2"} } return c.JSON(http.StatusOK, members) }) }
以上です
macにgoをインストールする手順メモ
1台のPCで複数バージョンのGoを使用したいのでgvm(Go Version Manger)を使用する。
https://github.com/moovweb/gvm
ドキュメントどおりにやったらすんなり入った。Mac使ってるので「Mac OS X Requirements」のコマンド実行
準備
$ brew update $ brew install mercurial
gvmのインストール 「Installing」のコマンド実行
$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
gvmがインストールできたことを確認
$ gvm version Go Version Manager v1.0.22 installed at /Users/xxxxx/.gvm
goをインストール
# インストール可能なバージョンを調べる $ gvm listall # 1.4と1.9のバイナリーをインストールする $ gvm install go1.4 -B $ gvm install go1.9 -B # インストールできたことを確認 $ gvm list gvm gos (installed) go1.4 go1.9 # バージョンを指定して適用 $ gvm use go1.9 # 適用されたことを確認 $ go version go1.9 darwin/amd64
とりあえずGoが使える状態になった。
インストールのところで「-B」をつけるとバイナリーをそのままインストールすることになるのだが、これつけないでインストールしようとしたらエラーが出たのでめんどくさくなってこの形としてしまった。。
以上です
labelクリックでinputにfocusする iOS対応
スマホで言えばこんなUIの入力フォーム。
この場合、画面の左半分がlabelで右半分がinput。
で、気がきかないから気にしてなかったのだが、labelをタップしても入力できないということで親切でないということで修正しようとしたらちょっとはまったのでメモしておく。
Labelをクリックしたらinputにfocusさせる(失敗)
javascriptで無理やりやろうとした。PCやandroidで確認してたらうまくいったのだが、何故かiPhoneのsafariでうまくいかず断念。
【ionic2】ログイン機能を実装する
はじめに
今回やりたかったのは、まぁ普通のログイン機能をもつアプリ。実装したい要件としては以下
・未ログインの場合は、ログイン画面を表示。
・ログイン済の場合は、他の画面が見れる。
実装方法としては以下のような形で行ってみた。
・ログイン済みかどうかはローカルストレージに保存して判定する。
・app.component.ts の platform.ready()でローカルストレージをチェック。
・ログイン済みの場合は他の画面を表示
・未ログインの場合はログイン画面を表示。ログインに成功したらローカルストレージに書き込んでindex.htmlをリロードする。リロードすることでplatform.ready()が再度呼ばれるので次はログインと判定されるはず。
実装
src/app/app.component.ts
・・・ import { Storage } from '@ionic/storage'; ・・・ export class MyApp { rootPage:any; constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, storage: Storage) { platform.ready().then(() => { statusBar.styleDefault(); splashScreen.hide(); storage.get('auth').then((val) => { if (val == null) { // 未ログインの場合はログイン画面を表示 this.rootPage = 'signin'; } else { // ログイン済みの場合はその他の画面を表示 this.rootPage = 'home'; } }); }); } }
src/pages/signin/signin.ts
import { Storage } from '@ionic/storage'; ・・・ export class SigninPage { ・・・ // ログイン signin() { this.ApiService.get('/signin', {}) .then(data => { // ログインに成功したらログイン情報をローカルストレージに保存して、リロード this.storage.set('auth', { token:data.token }); location.reload(); }) .catch(messages => { alert(messages); }); } /* ログアウトも手抜きここで書いちゃう */ signout() { // ローカルストレージを削除してリロード this.storage.remove('auth'); location.reload(); } }
なんか正当な方法じゃなさそうだけど、自分がやってるアプリだとこんなので十分事足りそう。以上です。
【ionic2】apiリクエストをproviderで共通処理化する
APIのURLを開発と本番で切り替える
こちらのURLのとおりにやったらできた。
http://roblouie.com/article/296/ionic-2-environment-variables-the-best-way/
webpackの設定とかはURLのとおりにやればできる。あとは以下のように各々呼び出せるようになる。
import { Injectable, Inject } from '@angular/core'; import { EnvVariables } from '../app/environment-variables/environment-variables.token'; @Injectable() export class ApiService { constructor(http: Http, @Inject(EnvVariables) public envVariables) { } test() { // this.envVariables.apiEndpoint } }
今回は上の記事に書いてあるそのまんまつかわせてもらった。
src/app/environment-variables/development.ts
export const devVariables = { apiEndpoint: 'http://dev.example.com', }
src/app/environment-variables/production.ts
export const prodVariables = { apiEndpoint: 'http://prod.example.com', }
APIリクエストを共通化する
Angularではproviderとして作るのが一般的ぽいのでそのようにしてみる。
ひな形作成
$ ionic g provider api-service
実行すると src/providers/api-service.ts が生成されるのでここに実装する。
今回は認証用のトークンをローカルストレージに保存しておいて、リクエストのたびにhttpヘッダーにさしこむようにした。
src/providers/api-service.ts
import { Injectable, Inject } from '@angular/core'; import { Http, Headers } from '@angular/http'; import 'rxjs/add/operator/map'; import { Storage } from '@ionic/storage'; import { EnvVariables } from '../app/environment-variables/environment-variables.token'; @Injectable() export class ApiService { constructor(public http: Http, @Inject(EnvVariables) public envVariables, private storage: Storage) { /* コンストラクタ */ } request(method, action, params) { return new Promise((resolve, reject) => { this.storage.get('auth.token') .then(token => { var url = this.envVariables.apiEndpoint + action; var http = null; var headers = { headers: new Headers({ 'Authorization': 'Token ' + token }) }; if (method == 'get') { http = this.http.get(url, headers) } else if (method == 'post') { http = this.http.post(url, data, headers) } if (http !== null) { http.subscribe((response) => { if (response.status == 200) { var result = response.json(); if (result.status == "success") { resolve(result); } else { reject(result.messages); } } else { reject('通信エラーが発生しました'); } },err => { reject(err); }); } }); }); } get(url, params) { return this.request('get', url, params); } post(url, data) { return this.request('post', url, params); } }
これでコンポーネントから呼び出せる
src/pages/hello.js
import { ApiService } from '../../providers/api-service'; ・・・ @Component({ selector: 'page-hello', templateUrl: 'hello.html', providers: [ApiService] }) export class SamplePage { constructor(public navCtrl: NavController, private ApiService: ApiService) { } test() { this.ApiService.get('/users/', {}) .then(data => { // success console.log(data); }) .catch(messages => { // error alert(messages); }); } }
以上です。