Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【Java】Java8で文字列をBase64変換する

java.util.Base64

Base64.Encoder (Java Platform SE 8)

Java8より、Base64によってバイトデータをエンコードできるBase64クラスが実装されました。
このUtilityクラスを利用することで、Java上で容易にBase64エンコード/デコードが利用できます。

Usage

import java.util.Base64;

public class Main {
    public static void main(String... args) {
        String message = "Hello, Base64!";
        // Base64文字列へエンコード
        String base64 = Base64.getEncoder().encodeToString(message.getBytes());
        // Base64文字列をデコード
        String decodedMessage = new String(Base64.getDecoder().decode(base64));
    }
}

参考文献

Base64.Encoder (Java Platform SE 8)

【Java】Jacksonを使ってJSON文字列←→オブジェクト変換を行う

Jacksonとは

github.com

JacksonJavaで使えるJSONライブラリです。
JSON形式の文字列をJavaのオブジェクトに直接マッピングしたり、逆にJavaオブジェクトをJSON文字列に変換できたりします。

HTTPレスポンスをパースしたりオブジェクトをBase64エンコーディングする時などに便利です。

Usage

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {

    public static class Obj {
        private String name;
        private int value;

        public Obj(){
        }

        public Obj(String name, int value){
            this.name = name;
            this.value = value;
        }
    }

    public static void main(String... args){
        Obj obj = new Obj("apple", 100);
        String json = getJSONFromObj(obj);
        Obj newObj = getObjFromJSON(json);
    }

    // JavaオブジェクトをJSONに変換
    private String getJSONFromObj(Obj obj) {

        String json = null;
        ObjectMapper mapper = new ObjectMapper();
        try {
            json = mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        return json;
    }

    // JSONをJavaオブジェクトに変換
    private Obj getObjFromJSON(String json) {
        ObjectMapper mapper = new ObjectMapper();
        Obj obj = null;
        try {
            obj = mapper.readValue(json, Obj.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

パース時の注意

JacksonのObjectMapperでデシリアライズする際のクラスは、

  • 引数なしコンストラクタが定義されていなければならない
  • インナークラスはstaticでなければならない(アウタークラスがインスタンス化されないとクラス定義がオンメモリにならないため)
  • JSONプロパティはすべて変換後のクラスメンバに存在しなければならない

という制約をクリアしている必要があります。
これらの制約に反していた場合には例外がスローされます。

具体的な注意点は、こちらの方の記事でとてもわかり易くまとめてあったので引用させていただきます。

qiita.com

【OAuth】Google/Facebook/TwitterのOAuth認証APIに設定するリダイレクトURLにパラメータを含める

Google/Facebook/Twitter のOAuth API

Using OAuth 2.0 to Access Google APIs  |  Google Identity Platform  |  Google Developers

ウェブ - Facebookログイン - ドキュメンテーション - 開発者向けFacebook

Oauth with the Twitter API — Twitter Developers

SNS認証におけるリダイレクト

GoogleFacebookTwitterOAuth認証APIを利用して各SNSで認可を行う際、自身が管理するアプリのサーバにリダイレクトさせて認証状況を確認したい場合があります。

このとき、各SNSOAuth認証APIを叩く前にリダイレクトしてもらうURLを設定します。

# リダイレクトURL
# APIサーバが提供するパスなど
https://hoge.net/oauth/callout/google

もし、リダイレクト前の状態をリダイレクト先に渡したい場合、
リクエストパラメータをクエリストリングに追加したい・・・等考えると思いますが、一部のSNSはリダイレクトURLにパラメータを直接指定すると404を返してきます。

# NG(404が返る)
https://hoge.net/oauth/callout/google?param=hoge

f:id:rennnosukesann:20180511140026p:plain

どうやら、リダイレクト先にパラメータを渡すためにはちょっとした工夫が必要なようです。

リダイレクト先にパラメータを渡す方法:Google

GoogleのOAuthAPIのリダイレクトURLには、直接パラメータを指定することができません。

かわりに、認証サーバへのリクエストURLであるAuthorizationURLにパラメータstateを介してパラメータを渡すことができます。

Javaで実装する場合こんな感じ。

ServiceBuilder builder = new ServiceBuilder()
                                         .apiKey(apiKey) 
                                         .apiSecret(apiSecret) 
                                         .scope(scope)
                                         .state(state) // リダイレクトURLに含めたいパラメータを指定可能(文字列)
                                         .callback(callback) // リダイレクトURL
                                         .build(GoogleApi20.instance());

上記コードではScriveJavaというJavaのOAuthクライアントライブラリを使用しています。
OAuthに対する認証リクエストを投げるのに便利です。

github.com

リダイレクト先にパラメータを渡す方法:Facebook

Facebookも同様に、AuthorizationURLのパラメータstateを指定することでリダイレクトURLパラメータを挿入します。

ServiceBuilder builder = new ServiceBuilder()
                                         .apiKey(apiKey) 
                                         .apiSecret(apiSecret) 
                                         .scope(scope)
                                         .state(state) // リダイレクトURLに含めたいパラメータを指定可能(文字列)
                                         .callback(callback) // リダイレクトURL
                                         .build(FacebookApi.instance());

Googleと大差ありませんね。

リダイレクト先にパラメータを渡す方法:Twitter

一方で、TwitterはリダイレクトURLへのパラメータ指定を許しているみたいです。

# NG(404が返る)
String callback = "https://hoge.net/oauth/callout/google?param=hoge";
ServiceBuilder builder = new ServiceBuilder()
                                         .apiKey(apiKey) 
                                         .apiSecret(apiSecret) 
                                         .scope(scope)
                                         .callback(callback) // リダイレクトURL
                                         .build(TwitterApi.Authenticate.instance());

逆に、stateにパラメータを設定してもリダイレクトURLにパラメータが挿入されません。
GoogleFacebookと異なるので注意したいところです。

参考

stackoverflow.com

stackoverflow.com

stackoverflow.com

【AngularJS】未コンパイル状態のHTMLテンプレートが一瞬表示されるのを防ぐ

テンプレートのちらつき

AngularJSのHTMLテンプレートを用いたページを表示したときに、一瞬だけ元のテンプレート表記がちらついてしまう場合があります。

例えば、以下のようなコードでHTML上の{{message}}に動的に文字列を挿入しようとしたとき、下の画像のように一瞬だけ元の文字列{{message}}の状態で表示されてしまうことがあります。

<div ng-app="myApp">
  <div ng-controller="MyAppCtrl">
    {{message}}
  </div>
</div>
let mod = angular.module('myApp',[]);
mod.controller('MyAppCtrl', ['$scope', '$timeout', function($scope, $timeout){
    $scope.message = "Hello!";  
}]);
結果

f:id:rennnosukesann:20180509221001p:plain

ng-cloak

このチラツキを防ぐために、ng-cloakディレクティブを使用します。
このディレクティブを指定したタグは、テンプレートにデータが挿入されるまで非表示状態になります。テンプレートにデータが挿入されたときタグは表示状態となり、未コンパイル状態のテンプレートが露出されずに済みます。

<div ng-app="myApp">
  <div ng-controller="MyAppCtrl" ng-cloak>
    {{message}}
  </div>
</div>
結果

f:id:rennnosukesann:20180509222125p:plain

内部では、画面読み込み時にng-cloakを指定したタグに対して下記のCSSルールが適用されています。そしてng-cloakディレクティブを指定したタグの内側に含むテンプレートがコンパイルされるとき、AngularJSはng-cloak属性を破棄します。

[ng:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
  display: none !important;
}

この一連の操作によって、「テンプレートにデータが挿入されるまでタグを表示しない」処理を実現しています。

なおng-cloakは指定したタグの子要素もすべて非表示にしてしまいます。そのため<body ng-cloak>とするなど、指定の仕方によってはページの表示が遅延しているように見えてしまうため、ユーザ体験を悪化させる要因になりかねません。

ちらつく可能性のあるテンプレートが特定の領域に限られている場合には、その領域のタグに限定してng-cloakを付加するのが望ましいです。

参考文献

ngCloak | AngularJS 1.2 日本語リファレンス | js STUDIO

【AngularJS】$httpによる通信を任意のタイミングで中断する

$httpのタイムアウト

AngularJSではHTTPリクエストを投げるためのサービス$httpが提供されていますが、
引数configのプロパティtimeoutを設定することで、HTTPリクエストをタイムアウトさせることができます。

timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.

AngularJS: API: $httpより引用

timeoutには整数型とPromise型オブジェクトを設定することができます。
整数型を設定した場合には指定整数ミリ秒後にリクエストをタイムアウトします。
Promise型を設定した場合には、Promise生成元のDeferedオブジェクトに対してDefered.resolve()がコールされたタイミングでリクエストがタイムアウトされます。

Usage

let mod = angular.module('myApp', []);
mod.controller('MyAppCtrl', ['$scope', '$http', '$q', '$timeout', function($scope, $http, $q, $timeout){
                             
  $scope.onButtonClick = function onButtonClick() {
    
    let defered = $q.defer();

    let success = function(response) {
      $scope.message = "success!";
      $scope.response = response;
    };

    let error = function(error) {
      $timeout(function(){
        $scope.message = "error.";
        $scope.response = error;
      });
    };

    // 1) ミリ秒指定
    $http({ method: "GET", url : "/hoge/fuga/api", timeout: 3000 })
    .then(success)
    .catch(error);

    // 2) Promiseオブジェクトを指定
    $http({ method: "GET", url : "/hoge/fuga/api", timeout: defered.promise })
    .then(success)
    .catch(error);           
    
    // Promiseオブジェクトをtimeoutに指定した場合、Defered.resolve()を呼ぶとタイムアウトする
    // 1) と同じ挙動
    $timeout(function(){
      defered.resolve();
    }, 3000);

  };
 
}]);

【AngularJS】プルダウンの一部を動的に選択不可能にする

メモ。

AngularJSではselectタグにng-optionsを指定するとIterableオブジェクトの要素からなるプルダウンを作成できますが、その一部をdisable状態にすることができます。

HTML
<div ng-app="myApp">
  <div ng-controller="NgOptionDisableCtrl">
    <select ng-model="num" ng-options='number disable when number === 3 for number in numbers'></select>
  </div>
</div>
JS
let mod = angular.module('myApp',[]);
mod.controller('NgOptionDisableCtrl', ['$scope', function($scope){
  $scope.num = 1;
  $scope.numbers = [1,2,3,4,5];
}]);


codepen.io


ng-options[変数] disable when [条件式] for ...と指定することで、条件をみたす場合にdisable状態になります。

上記コードでは、プルダウンの3を選択できないようになっています。

<select ng-model="num" ng-options='number disable when number === 3 for number in numbers'></select>

【JavaScript】反復可能なオブジェクトをArrayとして扱う

Array.from

Array.fromメソッドは、配列型オブジェクトや反復可能なオブジェクトをArray型にキャストするために用います。

// 配列
let ary = Array.from([1,2,3]);
console.log(ary);// [1, 2, 3]

// Set
let set = Array.from(new Set(["a",2,{}]));
console.log(set);// ["a", 2, Object {}]

// Map
let map_ = Array.from(new Map([[1,2], ["a", 3]]));
console.log(map_);// [[1, 2], ["a", 3]]

// String
let str = Array.from("Array");
console.log(str);// ["A", "r", "r", "a", "y]

ちなみに反復可能なオブジェクトなら変換可能なので、前回記事で扱った<input type='file'>でイベントリスナーを登録したときに取得できるFileListオブジェクトもArray.fromで配列に変換できます。

HTML
<input id="input_file" type="file" multiple/>
JS
function onChange(event) {
  // 複数選択したファイルをFileList型オブジェクトとして取得->Arrayに変換
  let files = Array.from(event.target.files);
  //  ...
}
document.getElementById("input_file").addEventListener('change', onChange, false);