Spring Boot 1.4.1 - Controller

Controller

Thymeleaf については次のリンクを参照してください。

http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf_ja.html

Thymeleaf を Spring Boot で使えるようにするために、build.gradle の dependencies に次の行を追加します。

 compile('org.springframework.boot:spring-boot-starter-thymeleaf')

1. Index

PROJECT_ROOT/src/main/resources/templates/index.html を作成します。

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8"/>
    <title th:text="#{title}">TITLE</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <p th:text="#{welcome}">WELCOME</p>
  </body>
</html>

PROJECT_ROOT/src/main/resources/messages.properties を作成します。

title=title
welcome=Welcome!!

コントローラを作成します。

package com.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    @GetMapping("/")
    public String index() {
        return "index";
    }

}

コントローラのメソッドが返す文字列に .html を付加した templates ディレクトリにあるファイルを Thymeleaf のテンプレートとして使用して、HTML をブラウザに返します。

./gradlew bootRun

で起動して、ブラウザで http://localhost:8080 にアクセスした画面は下のようになります。

f:id:section27:20161025222402p:plain

2. ロケール

デフォルトでは、org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver を使用して、ブラウザから送られる accept-language を使用したロケールでメッセージが表示されます。

org.springframework.web.servlet.i18n.CookieLocaleResolver, org.springframework.web.servlet.i18n.SessionLocaleResolver, org.springframework.web.servlet.i18n.FixedLocaleResolver を使用した、クッキー、セッション、固定といったあらかじめ用意された LocaleResolver を使用することもできます。

ここでは独自の LocaleResolver を作成して日本語(osaka_JP)になるようにしてみます。

package com.example;

import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

public class OsakaLocaleResolver implements LocaleResolver {

    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        return new Locale("osaka", "JP");
    }

    @Override
    public void setLocale(HttpServletRequest request, 
            HttpServletResponse response,
            Locale locale) {
        throw new UnsupportedOperationException("unsupported");
    }
}
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    OsakaLocaleResolver localeResolver() {
        return new OsakaLocaleResolver();
    }
}

PROJECT_ROOT/src/main/resources/messages_osaka.properties を作成します。

title=タイトル
welcome=大阪にようこそ

実行して、http://localhost:8080 にアクセスしてみます。

./gradlew bootRun

f:id:section27:20161025222418p:plain

Spring Boot 1.4.1 - RestController

1. Hello World

コントローラクラスを作成します。

最初にテストを記述します。

package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    @Autowired
    TestRestTemplate template;

    @Test
    public void contextLoads() {
        ResponseEntity<String> res = template.getForEntity("/", String.class);
        assertThat(res.getStatusCode(), is(HttpStatus.OK));
        assertThat(res.getBody(), is("Hello World!!"));
    }

}

次に、IndexControllerを作成します。

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/")
    public String index() {
        return "Hello World!!";
    }
}

REST のコントローラには、@RestController アノテーションを使ってクラスを作成します。 コントローラのメソッドには URL のマッピングを設定します。 ここで使用している GetMapping 以外に、PostMapping や、PutMpping、DeleteMapping 等があります。

最初のステップでは、Hello World!! という文字列を返しました。

2. コントローラのメソッドの引数

コントローラのメソッドは、HttpServletRequest や HttpServletResponse やその他さまざまなオブジェクトを受け取ることができます。

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

@RestController
public class IndexController {

    @GetMapping("/")
    public String index(HttpServletRequest req, HttpServletResponse res) {
        Objects.nonNull(req);
        Objects.nonNull(res);
        return "Hello World!!";
    }
}

3. PathVariable

URLパスの変数の値を受け取ることができます。

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/{id}")
    public String index(@PathVariable String id) {
        return "ID: " + id;
    }
}
package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    @Autowired
    TestRestTemplate template;

    @Test
    public void contextLoads() {
        ResponseEntity<String> res = template.getForEntity("/123", String.class);
        assertThat(res.getStatusCode(), is(HttpStatus.OK));
        assertThat(res.getBody(), is("ID: 123"));
    }

}

4. Request パラメータ

Request パラメータを受け取ることももちろんできます。

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/foo")
    public String index(@RequestParam String id) {
        return "ID: " + id;
    }
}
package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    @Autowired
    TestRestTemplate template;

    @Test
    public void contextLoads() {
        ResponseEntity<String> res = template.getForEntity("/foo?id=bar", String.class);
        assertThat(res.getStatusCode(), is(HttpStatus.OK));
        assertThat(res.getBody(), is("ID: bar"));
    }

}

ModelAttribute

Request パラメータが多い場合等は、ModelAttribute を使って、Request パラメータの値が設定されたオブジェクトを受け取ることができるようにすることができます。

package com.example;

public class Foo {

    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}
package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/foo")
    public String index(@ModelAttribute Foo foo) {
        return "ID: " + foo.getId();
    }
}
package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    @Autowired
    TestRestTemplate template;

    @Test
    public void contextLoads() {
        ResponseEntity<String> res = template.getForEntity("/foo?id=bar", String.class);
        assertThat(res.getStatusCode(), is(HttpStatus.OK));
        assertThat(res.getBody(), is("ID: bar"));
    }

}

5. HandlerMethodArgumentResolver

