目的
サーバ側でいろいろ処理してクライアントでは結果だけ出したかった。
はじめに
http://spring.io/guides/gs/rest-service/
の公式ガイドに簡単な流れがあるのでまずはこれを実行してみると良い。
SPRING INITIALIZER
https://start.spring.io
でGradleとかMavenのプロジェクトを生成してくれる。
本まとめではGradleを使う
DependenciesでWebとかMongoDBとか必要なのを入れておくと楽
実行
適当にIntelliJかなんかで開いて、既にあるhogehogeApplication.java
のmainを実行すればデバッグ的には楽か?
生成されたプロジェクトのそのままだと実行してもすぐ終了してしまうので、Greeting.javaとGreetingController.javaをぶち込めば http://localhost:8080/greeting でHello Worldできるはず。
https
証明書作る
プロジェクトのルートで
keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
適当に情報を入れてオレオレ証明書を作る。
httpsを有効にする
src/main/resources/application.properties
に
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: 証明書のパスワード
を追記するだけ。
そしたら https://localhost:8443
MongoDB
設定
HostとかPortとかを設定しないとけないので、AbstractMongoConfigurationを継承したクラスを作る。
例えばこういう感じで作成する。
@Configuration
@EnableMongoRepositories("jp.ac.ritsumei.cs.ubi")
public class SpringMongoConfig extends AbstractMongoConfiguration {
@Value("${spring.data.mongodb.host}")
private String mongoHost;
@Value("${spring.data.mongodb.port}")
private String mongoPort;
@Value("${spring.data.mongodb.database}")
private String mongoDB;
@Override
public MongoMappingContext mongoMappingContext()
throws ClassNotFoundException {
// TODO Auto-generated method stub
return super.mongoMappingContext();
}
@Override
@Bean
public Mongo mongo() throws Exception {
System.out.println("mongo host: " + mongoHost);
System.out.println("mongo db: " + mongoDB);
MongoCredential credential = MongoCredential.createMongoCRCredential("ユーザ名", mongoDB, "パス".toCharArray());
ServerAddress serverAddress = new ServerAddress(mongoHost, Integer.parseInt(mongoPort));
return new MongoClient(serverAddress, new ArrayList<MongoCredential>() {{
add(credential);
}});
}
@Override
protected String getDatabaseName() {
// TODO Auto-generated method stub
return mongoDB;
}
}
hostやportはapplication.properties
に追記する
spring.data.mongodb.host: localhost
spring.data.mongodb.port: 27017
spring.data.mongodb.database: hogehoge
モデル
MongoDBから取ってきた結果をぶち込むクラスを適当に作る。
@Document
をつけることで、どのコレクションを対象とするか指定する
変数名とフィールド名が違うなら@Field
で指定する
@Document(collection = "HogeNode")
public class HogeNode {
@Field("_id")
private String objectId;
//省略
Repository
findとかするクラスを作る。
public interface HogeRepository extends MongoRepository<Hoge, String> {
public HogeNode findByフィールド名(String id);
}
findByとかすると、By以降のフィールド名で引数の値をfindしてくれる。
Likeを加えたり、AndやOrを足したり、countByにするとそのようにMongoDBを叩いてくれる。
呼ぶ
@Autowired
private HogeRepository repository;
@Autowired
を付けると勝手にバインド?してくれるので、適当な場所でインスタンス作る。
HogeNode hogeNode = repository.findById(id);
さっき作ったfindByで呼ぶと結果が得られる。
結果がなかったらnull
Aggregate
Aggregateしたい場合には、
interface ProductRepositoryCustom {
List<HogeNode> aggregate(int sort);
}
このようなinterfaceを作って、
@Repository
class ProductRepositoryImpl implements ProductRepositoryCustom {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List<HogeNode> aggregate(int sort) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.sort(sort == 1 ? Sort.Direction.DESC : Sort.Direction.ASC, "nodeId"),
);
AggregationResults<HogeNode> results = mongoTemplate.aggregate(aggregation, "CollectionName", HogeNode.class);
return results.getMappedResults();
}
implementsしたクラスでMongoTemplete
を@Autowired
してAggregation
を作る。
呼ぶ
@Autowired
private ProductRepositoryCustom repositoryCustom;
でインスタンスが得られるので、コントローラあたりから呼ぶ
group
Aggregation.group("param1")
.first("param1").as("param1")
.avg("param2").as("param2")
groupした後の結果はHogeNode.class
のメンバを全部網羅しておかないとダメっぽい?
複数のDB
先述したSpringMongoConfig.java
に
@Bean(autowire = Autowire.BY_NAME, name = "hogeTemplate")
public MongoTemplate ritsubiMongoTemplate() throws Exception {
return new MongoTemplate(mongo(), "hogeDB");
}
任意のMongoTemplate
の名前を指定して、任意のDB名を引数としてMongoTemplate
を返すメソッドを実装する。
MongoTemplate
を@Autowire
してるところで指定したインスタンス名にすると、そのDBに繋がったMongoTemplate
となる。
ただし、こうすると先述したHogeRepository.java
がSpringMongoConfig.java
の
@Override
protected String getDatabaseName() {
return mongoDB;
}
に依存してしまう?
エラーぺージ
@Configuration
public class Config {
@Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new HashMap<>();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("status", 400);
errorAttributes.put("error", "Bad Request");
errorAttributes.put("message", "Bad Request");
return errorAttributes;
}
};
}
}
@Configuration
をつけたクラスに@Bean
を付けてErrorAttributes
を実装。
putしてるvalueは必須。
認証
簡単化のため、インメモリでユーザ認証するので以下を実装
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("name").password("pass").authorities("ADMIN");
}
}
こんな https://user:pass@localhost:8443/hogehoge 感じに書くだけで認証されてアクセスできる。
ただ、ブラウザからアクセスすると/login
にリダイレクトされる。
(ブラウザが勝手にやってるだけ?)
レスポンスのJSON
レスポンスのJSONはインデントが綺麗になってないので読み辛いが、
@Configuration
public class CustomWebMvcConfiguration extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.setPrettyPrint(true);
}
}
}
}
を実装すると、prettyになる。