L’API d’enregistrement devient RESTful

L'API d'enregistrement devient RESTful

1. Vue d'ensemble

Dans les derniers articles dethe Registration series here on example, nous avons construit la plupart des fonctionnalités dont nous avions besoin à la manière MVC.

Nous allons maintenant faire la transition de certaines de ces API vers une approche plus REST.

2. L'opérationRegister

Commençons par l'opération principale de registre:

@RequestMapping(value = "/user/registration", method = RequestMethod.POST)
@ResponseBody
public GenericResponse registerUserAccount(
      @Valid UserDto accountDto, HttpServletRequest request) {
    logger.debug("Registering user account with information: {}", accountDto);
    User registered = createUserAccount(accountDto);
    if (registered == null) {
        throw new UserAlreadyExistException();
    }
    String appUrl = "http://" + request.getServerName() + ":" +
      request.getServerPort() + request.getContextPath();

    eventPublisher.publishEvent(
      new OnRegistrationCompleteEvent(registered, request.getLocale(), appUrl));

    return new GenericResponse("success");
}

Alors, en quoi est-ce différent de la mise en œuvre originale axée sur MVC?

Voici:

  • la demande est maintenant correctement mappée sur un HTTP POST

  • nous renvoyons maintenant un DTO approprié et le rassemblons via l'annotation@ResponseBody directement dans le corps de la réponse

  • nous ne traitons plus du tout de gestion des erreurs dans la méthode

Nous supprimons également les anciensshowRegistrationPage(), car cela n'est pas nécessaire pour simplement afficher la page d'inscription.

3. Lesregistration.html

Avec ces changements, nous devons maintenant modifier lesregistration.html pour:

  • utiliser Ajax pour soumettre le formulaire d'inscription

  • recevoir les résultats de l'opération en JSON

Voici:



form


4. Gestion des exceptions

En plus de l'API plus RESTful, la logique de traitement des exceptions deviendra bien entendu plus mature.

Nous utilisons le même mécanisme@ControllerAdvice pour traiter proprement les exceptions levées par l'application - et nous avons maintenant besoin d'un nouveau type d'exception.

Il s'agit duBindException - qui est lancé lorsque leUserDto est validé (s'il est invalide). Nous remplacerons la méthode par défautResponseEntityExceptionHandlerhandleBindException() pour ajouter les erreurs dans le corps de la réponse:

@Override
protected ResponseEntity handleBindException
  (BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
    logger.error("400 Status Code", ex);
    BindingResult result = ex.getBindingResult();
    GenericResponse bodyOfResponse =
      new GenericResponse(result.getFieldErrors(), result.getGlobalErrors());

    return handleExceptionInternal(
      ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}


Nous devrons également gérer nosExceptionUserAlreadyExistException personnalisés - qui sont lancés lorsque l'utilisateur s'inscrit avec un e-mail qui existe déjà:

@ExceptionHandler({ UserAlreadyExistException.class })
public ResponseEntity handleUserAlreadyExist(RuntimeException ex, WebRequest request) {
    logger.error("409 Status Code", ex);
    GenericResponse bodyOfResponse = new GenericResponse(
      messages.getMessage("message.regError", null, request.getLocale()), "UserAlreadyExist");

    return handleExceptionInternal(
      ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request);
}




5. LesGenericResponse

Nous devons également améliorer l'implémentation deGenericResponse pour contenir ces erreurs de validation:

public class GenericResponse {

    public GenericResponse(List fieldErrors, List globalErrors) {
        super();
        ObjectMapper mapper = new ObjectMapper();
        try {
            this.message = mapper.writeValueAsString(fieldErrors);
            this.error = mapper.writeValueAsString(globalErrors);
        } catch (JsonProcessingException e) {
            this.message = "";
            this.error = "";
        }
    }
}

6. UI - Erreurs de champ et globales

Enfin, voyons comment gérer les erreurs de champ et globales à l'aide de jQuery:

var serverContext = [[function register(){
    $(".alert").html("").hide();
    var formData= $('form').serialize();
    $.post(serverContext + "/user/registration",formData ,function(data){
        if(data.message == "success"){
            window.location.href = serverContext +"/successRegister.html";
        }
    })
    .fail(function(data) {
        if(data.responseJSON.error.indexOf("MailError") > -1)
        {
            window.location.href = serverContext + "/emailError.html";
        }
        else if(data.responseJSON.error.indexOf("InternalError") > -1){
            window.location.href = serverContext +
              "/login.html?message=" + data.responseJSON.message;
        }
        else if(data.responseJSON.error == "UserAlreadyExist"){
            $("#emailError").show().html(data.responseJSON.message);
        }
        else{
            var errors = $.parseJSON(data.responseJSON.message);
            $.each( errors, function( index,item ){
                $("#"+item.field+"Error").show().html(item.defaultMessage);
            });
            errors = $.parseJSON(data.responseJSON.error);
            $.each( errors, function( index,item ){
                $("#globalError").show().append(item.defaultMessage+"
"); }); } }

Notez que:

  • S'il y a des erreurs de validation - alors l'objetmessage contient les erreurs de champ et l'objeterror contient les erreurs globales

  • Nous affichons chaque erreur de champ à côté de son champ

  • Nous affichons toutes les erreurs globales en un seul endroit à la fin du formulaire

7. Conclusion

Cet article rapide a pour objectif de placer l'API dans une direction plus reposante et de montrer un moyen simple de traiter cette API en amont.

Le frontal jQuery lui-même n’est pas l’objet principal, mais un client potentiel de base pouvant être implémenté dans un grand nombre de frameworks JS, tandis que l’API reste identique.

Lesfull implementation de ce didacticiel se trouvent dansthe github project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.