Skip to content

Validation

Q-Framework validation is declaration-based. Instead of writing validation logic manually, you declare rules with annotations and the framework handles everything automatically.

Required Fields

Required rules are declared inside @QfCreateAttribute or @QfUpdateAttribute using requiredOn:

java
// Always required
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfUpdateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfDisplayLabel(text = "Name")
private String name;

// Required conditionally
@QfCreateAttribute(
    requiredOn = @QfRequiredOn(
        conditions = @QfConditionExpr(expr = "memberType == 'BUSINESS'")
    )
)
@QfDisplayLabel(text = "Business Registration Number")
private String businessNumber;

@QfRequiredOn is an element annotation (not standalone) used inside @QfCreateAttribute / @QfUpdateAttribute:

FormDescription
@QfRequiredOn(always = true)Always required
@QfRequiredOn(conditions = ...)Required when condition matches
@QfRequiredOn (default)Not required

@QfValidationRule

@QfValidationRule is the single validation annotation. It is repeatable — multiple rules can be stacked on one field.

java
@QfEntity(name = @QfI18n(defaultMessage = "Member", texts = {}))
public class MemberEntity {

    // Login ID — custom regex pattern
    @QfListAttribute(sortable = true)
    @QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
    @QfValidationRule(
        rule = QfValidationRule.Rule.login_id   // uses configured login-ID regex
    )
    @QfDisplayLabel(text = "Login ID")
    private String loginId;

    // Email — control type declares format; crypto for encryption
    @QfDetailAttribute
    @QfCreateAttribute
    @QfCrypto
    @QfControlType(QfControlType.Type.email)
    @QfDisplayLabel(text = "Email")
    private String email;

    // Phone — custom regex
    @QfDetailAttribute
    @QfCreateAttribute
    @QfUpdateAttribute
    @QfCrypto
    @QfValidationRule(
        rule = QfValidationRule.Rule.regex,
        params = "^(?:\\+82[-\\s]?)?(?:0?1[0-9])[-\\s]?\\d{3,4}[-\\s]?\\d{4}$",
        invalidValueMessageKey = "validation.message.phone.invalid"
    )
    @QfDisplayLabel(text = "Phone")
    private String phone;

    // Password — uses configured password policy
    @QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
    @QfCrypto
    @QfValidationRule(rule = QfValidationRule.Rule.user_pwd)
    @QfDisplayLabel(text = "Password")
    private String password;
}

Validation Rule Types

RuleDescriptionparams
Rule.regexCustom regular expressionparams[0] = pattern string
Rule.uniqueServer-side uniqueness checkparams[0] = check URL (optional)
Rule.login_idLogin ID format (from config)none
Rule.user_pwdPassword policy (from config)none

Validation Error Message

Provide either a message key or inline i18n messages:

java
// By message key (resolved from message resource)
@QfValidationRule(
    rule = QfValidationRule.Rule.regex,
    params = "^[A-Za-z0-9_]+$",
    invalidValueMessageKey = "validation.message.loginId.invalid"
)

// By inline message
@QfValidationRule(
    rule = QfValidationRule.Rule.regex,
    params = "^[A-Za-z0-9_]+$",
    invalidValueMessages = @QfI18n(
        defaultMessage = "Only alphanumeric and underscore allowed",
        texts = { @QfI18nText(locale = "ko", message = "영문, 숫자, 밑줄(_)만 허용됩니다") }
    )
)

Control Type as Validation Signal

@QfControlType declares how the field is rendered, and the framework uses the type to infer client-side validation behavior:

java
@QfControlType(QfControlType.Type.email)   // email format validation on client
@QfDisplayLabel(text = "Email")
private String email;

Common control types relevant to validation:

TypeEffect
emailEmail format check
textPlain text (no format constraint)
numberNumeric input
dateDate picker
textareaMulti-line text

Compile-Time Fail-Fast

Q-Framework detects invalid annotation combinations at compile time.

Encrypted + Search — Compile Error

java
@QfCrypto
@QfSearch   // compile error — encrypted fields cannot be searched server-side
private String email;
ERROR: @QfCrypto and @QfSearch cannot be used together.
       Encrypted fields do not support server-side search.
       → MemberEntity.email

AI-Generated Code

Q-Framework's compile-time validation applies equally to AI-generated code. Even if AI produces an invalid annotation combination, the build will be rejected.


Runtime Validation Error Response

When validation fails, Q-Framework returns a standardized error response.

json
{
  "success": false,
  "code": "Q_RUNTIME_000002",
  "message": "Input validation failed.",
  "errors": [
    {
      "field": "loginId",
      "message": "Only alphanumeric and underscore allowed",
      "rejectedValue": "invalid user!"
    }
  ]
}

Custom Validation with @QfOn Hooks

Complex business rules that cannot be expressed with annotations are handled via @QfOn hooks.

java
@QfOn(
    phase = QfOnPhase.BEFORE,
    layer = QfOnLayer.DOMAIN,
    op = QfOnOp.CREATE
)
public void validateUniqueLoginId(/* hook parameters */) {
    // Duplicate login ID check
    if (memberRepository.existsByLoginId(loginId)) {
        throw new QfManagedException(MemberResultCode.DUPLICATE_LOGIN_ID);
    }
}

INFO

Complex validations that cannot be expressed with annotations are handled in @QfOn hooks. The hook contains logic, but the annotation declarations remain the SSOT for structural rules.


Next Steps

Released under the Apache 2.0 License.