Client/Server Validierung

In den letzten Monaten und Jahren habe ich mich bei diversen Gelegenheiten mit RESTful APIs und der Entwicklung von Clients für selbige beschäftigt. Die Frameworks dafür sind zahllos und für jeden Geschmack ist schnell etwas passendes gefunden. Mit wenigen Zeilen Code steht ein einfacher Server und auch ein hübscher Client ist schnell gehackt. Die Konzepte dahinter sind genau so vielfältig, wie clever.

Einen Aspekt habe bisher aber noch nirgendwo zufriedenstellend gelöst gesehen: Validierung. Als Benutzer möchte man das Feedback möglichst früh und vollständig. Client-Validierung ist also ein muss. Andererseits traut der geneigte Entwickler niemals einem Client. Validierung auf dem Server ist also nicht nur ebenfalls ein muss – und sollte dann auch noch mit der Client-Validierung zusammen passen. Was passiert also momentan? Die Validierung wird doppelt (oder gar noch öfter, wenn man es mit mehreren Clients zu tun hat) und – damit es richtig Spaß macht – auch noch in verschiedenen Sprachen (wenn man nicht gerade auf dem Server auch mit Javascript arbeitet) implementiert. Bleibt die Frage: Warum?!

Die Lösung, die ich mir vorstelle, sieht ungefähr so aus (Beispielhaft für meinen Technologie Stack, aber sicher auch auf andere Technologien übertragbar):

Validierungsregeln auf Server-Seite über Bean Validation (Das funktioniert bereits wunderbar):

public class User implements Serializable {
 @NotEmpty
 @Email
 private String email;

 @Size(min=5)
 @Pattern(regexp = "[a-zA-Z]*")
 private String username;

 @Size(min=8)
 private String password;
}

Validierung dann durchgeführt durch den Spring MVC Controller. (Das „schema“-Attribut der @Controller Annotation ist Wunschdenken, der Rest funktioniert ebenfalls schon)

@Controller(schema=SchemaProvider.AUTO)
public class UserController {

 @RequestMapping(value = "/user", method = RequestMethod.POST)
 public String saveUser(@Valid User user, BindingResult bindingResult) {
  /* ... */
 }

}

Dank des „SchemaProvider.AUTO“ veröffentlicht der Controller die Meta(/Validierungs)-Daten unter /user?schema als JSON Schema. Eine Grundlage dafür liefert evtl. dieser Pull Request für das Jackson JSON Schema Module. Das Ergebnis sähe dann vielleicht so aus:

{
 "title": "User Schema",
 "type": "object",
 "properties": {
  "email": {
   "type": "string",
   "pattern": "[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}"
  },
  "username": {
   "type": "string",
   "pattern": "[a-zA-Z]*",
   "minimum": 5
  },
  "password": {
   "type": "string",
   "minimum": 8
  }
 },
 "additionalProperties": false,
 "required": ["email", "username", "password"]
}

Das wiederum kann Angular Schema Form benutzen, um mit minimalem Code ein hübsches Formular zu zaubern und live zu validieren (siehe die Beispiele):

[
 "email",
 "username",
 "password",
 {
  "type": "submit",
  "style": "btn-info",
  "title": "OK"
 }
]

IMHO wäre das eine sehr elegante Lösung, die mit minimalem Code-Aufwand auskommt und die Validierungsregeln relativ einfach synchronisiert. Denkbar wäre z.B. auch die Regeln aus dem JSON Schema für die Validierung in Mobile Apps zu verwenden.

Nachteile? Man benötigt bereits bei der Anzeige des Formulars einen zusätzlichen Server-Aufruf, was in manchen Situationen schmerzen kann. Außerdem funktioniert das natürlich nur für ein Set an Standardvalidierungen. Will man z.B. sicherstellen, dass die E-Mail-Adresse auch unique ist, dann wird die Sache schon etwas komplizierter – Aber nicht unlösbar!

Mein neues Bastelprojekt für die Winterzeit ist jedenfalls gefunden – Ich will das haben!