tokuhirom's blog

How do I set TemplateExceptionHandler with spring-boot+freemarker?

You should use one of following properties.

-Dspring.freemarker.template_exception_handler=rethrow
-Dspring.freemarker.template_exception_handler=debug
-Dspring.freemarker.template_exception_handler=html_debug

Thanks,

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

jspm で angular2 を使ってみる。

jspm+systemjs を angular2 では推してるっぽいし使ってみるかとおもって使ってみるとなかなか一筋縄ではいかない。 ベータ! という感じがしてよい。

そういうわけで、jspm+systemjs でやってみた記録です。

node はすでにセットアップ済みという前提です。

npm install -g typescript jspm

として、必要なツールをインストールする。

次に jspm の初期設定をする。

$ jspm init
Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [./]:
Enter jspm packages folder [./jspm_packages]:
Enter config file path [./config.js]:
Configuration file config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Do you wish to use a transpiler? [yes]:
Which ES6 transpiler would you like to use, Babel, TypeScript or Traceur? [babel]:typescript
(以下略)

なんかいろいろ聞かれてセットアップが完了する。

次に依存モジュールをインストールする。

jspm install angular2 reflect-metadata zone.js es6-shim typescript

config.js の中身を以下の様な感じにいじる。そんなに意味不明な設定はない。

System.config({
    baseURL: "/",
    defaultJSExtensions: true,
    transpiler: "typescript",
    typescriptOptions: {
        "module": "commonjs",
        "emitDecoratorMetadata": true
    },
    paths: {
        "github:*": "jspm_packages/github/*",
        "npm:*": "jspm_packages/npm/*"
    },

    packages: {
        "app": {
            "main": "main",
            "defaultExtension": "ts"
        }
    },

    map: {
        /* ここは自動生成される */
    }
});

で、app/main.ts に以下のように書く。基本的には、通常の angular2 アプリと一緒なのだが、依存を明示しないといけない点があってダサい。 この依存は config.js の中に 'meta' 項目として記載することも可能なのだが、まあ import を明示的にしておいたほうがわかりやすいだろうということで import した。

//import deps
import 'zone.js';
import 'zone.js/dist/long-stack-trace-zone';

import 'reflect-metadata';
import 'es6-shim';

// app code
import {Component} from 'angular2/core';
import {bootstrap} from "angular2/platform/browser";

@Component({
    selector: 'test-app',
    template: '<h4>Hello {{name}}</h4>'
})
class TestApp {
    name: string;

    constructor(){
        this.name = 'Angular2';
        setTimeout(() => {
        this.name = 'Angular2!!!'
        },1500);
    }
}

//start our app
bootstrap(TestApp);

最後に index.html に以下のように書く。

<html>
<head>
    <title>Demo App</title>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
</head>
<body>
    <test-app>
        Loading...
    </test-app>

    <script>
        System.import('app');
    </script>
</body>
</html>

bundle

jspm bundle-sfx --minify app

とするだけで bundle 版が作成可能。bundle-sfx で作成した場合、system.js も同梱されてるので、取り扱いが楽。

で、production 環境では以下のように、build.js だけを読めばOK.

<html>
<head>
    <title>Demo App</title>
</head>
<body>
    <test-app>
        Loading...
    </test-app>
    <script src="build.js"></script>
</body>
</html>

今後の展望

java app での良い感じの取り回しについて考えていきたい。

たとえば以下のような構成にするのが良いのではないかと考える。

src/main/java
src/main/resources/static/index.html ← build.js を読む
front/app/main.ts
front/index.html                     ← config.js 読んで、動的に処理

で、web server は file:front/,classpath:/static/ を static file として扱うようにして、gradle で jspm をキックして、build/resources/static/build.js を生成すればよい。

というところまで考えたのでよろしくお願いします。

まとめ

JS 系のツールは、なんかいろいろと設定が膨らみがちだが、jspm は最小限の設定ですむのでいい感じっぽい。 (最初 webpack でやろうと思っていたが、jspm の方が楽だった)