HandlerMethodArgumentResolver の実装クラスを作成して、任意のオブジェクトをコントローラのメソッドで受け取ることができるようにすることができます。 Spring Security を使ってログインしているユーザの Principal をコントローラで受け取る場合などこの仕組みが多く使用されています。

package com.example;

public class Foo {

    public final String foo = "foo";

}
package com.example;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class FooHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Foo.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, 
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory)
            throws Exception {
        return new Foo();
    }
}
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.List;

@SpringBootApplication
public class DemoApplication extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(
            List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new FooHandlerMethodArgumentResolver());
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/")
    public String index(Foo foo) {
        return foo.foo;
    }
}
package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    @Autowired
    TestRestTemplate template;

    @Test
    public void contextLoads() {
        ResponseEntity<String> res = template.getForEntity("/", String.class);
        assertThat(res.getStatusCode(), is(HttpStatus.OK));
        assertThat(res.getBody(), is("foo"));
    }

}

macOS Sierraにawscliインストールしてみる

pipのインストール

pip installationで、get-pip.pyをダウンロードします。

sudo python get-pip.py

AWS CLIをインストール

sudo pip install awscli --upgrade --ignore-installed six

AWS Access Key IDとAWS Secret Access Keyの取得

IAMでユーザを作成して、AWS Access Key IDとAWS Secret Access Keyの取得

リージョンを調べる

AWS Regions and Endpoints

awsコマンドの初期設定

aws configure
AWS Access Key ID [None]: xxx
AWS Secret Access Key [None]: xxx
Default region name [None]: ap-northeast-1
Default output format [None]:

設定した内容の確認

cat .aws/config
cat .aws/credentials

VPCでsubnet-idを調べる

EC2ダッシュボードで、キーペアを作成(MyKeyPair)

インスタンスの作成

とりあえず、デフォルトVPCで作成

# RDS
aws rds create-db-instance \
  --db-instance-identifier testdb \
  --db-instance-class db.t2.micro \
  --engine mysql \
  --engine-version 5.7.11 \
  --master-username dbuser \
  --master-user-password password \
  --allocated-storage 5

# AP
aws ec2 run-instances \
  --image-id ami-831fcde2 \
  --count 1 \
  --instance-type t2.micro \
  --key-name MyKeyPair \
  --security-group-ids sg-xxx \
  --subnet-id subnet-xxx
aws ec2 run-instances \
  --image-id ami-831fcde2 \
  --count 1 \
  --instance-type t2.micro \
  --key-name MyKeyPair \
  --security-group-ids sg-xxx \
  --subnet-id subnet-xxx

TypeScriptはじめました

GitHubリポジトリ

typescript-cryptojs

動かし方

npm install
npm start

動くようになるまでにしたこと

初期化

npm init
typings init

最初にcrypto-jsとその型定義

npm install crypto-js --save
typings install dt~cryptojs --save --global

tsconfig.jsonを記述

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "removeComments": false
    },
    "files": [
        "typings/index.d.ts"
    ]
}

app/main.tsを書く前に

これだけだと、requireファンクションを使うことができなかった。 Angular2のtypings.jsonをみて、少し追加。

typings install dt~node --save --global

これで、requireが使えるようになる。 だけど、とりあえず、Angular2を参考にcore-jsとjasmineも追加しておくことにした。

typings install dt~core-js --save --global
typings install dt~jasmine --save --global

app/main.tsを書く

Visual Studio Codeを使ってさくさくっと記述。型があるのは補完とかがきいて便利。

Seleniumメモ

サポートされているプログラミング言語

できること

Selenium WebDriverを使ってさまざまなブラウザを自動的に操作することができます。

Azure IoT HubのMQTT(s)

環境

MQTTに接続するユーザ名とパスワード

ユーザ名

{iothubhostname}/{device_id}

パスワード

SharedAccessSignature sig={signature-string}&se={expiry}&sr={URL-encoded-resourceURI}

String password = String.format(
    "SharedAccessSignature sig=%s&se=%s&sr=%s",
    URLEncoder.encode(sig, StandardCharsets.UTF_8.name()),
    expiry,
    scope);

expiry

有効期限でUNIXタイムスタンプ(秒)のlong値です

URL-encoded-resourceURI(scope)

{iothubhostname}/devices/{device_id}

signature-string(sig)

バイスのキーをBase64でデコードしたバイナリをHMAC-SHA256のキーとして使用します。

String deviceKey = properties.getDeviceKey();

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(Base64.getMimeDecoder().decode(deviceKey),
    "HmacSHA256"));

URL-encoded-resourceURIとexpiryを改行で分けたメッセージを署名します

String scope = String.format("%s/devices/%s",
    properties.getHostname(),
    URLEncoder.encode(properties.getDeviceId(), StandardCharsets.UTF_8.name()));
String message = String.format("%s\n%s", scope, expiry);
String sig = Base64.getMimeEncoder().encodeToString(mac.doFinal(message.getBytes()));

IoT Hubへのメッセージの送信

devices/{device_id}/messages/events/ または devices/{device_id}/messages/events/{property_bag} をトピック名としてメッセージを送信できます。

IoT Hubからのメッセージの受信

devices/{device_id}/messages/devicebound/# をトピックフィルターとして使用してサブスクライブできます。

GitHub

github.com