2018年6月10日日曜日

zshのviモードでCtrl+Rでコマンドの履歴検索をできるようにする

これでCtrl+Rでコマンドの履歴検索ができるようになる。

bindkey '^R' history-incremental-pattern-search-backward

2018年5月28日月曜日

Spring MVCとThymeleafの組み合わせでcheckboxのデフォルト値を設定する

Spring MVCとThymeleafの組み合わせでcheckboxを使った場合の動きを理解する。

checkboxの値をBooleanで受け取る場合

Form

public class Form {

    private Boolean check;

    // ほかは省略
}

Controller

checkboxの値を確認できるように標準出力に値を出力するだけに
@PostMapping("hello")
public String hello(Form form) {
    System.out.println("form.check = " + form.check);
    return "input";
}

View

<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" />
  <button type="submit">submit</button>
</form>

動かしてみた結果

出力されるhtmlには、checkboxとcheckboxに対応したhiddenが出力されます。このhiddenがあることでcheckboxがチェックされなかった場合でもFormに値が設定されます。(Booleanなのでfalseが設定される)
これは、thymeleafのth:fieldで自動的に出力されます。
<form action="/hello" method="post">
  <input type="checkbox" id="check1" name="check" value="true" /><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

checkboxをチェックしないで送信した時は標準出力に↓が出力されます。
form.check = false

ちなみに、th:fieldを使わないと下のようにhiddenが出力されないので、checkboxがチェックされなかった場合はFormには値が設定されないのでnullのままとなります。
<form action="/hello" method="post">
  <input type="checkbox" name="checkbox" />
  <button type="submit">submit</button>
</form>

checkboxの値をBoolean以外で受け取る場合

単一のcheckboxの値をBooleanで受け取ることはないと思うけど、Stringで受け取った場合の動きを確認してみます。

Stringの場合も、Booleanと同じようにth:fieldを使うと、↓が出力されます。だけど、Stringの場合はth:fieldが出力するアンダースコア付きのhiddenに対応していないので、このままだと未チェック時はFormの値はnullのままになります。

<form action="/hello" method="post">
  <input type="checkbox" value="on" id="check1" name="check"/><input type="hidden" name="_check" value="on"/>
  <button type="submit">submit</button>
</form>

未送信時に送信する値は、別途↓のようにhiddenで定義します。デフォルト設定だと、対応するcheckboxのnameの先頭に!をつけた名前でhiddenを定義する必要があります。
プレフィックスの値はWebDataBinderのJavadocが参考になります。
<form th:action="@{/hello}" method="post" th:object="${form}">
  <input type="checkbox" th:field="*{check}" value="on" />
  <input type="hidden" name="!check" value="off" />
  <button type="submit">submit</button>
</form>

これで、未チェック時にはhiddenで指定した値をFormで受け取れるようになります。
form.check = off

2018年5月2日水曜日

Spring Securityで認証後にかならず決まったページに遷移させる

defaultSuccessurlの2番目の引数にtrueを指定すると、ログイン成功後に必ず最初に指定したパスにリダイレクトされる。
@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .formLogin()
        .defaultSuccessUrl("/index", true)
        .and()
        .logout()
        .invalidateHttpSession(true);
}

2018年4月3日火曜日

Spring BootとFlywayでテーブルなどがあるスキーマに対してマイグレーションを実行する

application.propertiesに下を追加して、既存の状態をバージョン001とすることでマイグレーションが可能になる。
アプリ側のSQLファイルのバージョンは、V002__Create_hoge_table.sqlのようにベースバージョンより大きいバージョンにする。

spring.flyway.baseline-on-migrate=true
spring.flyway.baseline-version=001



なお、デフォルト状態だとFound non-empty schema "PUBLIC" without metadata table!のエラーが出て実行できない。

2018年3月25日日曜日

SQL Serverの計算列

SQL Serverでは、テーブル作成時にカラム名の後にasで計算式を書くことで計算結果をそのカラムに自動的に設定できる。
この計算列は、計算結果が格納されるのでINSERTやUPDATEに含めることはできない。

テーブル定義

create table test
(
  id   int not null
    primary key,
  num1 int,
  num2 int,
  num3 as [num1] + [num2]
)


insertの実行

計算列のnum3は含めずに登録を行う。
insert  into test (id, num1, num2) values (1, 1, 2)

結果の確認

num3には、num1とnum2を元に計算した結果が格納されているのがわかる。
1> select * from test
2> go
id          num1        num2        num3       
----------- ----------- ----------- -----------
          1           1           2           3

(1 rows affected)

2018年3月21日水曜日

Tomcatでポストサイズの上限を設定する

HTTP ConnectorにmaxPostSizeを設定するとポストサイズの上限を設定できるようなので動きを確認してみた。

確認用Servlet

確認用のServletではpostされた内容を標準出力にだしています。
override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
    println("Content-Length: ${req.contentLengthLong}")
    println("parameter count: ${req.parameterMap.size}")
    req.parameterMap.forEach {
        println("${it.key}: ${it.value.joinToString(", ")}")
    }
    doGet(req, resp)
}

デフォルト設定で実行

デフォルト設定だと上限が2MBとなるのでエラーなどは発生しないでうまく処理が行われる。
➜  ~ curl http://localhost:8080/sample/hello --data-urlencode "param_key=値" -X POST -i     
HTTP/1.1 200 

サーバの標準出力の内容
Content-Length: 19
parameter count: 1
param_key: 値

maxPoolSizeを指定して実行

conf/server.xmlのConnector部分にmaxPostSizeの設定を追加する。簡単に上限を超えるように設定値は1バイトに。
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxPostSize="1"
           redirectPort="8443" />

デフォルトの設定だと、クライアントには200が返ってくる。
➜  ~ curl http://localhost:8080/sample/hello --data-urlencode "param_key=値" -X POST -i 
HTTP/1.1 200 

サーバのログを見ると、パラメータが存在しない状態でServletが呼び出されている。
このような場合、リクエストスコープのorg.apache.catalina.parameter_parse_failed_reasonの値(org.apache.tomcat.util.http.Parameters.FailReason)を見ると何が起きたかがわかるらしい。
Content-Length: 19
parameter count: 0

FailedRequestFilterを適用して実行

web.xmlにFailedRequestFilterを追加する。
<filter>
  <filter-name>failedRequestFilter</filter-name>
  <filter-class>org.apache.catalina.filters.FailedRequestFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>failedRequestFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Filterで指定サイズを超えた場合は処理を中断し、413 Request Entity Too Largeが返されるようになる。
➜  ~ curl http://localhost:8080/sample/hello --data-urlencode "param_key=値" -X POST -i 
HTTP/1.1 413 


ちなみにこのフィルターはapplication/x-www-form-urlencoded以外には影響を与えないようです。例えば、application/jsonの場合には下のように正常に処理が終わります。
➜  ~ curl http://localhost:8080/sample/hello -H "Content-Type: application/json" -d "{}"  -i   
HTTP/1.1 200 

2018年3月6日火曜日

Versions Maven Pluginを使ってparentのバージョンを変更する

org.codehaus.mojo:versions-maven-plugin:2.5:update-parent
で参照可能な最新バージョンに変更できる。

parentVersionを使用して、バージョン番号の範囲を指定できる。

例えば、下のように1.0.0か1.1.0のように指定する。
org.codehaus.mojo:versions-maven-plugin:2.5:update-parent -DparentVersion=[1.0.0,1.1.0]