luchob / softuni-spring-may-2024

Our common projects
15 stars 1 forks source link

Rest заявка, която проверка дали даден обект съществува в Rest Server-а> #25

Closed mr-pachev closed 4 months ago

mr-pachev commented 4 months ago

Попълнете отделните секции отдолу и изтрийте упътванията.

Връзка към проекта: Human Resource Managements - Rest Client -https://github.com/mr-pachev/human-resource-managements.git HRM-Users - Rest Server - https://github.com/mr-pachev/hrm-users.git

Кратко описание -> Верификация на данните при регистрация на нов потребител. Не знам как да направя заявка към Rest Server-а и съответно как да проверя отговора в Rest Client-a, дали изпратеният обект е записан или вече съществува такъв.

Операция -> създаване на нов потребител.

Стъпки -> минала проверка на за валидация на полетата от формата, направена проверка дали въведените пароли в полетата password и confirm password са едентични, булев отговор от сървиса дали потребителя е създаден (направена проверка дали вече съществува потребител)

Опишете възможно най-лесните стъпки, с помощта на които бързо може да се репродуцира проблемът.

luchob commented 4 months ago

Привет, мистър Пачев! :-)

Свалих hrm-users, там нямаше много контролери, но разбирам въпроса ти. :-)

Наскоро имахме подобно чудене и ще ти кажа горе долу как подходихме (част от кода е на котлин както и ще скрия част от него заради "legal issues", но мисля че няма да е голям проблем това).

Постановка:

POST заявка, която не може да мине заради някакво условие на сървъра (например в твоя случай това е вече съществуващ юзър).

Как го направихме. На сървъра:

  1. Например един custom Exception със няколко кода за грешки, напр:

    class XXXConflictException(message: String, val errorCode: ErrorCode) : RuntimeException(message) {
    
    enum class ErrorCode {
    CODE1,
    CODE2,
    CODE3
    }
    }

Тук XXX и CODE1-CODE3 са реално с други имена в нашия проект и съответстват на няколко вида потенциални грешки които само сървъра може да установи.

  1. Този custom exception се хвърля на сървъра когато усетим че има проблем (например, в твоя случай - съществува user). Прихваща се с глобален (или локален) exception handler, например:

    @ControllerAdvice
    class GlobalExceptionHandlers {
    
    @ExceptionHandler(XXXConflictException::class)
    @ResponseBody
    @ResponseStatus(HttpStatus.CONFLICT)
    fun handleXXXConflictException(exception: XXXConflictException): ApiErrorDTO {
    return ApiErrorDTO(
      timestamp = LocalDateTime.now(),
      status = HttpStatus.CONFLICT.value(),
      error = HttpStatus.CONFLICT.reasonPhrase,
      errorCode = exception.errorCode.name
    )
    }
    }

ApiErrorDTO e къстъм отговор за грешка, нормално DTO което съдържа код на грешката.

На клиента (при нас е андроид клиент, а не е спринг приложение, затова показвам едно примерче за обработка на статус конфликт):

  1. offerRestClient
        .get()
        .uri("/offers..")
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .onStatus(
            s -> s.isSameCodeAs(HttpStatus.CONFLICT),
            (req, resp) -> {
              // convert response to cusom object
              ErrorDTO errorDTO = objectMapper.readValue(resp.getBody(), ErrorDTO.class);
              throw new YourException(errorDTO.errorCode());
            }
        )
        .body(new ParameterizedTypeReference<>(){});
  2. Хвърляш подходящ YourException, където си запазил конкретния код на грешката идващ от сървъра и го обработваш с глобален/локален exception handler или пък дори try/catch.

BTW в документацията на спринг е много философски споменато как да създадем DTO от response в по-сложните случаи :-) Много са гадни понякога.

По-лесни варианти за решение:

  1. Разчиташ само на http status code conflict без ErrorDTO/ApiErrorDTO
  2. Правиш GET REST endpoint който да ти проверява дали дадено име е заето.

По-трудните варианти ги бяхме опитали в минали години, с PathFinder. Виж тук и съответния exception handler на ред 68. За съжаление, клиента е JS.

Поздрави, Л.

mr-pachev commented 4 months ago

Благодаря за изчерпателният отговор. Надявам се да успея да приложа част от нещата, които ме посъветва.