Closed sunakan closed 2 years ago
キーワードメモ
@RestControllerAdvice
@ExceptionHandler
カスタム例外ハンドラー
Adviceとは差し込みたい処理
RFC 7807 Problem Details for HTTP APIs
RFC 7807 - Problem Details for HTTP APIs 日本語訳 https://tex2e.github.io/rfc-translater/html/rfc7807.html
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en
{
"type": "https://example.net/validation-error",
"title": "Your request parameters didn't validate.",
"invalid-params": [ {
"name": "age",
"reason": "must be a positive integer"
},
{
"name": "color",
"reason": "must be 'green', 'red' or 'blue'"}
]
}
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc",
"balance": 30,
"accounts": ["/account/12345",
"/account/67890"]
}
素案
@RestController
class UserAndAuthenticationController(
val mySessionJwt: MySessionJwt,
val myAuth: MyAuth,
val registerUserUseCase: RegisterUserUseCase,
val loginUseCase: LoginUseCase,
val updateUserUseCase: UpdateUserUseCase,
) : UsersApi {
override fun createUser(body: NewUserRequest): ResponseEntity<UserResponse> {
val registeredUser = registerUserUseCase.execute(body.user.email, body.user.email, body.user.password).fold(
{ throw CreateUserUseCaseErrorException(it) },
{ it }
)
val token: String = mySessionJwt.encode(MySession(registeredUser.userId, registeredUser.email)).fold(
{ throw RealworldSessionEncodeErrorException(it) },
{ it }
)
return ResponseEntity(
UserResponse(
user = User(
email = registeredUser.email.value,
username = registeredUser.username.value,
bio = registeredUser.bio.value,
image = registeredUser.image.value,
token = token
)
),
HttpStatus.valueOf(201)
)
}
data class CreateUserUseCaseErrorException(val error: RegisterUserUseCase.Error) : Exception()
@ExceptionHandler(value = [CreateUserUseCaseErrorException::class])
fun onCreateUserUseCaseErrorException(e: CreateUserUseCaseErrorException): ResponseEntity<GenericErrorModel> {
val generateResponseEntity: (List<String>) -> ResponseEntity<GenericErrorModel> = { body ->
ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = body)),
HttpStatus.valueOf(422)
)
}
return when (val error = e.error) {
is RegisterUserUseCase.Error.AlreadyRegisteredEmail -> generateResponseEntity(listOf("メールアドレスはすでに登録されています"))
is RegisterUserUseCase.Error.AlreadyRegisteredUsername -> generateResponseEntity(listOf("ユーザー名は既に登録されています"))
is RegisterUserUseCase.Error.InvalidUser -> generateResponseEntity(error.errors.map { it.message })
is RegisterUserUseCase.Error.Unexpected -> throw NotImplementedError()
}
}
}
コントローラ内にExceptionHandler書くのどうなん?優先度的に。。とか考えてたら、コントローラでのハンドリングを勧めている記事発見
各コントローラーのExceptionHandler -> RestControllerAdvices(優先度順) です。 また、各コントローラーのExceptionHandlerでキャッチした後、適当にExceptionをthrowsしても共通的なRestControllerAdvicesに遷移しませんでした。 なので、各コントローラー内でハンドリングするのであれば、キャッチした後にきっちり例外が起こらないようにする必要があります。
おぉ!自分が知りたかったことを書いてある!! APIテストでも後々確認できるけど、そのまま(コントローラ内にハンドラ書く)で行けそう
ハンドリングできたので閉じます
現在
解決/達成したいこと
改めて考えたいこと
参考