Java アプリとの連携については、ある程度、こんな感じでやればできるなあという感触をつかめた。

どうしてもブラウザでの確認がめんどくさいので、Ajax 通信部分のモッキング含めて、いい感じにテストできるようにしたい。そのへんもちゃんと読まないとな。 https://angular.io/docs/ts/latest/guide/testing.html

あと、本稿の内容は以下の gist の内容を参考に最新版でも動くようにしたものです。 https://gist.github.com/robwormald/429e01c6d802767441ec

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

spring boot アプリケーションのMetrics値取得について

Spring boot の metrics 機能について

spring boot には metrics 機能が標準でついている。metrics では Counter と Gauge の2種類のメトリクスを食わせることが可能である。

Counter は、単純に増加/現象する値を表すのに使うメトリクス値である。 たとえば、アクセスの件数、エラーの件数、現在利用中の値を取得したりするのに使う。

サンプルコードは以下の通り。

@Service
public class FooService {
    @AutoWired
    private CounterService counterService;
    @Autowired
    private FooRepository fooRepository;

    public Foo get(String key) {
        counterService.increment("foo");
        return fooRepository.get(key);
    }
}

Gauge は、単純に数値を渡すのに使う。普通に使うと、直近の一個の値がそのまま metrics 数字を取得できるだけなので、利用方法がちょっと思いつかない。 サンプルコードとしては以下の様な感じで利用される。

gaugeService.set("foo", 3.14);

Metrics 値の利用

Actuator での閲覧

特に設定せずに spring boot アプリケーションを起動すると、http://localhost:8080/metrics からアプリケーションのメトリクス値が取得可能になっている。

ここを閲覧して、変な値が出ていないか確認することができる。 ここを cron で取得して、alert を上げるなどの利用法が行われている。と思う。

外部レポジトリへの送信

spring-boot の metrics では、データを外部のミドルウェアに送信することができる。以下のミドルウェアに送信するための exporter が標準添付されている。

  • graphite
  • open tsdb
  • redis

gauge の値は actuator で確認するぶんには、最後の一個が見れるだけなので、使いにくいが、各値を外部 middleware に保存することで可能性が広がる。

Dropwizard Metrics との統合

dropwizard という web application framework には metrics という似たようなしくみがあるのだが、dropwizard の metrics の方がよく作りこまれている。。んですよ。 で、spring-boot の metrics は、classpath に dropwizard metrics があれば、そっちを使うように実装が差し替わります。

Dropwizard Metrics を利用するようにすると、gauge を取得するときに、prefix に 'histogram.' あるいは 'timer.' をつけると、生の値ではなく、min/max/stddev/percentile などの値が取得できるようになります。

Dropwizard metrics では、高速にメトリクス集計することが可能。Reservoir というコンポーネントで、集計を行っている。 Reservoir は性質のことなるによって幾つかの実装が存在している。

Uniform Reservoirs

Uniform Reservoirs は、Vitter's Algorithm R ってやつでランダムサンプリングして集計している。 長期間の間のメトリクスを取るのに向いてる。データの傾向が変わったことを検出するのとかには向いてない。

Exponentially Decaying Reservoirs

http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf 詳しくはこのへん。

過去、約5分間の傾向をしる事ができる。メトリクス傾向の変化を即座に検知したいなんてときにはこれ。 spring-boot-actuator の histogram 実装はこれをデフォルトとして選択している。 とりあえずこれをそのまま使っておけばいい。と思う。

Sliding Window Reservoirs

過去N個のデータを利用して値を出す

Sliding Time Window Reservoirs

過去N秒のデータを利用して結果を出す。データの流量が多いとメモリ量めっちゃ食って死ぬ。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

oh-my-zsh やめて zplug にしようかと思ったけどやっぱり zgen にした

mput さんの記事→ https://moneyforward.com/engineers_blog/2016/03/15/dotfiles/ を見て、zplug というものの存在を知った。

