Edited at

[自分用]Spring BootでRESTful Web Service立てたやり方まとめ

More than 3 years have passed since last update.


目的

サーバ側でいろいろ処理してクライアントでは結果だけ出したかった。


はじめに

http://spring.io/guides/gs/rest-service/

の公式ガイドに簡単な流れがあるのでまずはこれを実行してみると良い。


SPRING INITIALIZER

https://start.spring.io

でGradleとかMavenのプロジェクトを生成してくれる。

本まとめではGradleを使う

DependenciesでWebとかMongoDBとか必要なのを入れておくと楽


実行

適当にIntelliJかなんかで開いて、既にあるhogehogeApplication.javaのmainを実行すればデバッグ的には楽か?

生成されたプロジェクトのそのままだと実行してもすぐ終了してしまうので、Greeting.javaGreetingController.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を継承したクラスを作る。

例えばこういう感じで作成する。


SpringMongoConfig.java

@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で指定する


HogeNode.java

@Document(collection = "HogeNode")

public class HogeNode {
@Field("_id")
private String objectId;
//省略


Repository

findとかするクラスを作る。


HogeRepository.java

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したい場合には、


ProductRepositoryCustom.java

interface ProductRepositoryCustom {

List<HogeNode> aggregate(int sort);
}

このようなinterfaceを作って、


ProductRepositoryImpl.java

@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.javaSpringMongoConfig.java

@Override

protected String getDatabaseName() {
return mongoDB;
}

に依存してしまう?


エラーぺージ


Config.java

@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は必須。


認証

簡単化のため、インメモリでユーザ認証するので以下を実装


WebSecurityConfig.java

@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になる。