mirror of https://github.com/usbharu/Hideout.git
feat: Null非許容のパラメーターにnullがきたときのエラーメッセージを改善
This commit is contained in:
parent
b33f62b656
commit
5c8d7e8d36
|
@ -141,10 +141,11 @@ class AccountApiTest {
|
||||||
mockMvc
|
mockMvc
|
||||||
.post("/api/v1/accounts") {
|
.post("/api/v1/accounts") {
|
||||||
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
contentType = MediaType.APPLICATION_FORM_URLENCODED
|
||||||
param("username", "api-test-user-3")
|
param("password", "api-test-user-3")
|
||||||
with(csrf())
|
with(csrf())
|
||||||
}
|
}
|
||||||
.andExpect { status { isBadRequest() } }
|
.andDo { print() }
|
||||||
|
.andExpect { status { isUnprocessableEntity() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -156,7 +157,7 @@ class AccountApiTest {
|
||||||
param("username", "api-test-user-4")
|
param("username", "api-test-user-4")
|
||||||
with(csrf())
|
with(csrf())
|
||||||
}
|
}
|
||||||
.andExpect { status { isBadRequest() } }
|
.andExpect { status { isUnprocessableEntity() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.validation.BindException
|
import org.springframework.validation.BindException
|
||||||
|
import org.springframework.validation.FieldError
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice
|
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||||
|
|
||||||
|
@ -52,23 +53,43 @@ class MastodonApiControllerAdvice {
|
||||||
@ExceptionHandler(BindException::class)
|
@ExceptionHandler(BindException::class)
|
||||||
fun handleException(ex: BindException): ResponseEntity<UnprocessableEntityResponse> {
|
fun handleException(ex: BindException): ResponseEntity<UnprocessableEntityResponse> {
|
||||||
logger.debug("Failed bind entity.", ex)
|
logger.debug("Failed bind entity.", ex)
|
||||||
val error = ex.bindingResult.fieldErrors
|
|
||||||
val message = error.map {
|
|
||||||
"${it.field} ${it.defaultMessage}"
|
|
||||||
}.joinToString(prefix = "Validation failed: ")
|
|
||||||
|
|
||||||
val details = error.associate {
|
val details = mutableMapOf<String, MutableList<UnprocessableEntityResponseDetails>>()
|
||||||
it.field to UnprocessableEntityResponseDetails(
|
|
||||||
when (it.code) {
|
ex.allErrors.forEach {
|
||||||
|
val defaultMessage = it.defaultMessage
|
||||||
|
when {
|
||||||
|
it is FieldError -> {
|
||||||
|
val code = when (it.code) {
|
||||||
"Email" -> "ERR_INVALID"
|
"Email" -> "ERR_INVALID"
|
||||||
"Pattern" -> "ERR_INVALID"
|
"Pattern" -> "ERR_INVALID"
|
||||||
else -> "ERR_INVALID"
|
else -> "ERR_INVALID"
|
||||||
},
|
}
|
||||||
it.defaultMessage
|
details.getOrPut(it.field) {
|
||||||
)
|
mutableListOf()
|
||||||
|
}.add(UnprocessableEntityResponseDetails(code, defaultMessage.orEmpty()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.unprocessableEntity().body(UnprocessableEntityResponse(message, details))
|
defaultMessage?.startsWith("Parameter specified as non-null is null:") == true -> {
|
||||||
|
val parameter = defaultMessage.substringAfterLast("parameter ")
|
||||||
|
|
||||||
|
details.getOrPut(parameter) {
|
||||||
|
mutableListOf()
|
||||||
|
}.add(UnprocessableEntityResponseDetails("ERR_BLANK", "can't be blank"))
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
logger.warn("Unknown validation error", ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val message = details.map {
|
||||||
|
it.key + " " + it.value.joinToString { it.description }
|
||||||
|
}.joinToString()
|
||||||
|
|
||||||
|
return ResponseEntity.unprocessableEntity()
|
||||||
|
.body(UnprocessableEntityResponse(message, details))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(StatusNotFoundException::class)
|
@ExceptionHandler(StatusNotFoundException::class)
|
||||||
|
|
|
@ -1455,6 +1455,8 @@ components:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
username:
|
username:
|
||||||
|
|
||||||
|
nullable: false
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
maxLength: 300
|
maxLength: 300
|
||||||
|
@ -2841,8 +2843,10 @@ components:
|
||||||
error:
|
error:
|
||||||
type: string
|
type: string
|
||||||
details:
|
details:
|
||||||
type: object
|
type: array
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
$ref: "#/components/schemas/UnprocessableEntityResponseDetails"
|
$ref: "#/components/schemas/UnprocessableEntityResponseDetails"
|
||||||
|
|
||||||
UnprocessableEntityResponseDetails:
|
UnprocessableEntityResponseDetails:
|
||||||
|
@ -2852,6 +2856,10 @@ components:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
nullable: false
|
||||||
|
required:
|
||||||
|
- error
|
||||||
|
- description
|
||||||
|
|
||||||
UnauthorizedResponse:
|
UnauthorizedResponse:
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -36,3 +36,4 @@ Not Integer, not Long => we have a decimal value!
|
||||||
}}{{^isInteger}}{{^isLong}}{{#minimum}}
|
}}{{^isInteger}}{{^isLong}}{{#minimum}}
|
||||||
@get:DecimalMin("{{.}}"){{/minimum}}{{#maximum}}
|
@get:DecimalMin("{{.}}"){{/minimum}}{{#maximum}}
|
||||||
@get:DecimalMax("{{.}}"){{/maximum}}{{/isLong}}{{/isInteger}}
|
@get:DecimalMax("{{.}}"){{/maximum}}{{/isLong}}{{/isInteger}}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{#useBeanValidation}}{{>beanValidation}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}
|
{{#useBeanValidation}}{{>beanValidation}}{{>beanValidationModel}}{{/useBeanValidation}}{{#swagger2AnnotationLibrary}}
|
||||||
@Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}
|
@Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}
|
||||||
@ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}
|
@ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}required = true, {{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{^isNullable}}@get:NotNull{{/isNullable}}
|
||||||
@get:JsonProperty("{{{baseName}}}", required = true){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}
|
@get:JsonProperty("{{{baseName}}}", required = true){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}
|
||||||
|
|
Loading…
Reference in New Issue