zplug を設定しようと思ったが、そもそも zsh の設定をこまごまとやるのがあんま好きじゃないし、コマンドのインストールとかまで出来るのが zplug だということで、ややオーバースペックに感じた。 いろいろ見ていると zgen というのが好みにあってる感じっぽかったので zgen にした。

source "${HOME}/.zgen/zgen.zsh"

if ! zgen saved; then
    zgen oh-my-zsh

    zgen oh-my-zsh plugins/git
    zgen oh-my-zsh plugins/ssh-agent

    zgen load zsh-users/zsh-syntax-highlighting
    zgen load "zsh-users/zsh-completions"

    if [[ $HOST = 'www3301gi.sakura.ne.jp' ]]; then
        zgen oh-my-zsh themes/clean
    else
        zgen oh-my-zsh themes/sonicradish
    fi

    zgen save
fi

oh-my-zsh の機能もそんなに使ってなかったし、まあこれでいいかという感じ。

mput さんがおすすめしていた predict on も試してみたが、git stash ってウトウトしたらうっかり git stash pop 実行してしまったりして、イライラするばかりだったのでやめました。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

spring で controller 書くほどでもないんだけどテンプレートエンジンでレンダリングして欲しい時のやり方

addViewControllers ッテの使うと良いっぽい。

@Configuration
@Slf4j
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
}

もともと java だと JSP でゴーリゴリみたいな世界観の人もいるので、そういう人が使いやすいようにする処置っぽい。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Spring の Freemarker で参照可能な変数について

Spring で freemarker を利用している場合、いくつかの変数がデフォルトで利用可能になっている。

    protected SimpleHash buildTemplateModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
        AllHttpScopesHashModel fmModel = new AllHttpScopesHashModel(getObjectWrapper(), getServletContext(), request);
        fmModel.put(FreemarkerServlet.KEY_JSP_TAGLIBS, this.taglibFactory);
        fmModel.put(FreemarkerServlet.KEY_APPLICATION, this.servletContextHashModel);
        fmModel.put(FreemarkerServlet.KEY_SESSION, buildSessionModel(request, response));
        fmModel.put(FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(request, response, getObjectWrapper()));
        fmModel.put(FreemarkerServlet.KEY_REQUEST_PARAMETERS, new HttpRequestParametersHashModel(request));
        fmModel.putAll(model);
        return fmModel;
    }

https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerView.java#L295-L304

これはつまり、Freemarker がデフォルトで提供している Servlet と同等の変数を埋めているということになる。 参考: http://freemarker.incubator.apache.org/docs/pgui_misc_servlet.html

  • JspTaglibs
  • Application
  • Session
  • Request
  • RequestParameters

といった値が利用可能なので、場合によったら利用してもよい。(controller できちんと処理したほうがよいケースも多いので、乱用しないように気をつけるべし)

たとえば、Spring Security の login form などは以下のように書けばよいってなわけ。(というか spring security のサンプルコードが thymeleaf だったので悩んでて見つけた)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
>
<head>
    <title>Spring Security Example </title>
</head>
<body>
<#if RequestParameters.error??>
Invalid username and password.
</#if>
<#if RequestParameters.logout??>
You have been logged out.
</#if>
<form action="/login" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
    <div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

[spring] spring mvc で、一部のコントローラは先に処理させたい時のライフハック

/{user_id} はエンドユーザーのページだけど、 /entrylist は entry の list が出る、みたいな、ちょっと アレな URL 設計になってしまったサイトを spring mvc でどのように制御するか。

↓に書いてあるとおり、SimpleUrlHandlerMapping で処理できる。 http://www.mkyong.com/spring-mvc/configure-the-handler-mapping-priority-in-spring-mvc/

ということはわかったら、"SimpleUrlHandlerMapping spring boot" とかでググると、Java configuration での書き方がみつかる。

↓以下参照。 See http://stackoverflow.com/questions/25037113/java-configuration-of-simpleurlhandlermapping-spring-boot

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

fluentd を手元で動かしてデバッグしたいとき

以下のようにして fluentd をインストール。

gem install fluentd --no-ri --no-rdoc

/etc/fluent/fluent.conf に以下のようにかく。

