Spring Sillage
RESTful APIフレームワーク
SillageはSpring MVCの上で動くRESTful API専用のフレームワークです。
Spring MVCを使ってRESTful APIを作ろうとすると、HTTPステータスコードの出し分けは、Exceptionを経由するのが通常で、予期できる例外である重複チェックなども、Exceptionをthrowしなければならず、コードの見通しが悪くなりがちです。また、入力チェックもフレームワークの都合で、@Validで出来る範囲とそうでない場合とで処理が分散することになり、これまた可読性に影響します。
SillageはErlangのwebmachineやClojureのliberatorのように、デシジョンツリーを形成し、HTTPステータスを出し分けることができます。HTTPステータス400 Bad Requestを返すための処理は、MALFORMEDのメソッドに一元化できますし、重複チェックの処理はEXISTSメソッドに一元化できます。
Decisionはオーバーライド可能で
code:java
@Component
@AllowedMethods({HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE})
public class UserResource {
private final Validator validator;
private final UserRepository userRepository;
public UserResource(UserRepository userRepository,
Validator validator) {
this.userRepository = userRepository;
this.validator = validator;
}
@Decision(value = MALFORMED, method = HttpMethod.PUT)
public Problem validateUpdateRequest(RestContext context,
@RequestBody UserUpdateRequest updateRequest,
Errors errors) {
ValidationUtils.invokeValidator(validator, updateRequest, errors);
if (errors.hasErrors()) {
return new ConstraintViolationProblem(Status.BAD_REQUEST,
errors.getFieldErrors().stream()
.map(e -> new Violation(e.getField(), e.getDefaultMessage()))
.collect(Collectors.toList()));
} else {
context.putValue(updateRequest);
return null;
}
}
@Decision(EXISTS)
public boolean exists(@PathVariable Long id, RestContext context) {
userRepository.findById(id).ifPresent(context::putValue);
return context.getValue(User.class).isPresent();
}
@Decision(HANDLE_OK)
public User user(@DecisionContext User user) {
return user;
}
@Decision(DELETE)
public void delete(@DecisionContext User user) {
userRepository.delete(user);
}
@Decision(PUT)
public void update(@DecisionContext User user,
@DecisionContext UserUpdateRequest updateRequest) {
BeanUtils.copyProperties(updateRequest, user);
userRepository.save(user);
}
@Decision(NEW)
public boolean isNew(@DecisionContext User user) {
return user == null;
}
}
Decision
以下のデシジョンポイントがあります。これをオーバーライドすることで、HTTPステータスに沿った挙動をカスタマイズできます。
table:Decisions
key 説明 デフォルト
ALLOWD リクエストが許可されるか? (認可) true
AUTHORIZED リクエストが認証されているか? true
CHARSET_AVAILABLE リクエストのcharsetが利用可能? true
CAN_POST_TO_GONE 以前存在したリソースにPOSTできるか? false
CAN_POST_TO_MISSING 存在しないリソースへPOSTできるか? true
CAN_PUT_TO_MISSING 存在しないリソースへPUTできるか? true
CONFLICT PUTやPOSTリクエストが競合しているか? false
DELETE_ENACTED DELETEリクエストが処理されたか? true
ENCODING_AVAILABLE リクエストされたencodingが利用可能か?
ETAG_MATCHES_FOR_IF_MATCH
ETAG_MATCHES_FOR_IF_NONE
EXISTED リソースが以前存在していたか? false
EXISTS リソースが存在しているか? true
KNOWN_CONTENT_TYPE
LANGUAGE_AVAILABLE リクエストされた言語が利用可能か?
MALFORMED リクエストの形式に誤りがあるか? false
MEDIA_TYPE_ALLOWED
METHOD_ALLOWED
MODIFIED_SINCE
MOVED_PERMANENTLY
MOVED_TEMPORARILY
MULTIPLE_REPRESENTATIONS
POST_ENACTED
PUT_ENACTED
PATCH_ENACTED
NEW
POST_REDIRECT
PUT_TO_DIFFERENT_URL
PROCESSSABLE
RESPOND_WITH_ENTITY
SERVICE_AVAILABLE
UNMODIFIED_SINCE
URI_TOO_LONG false
VALID_CONTENT_HEADER true
VALID_ENTITY_LENGTH true