<source>
    type forward
</source>
<match *.**>
    type stdout
</match>

んで、以下のようにして起動。

fluentd --no-supervisor --verbose

起動後は、メッセージ確認可能。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

【解決済み】jackson 2.7 以後は @ConstructorProperties を参照するようになっているので、lombok を使っている場合に何も考えずにアップグレードすると死ぬ

Jackson 2.7 がリリースされていますが、このバージョンから @ConstructorProperties を参照するようになっています。

See

@java.beans.ConstructorProperties は、Java Beans の標準にある annotation です。 これにより、引数を持つコンストラクタを Jackson で利用できるようになります。

たとえば、以下のように、 immutable なオブジェクトを Jackson で扱えるようになるのです!

public class Point {
    @ConstructorProperties({"x", "y"})
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    private final int x, y;
}

そういうわけで、便利なんですが、ここに一つ落とし穴があります。

lombok の @AllArgsConstructor または @Value を利用していて、かつ @JsonProperty でフィールド名を変更している場合です。 つまり、以下のようなケース。

@AllArgsConstructor
@Value
public class Foo {
    @JsonProperty("y")
    private String x;
}

この場合、生成コードは以下のようなものになります。

public class Foo {
    @JsonProperty("y")
    private String x;

    public String getX() { return this.x; }

    @ConstructorProperties({"x"})
    public Foo(String x) { return this.x; }
}

で、こうなった時に、Jackson 氏は @ConstructorProperties@JsonProperty で割り当てられてる名前が違うやんけ! と文句を言ってくるわけです。

再現コードは以下のようになります。

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;

import lombok.AllArgsConstructor;
import lombok.Data;

public class Boo {
    @Data
    @AllArgsConstructor
    public static class Foo {
        @JsonProperty("foo_id")
        private String fooId;
    }

    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaType javaType = objectMapper.getTypeFactory().constructType(Foo.class);
        BeanDescription desc = objectMapper.getSerializationConfig().introspect(javaType);
        List<BeanPropertyDefinition> properties = desc.findProperties();
        System.out.println(properties);
    }
}

で、この挙動を抑制するには、@ConstructorProperties の付与を停止すればよいです。

プロジェクトのルート・ディレクトリに lombok.config というファイルを配置します。 build.gradle と同じディレクトリにおけばOK.

このファイルに以下のように記述すれば、@ConstructorProperties の生成が抑制されるので問題なくなる。

lombok.anyConstructor.suppressConstructorProperties = true

そんな感じです。

【2016.09.08 追記】 最新版では問題発生しなくなっています。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Spring の ConfigurationProperties を kotlin で使う

以下のように、open class にすれば良いらしい。kotlin のクラスはデフォルトで final 指定がつくので、open 指定しないとダメな様子。 @Configuration @ConfigurationProperties(prefix = "koblog") open class KoblogSettings { var filePath: String = "" }

var にして default 値入れないといけないので、負けた感じがすごい。

そのうち、immutable なクラスにも対応する日がくるのでしょう。たぶん。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

gradle で maven central にリリースできた。

http://tagomoris.hatenablog.com/entries/2016/02/16

こちらを参考にしてやったら簡単にできた。 tagomoirs++

// releng: gradle clean && gradle uploadArchives -Prelease

group 'me.geso'
version '1.0.0'

apply plugin: 'java'
apply plugin: "maven"
apply plugin: "signing"

sourceCompatibility = 1.8

//set build variables based on build type (release, continuous integration, development)
def isDevBuild
def isCiBuild
def isReleaseBuild
def sonatypeRepositoryUrl
if (hasProperty("release")) {
    isReleaseBuild = true
    sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
} else if (hasProperty("ci")) {
    isCiBuild = true
    version += "-SNAPSHOT"
    sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
} else {
    isDevBuild = true
    version += "-SNAPSHOT"
}

repositories {
    mavenCentral()
}

task javadocJar(type: Jar) {
    classifier = 'javadoc'
    from "${buildDir}/javadoc"
}

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

artifacts {
    archives javadocJar, sourcesJar
}

signing {
    required { isReleaseBuild }
    sign configurations.archives
}

uploadArchives {
    repositories {
        if (isDevBuild) {
            mavenLocal()
        }
        else {
            mavenDeployer {
                if(isReleaseBuild) {
                    beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
                }

                repository(url: sonatypeRepositoryUrl) {
                    authentication(userName: sonatypeUsername, password: sonatypePassword)
                }

                pom.project {
                    name 'paranoid_dns_resolver'
                    packaging 'jar'
                    description 'Paranoid DNS Resolver'
                    url 'https://github.com/tokuhirom/paranoid_dns_resolver'

                    scm {
                        url "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                        connection "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                        developerConnection "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                    }
                    licenses {
                        license {
                            name 'MIT'
                            url 'https://opensource.org/licenses/MIT'
                        }
                    }
                    developers {
                        developer {
                            id 'tokuhirom'
                            name 'Tokuhiro Matsuno'
                            email 'tokuhirom@gmail.com'
                        }
                    }
                }
            }
        }
    }
}

dependencies {
    compile 'org.apache.httpcomponents:httpclient:4.5.2'
    compile 'com.github.tomakehurst:wiremock:1.58'
    compile 'org.assertj:assertj-core:3.3.0'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.11'
}
Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Spring Boot とファイルアップロード

File upload のサンプルがこれ。 https://spring.io/guides/gs/uploading-files/

http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/MultipartProperties.html このへんによると、

multipart.location specifies the directory where files will be stored. The default is "". A common value is to use the system's temporary directory, which can be obtained.
multipart.maxFileSize specifies the maximum size permitted for uploaded files. The default is 1Mb.
multipart.maxRequestSize specifies the maximum size allowed for multipart/form-data requests. The default is 10Mb
multipart.fileSizeThreshold specifies the size threshold after which files will be written to disk. Default is 0, which means that the file will be written to disk immediately.

とのこと。multipart.maxFileSize が 1Mb なのは、最近のでっかい画像を考えるとちょっと足りない。

なので、通常は、ファイルをアップロードさせるアプリケーションを開発するときには、明示する必要がある。

multipart.fileSizeThreshold は、なんか大きめの値を設定したくなりがちだが、通常はデフォルトの0で問題ないと思う。小さいファイルをたくさんアップさせるようなサービスの場合は別だが、そんなサービスそうそうない。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Freemarker でテンプレート書くときに、Freemarker tag っぽいのを出力したい

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

gradle が混乱したときにとにかく念入りにキャッシュ消したい時にうつコマンド

gradle を使っている時に、変なライブラリを依存に加えた結果、依存関係が混乱し、どうにもならなくなるときがある。 そんな時は設定を戻してから以下のように打てば、とにかくキャッシュは消えて、とりあえずなんとかなる気がする。

pkill -f gradle; rm -rf ~/.m2/repository/ ~/.gradle/caches/ .gradle; ./gradlew --recompile-scripts --refresh-dependencies --rerun-tasks clean check
Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Java で method_missing したい。

Java で method_missing したいというのは、テストを書いているときによくあるケースである。 Mockito など利用すればいいのだが、その中身が気になってしまうのが、エンジニアの性であろう。

そういうわけで、method_missing のやり方を調べていると、Proxy を利用する例が多々みつかる。 しかしこの方法では満足な結果は得られない。なぜならば Proxy では interface に対するモッキングはできるがクラスにたいするモッキングが出来ないからだ。悲しい。

参考: http://www.javaworld.com/article/2076233/java-se/explore-the-dynamic-proxy-api.html

そういうわけで、mocking には cglib or javassist などのバイトコード生成系ライブラリを利用するのが良い。 http://stackoverflow.com/questions/3291637/alternatives-to-java-lang-reflect-proxy-for-creating-proxies-of-abstract-classes


なお mockito の

when(body).thenReturn()

のような記法で、body がキャプチャできるわけないしキモいなあと思っていたのだが、これは単に DSL としてかっこ良く見せてるだけで、実際には引数としてなにかに利用しているわけではないようだ。つまり以下のように書いても同じなのだが、読みやすくするためにああなっている、ということのようだ。

body; when().thenReturn()
Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring boot で、テスト時だけ読ませたい @Configuration があるとき

なんかよくわからんけど @ContextConfiguration 指定すると @SpringApplicationConfiguration が処理されなくなるのかな。よくわからんけど。

なので、@SpringApplicationConfiguration(classes = { MyApplication.class, MyConfig1.class, MyConfig2.class }) みたいにしておいて、MyConfig1.class を test.MyConfig1 など、component scan の対象外に追い出せば良い。

以上。

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring の MockMVC で通信結果をダンプしたいとき

mockmvc.perform(get("/"))
    .andDo(log());

とかしとけばいい。

    .andDo(print());

のほうが、確実に出力されるのでいいかも(ロガーの設定によらず出力される)

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring boot でテスト書いてたら 403 になって、csrf filter に引っかかってるぽくていらっとしたときの対策

@Before
public void setUp() throws Exception {
    mockMvc = MockMvcBuilders
            .webAppContextSetup(webApplicationContext)
            .apply(springSecurity())
            .build();
}

みたいな設定してるときに csrf filter かかってきてテストコードが 403 で弾かれてうざいわーって時

mockMvc.perform(post("/register")
              .with(csrf())
              .param("action", "signup"))
     .andExpect(status().isOk());

とかすりゃ、とりあえず csrf token 渡すようになるっぽい。

って stackoverflow に書いてあった。

http://stackoverflow.com/questions/21749781/why-i-received-an-error-403-with-mockmvc-and-junit

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

Spring Boot で redirect: とかなってるやつを自分で拡張したい

通常の Spring Boot では redirect:/ とか書くと、Moved Temporaly になるけど Moved Permanently 返したいときがある。

そんなとき、以下のような答えがインターネッツを検索していると出てくる。

  • @ResponseStatus(value=HttpStatus.MOVED_PERMANENTLY) ってコントローラメソッドに付与するって方法がある
    • メソッド全体にかかってくるのがダサい。
  • httpServletReqeust.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, 302) でやる
    • わかりづらい

そういうわけで、redirect301:/hoge/ とかでできるようにしたらいいんじゃないかと思いつく。 この似非 URL みたいなやつは ViewResolver ってやつがハンドリングしている。

ViewResolver は https://github.com/spring-projects/spring-framework/blob/554bf49/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L700 のへんで集められるが、要するに DI で ViewResolver をねじ込めば良い。

Spring Boot の場合、以下のようにすればよい。ViewResolver は複数指定できて、順番に呼び出される感じになっている。apache module のような呼びだされ方を想定しておけば良い。

@Slf4j
@Configuration
public class WebConfig implements ServletContextAware {
    @Bean
    public ViewResolver myViewResolver() {
        return (viewName, locale) -> {
            final String header = "redirect301:";
            if (viewName.startsWith(header)) {
                String redirectUrl = viewName.substring(header.length());
                RedirectView view = new RedirectView(redirectUrl, true, true);
                view.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
                return view;
            } else {
                return null; // DECLINED
            }
        };
    }
}
Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

Harriet 0.05 released.

shipped.

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring mvc の interceptor のパスマッチャのやつ

Spring mvc の interceptor ってやつ。hook みたいなやつ。以下のようなパスの設定ができる。 excludePathPatterns の設定だけだと動かないとかいう説もあって、注意が必要。

registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");

http://docs.spring.io/spring/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-config-interceptors

ここのパターンは PathMatcher でマッチするのだが、AntPathMatcher が事実上唯一の実装。 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html このマッチャで利用可能なメタキャラクタは以下のもののみ。

? matches one character
* matches zero or more characters
** matches zero or more directories in a path

インターセプターで、特定のパスのみ認証スキップとかやってる場合、気をつけないとエラー表示ページとかがそのまま interceptor かかってて謎のリダイレクトかかったりしてだるいです。

以上。

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000
Next page