Spring boot zatrzymanie przed wykonaniem drugiego walidatora jeśli pierwszy się nie powiedzie

0

Ten punkt końcowy sprawdza, czy data wejściowa przekazana jako parametr adresu URL jest poprawna, czy nie. W tym celu stworzyłem dwa niestandardowe walidatory. Pierwszy sprawdza, czy data jest poprawna składniowo, a drugi, czy mieści się w zakresie prognozy pogody. Kiedy po niepowodzeniu jednego, program przechodzi do drugiego walidatora oczywiście pojawia się błąd: „HV000028: Nieoczekiwany wyjątek podczas wywołania isValid”. ponieważ data nie jest poprawna składniowo i dochodzi do drugiego walidatora, który już oczekuje poprawnego formatu daty i tego właśnie chcę uniknąć. Jak więc mogę przerwać dalszą walidację i po pierwszej walidacji pokazać stronę błędu w tym momencie, jeśli walidacja najpierw się nie powiedzie? To właśnie pierwszy walidator ma za zadanie wyłapać błędną datę Mam nadzieję, że mój problem jest zrozumiały.

Oto mój kod.

MainController.java

@RestController
@Validated
@RequestMapping("/")
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@ComponentScan("pl.jawegiel")
public class MainController {
    
    @GetMapping(path = "/bestLocation/{paramDay}")
    String getBestLocation(@PathVariable(value = "paramDay") @CorrectDate @DateWithinForecast String paramDay) {
        return "valid";
    }
}

CorrectDateValidator.java

public class CorrectDateValidator implements ConstraintValidator<CorrectDate, String> {
    
    private static final int PROPER_NUMBER_OF_DASHES = 2;
    public static final String DATE_REGEX = "^\\d{4}[\\-/\\s]?((((0[13578])|(1[02]))[\\-/\\s]?(([0-2][0-9])|(3[01])))|(((0[469])|(11))[\\-/\\s]?(([0-2][0-9])|(30)))|(02[\\-/\\s]?[0-2][0-9]))$";


    public void initialize(CorrectDate constraint) {
    }

    public boolean isValid(String date, ConstraintValidatorContext context) {
        return (date.matches(DATE_REGEX)) && (date.length() - date.replace("-", "").length() == PROPER_NUMBER_OF_DASHES);
    }
}

DateWithinForecastValidator.java

    public class DateWithinForecastValidator implements ConstraintValidator<DateWithinForecast, String> {

    private static final int ALLOWED_NUMBER_OF_DAYS = 15;
    private static final int NO_DIFFERENCE_BETWEEN_TODAY_AND_DATE_FROM_PARAM = 0;

    public void initialize(DateWithinForecast constraint) {
    }

    public boolean isValid(String date, ConstraintValidatorContext context) {
        LocalDate dateFromParamDay = LocalDate.parse(date);
        LocalDate today = LocalDate.now();
        LocalDate after16Days = today.plus(ALLOWED_NUMBER_OF_DAYS, ChronoUnit.DAYS);
        return today.compareTo(dateFromParamDay) * dateFromParamDay.compareTo(after16Days) >= NO_DIFFERENCE_BETWEEN_TODAY_AND_DATE_FROM_PARAM;
    }
}
5

A nie możesz jak człowiek przekazać tego Stringa do obiektu walidatora? Czy serio wszystko musi być oznaczone pierdyliardem adnotacji?

0

Z wykorzystaniem adnotacji wszystko wygląda elegancko i chciałem to tak załatwić. A w jaki sposób zastosować Twoje rozwiązanie? W ostateczności jak nic nie wyjdzie to dodam po prostu ifa w ciele metody ale jak piszę, wolałbym w ładniejszy sposob.

2

@Kubaz: pomogę ci o ile wyjasnisz mi po co ci:

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)

oraz wisienka na torcie:

@ComponentScan("pl.jawegiel")

Wyjaśnij proszę co robią te dwie adnotacje i czemu masz je akurat tutaj.

I nie, zasranie kodu adnotacjami wcale nie wygląda elegancko. Wyobraź sobię ze wchodzisz do takiego kodu i chcesz prześledzić co się dzieje jak przychodzi request. Zastanów się jak i gdzie będziesz szukać takich magicznych kawałków kodu które są niewidzialnie rozsiane po kodzie.

1

Z wykorzystaniem adnotacji wszystko wygląda elegancko i chciałem to tak załatwić

Masz metodę

@Transactional
@Retryable
@Cacheable
public Z z someMethod(X x, Y, y) {
//ciało metody
}

Najpierw się będzie cachować transakcje, czy transakcja będzie na cachu? Będziesz robił retry na transakcji? Czy transakcje na retry?

2

Adnotacje dobrane na chybił trafił. Poczytaj docsy i się okaże co i jak ;)

public static final String DATE_REGEX = "^\\d{4}[\\-/\\s]?((((0[13578])|(1[02]))[\\-/\\s]?(([0-2][0-9])|(3[01])))|(((0[469])|(11))[\\-/\\s]?(([0-2][0-9])|(30)))|(02[\\-/\\s]?[0-2][0-9]))$";

everyday we stray further from god

3

Wyrażenie regularne do daty jest przekombinowane a nawet zbędne. Jeśli to parametr przekazywany z URL, nie ma co się bawić i wystarczy, że będzie to osiem znaków w stylu 20200101. Taki ciąg znaków można parsować na parę sposobów, np. z użyciem klas w pakiecie time https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html. Przykład z kosmosu, ale pokazuje, że coś można zrobić w paru linijkach:

LocalDate localDate = LocalDate.parse("20200101", DateTimeFormatter.ofPattern("uuuuMMdd"));
System.out.println("Data " + localDate.minusDays(10));
if (localDate.getYear() > 2021) {
	System.out.println("Reksio je salceson");
}

Jeśli data nie będzie pasować do formatu, dostaniemy wyjątek. W przeciwnym razie możemy od razu "liczyć" daty: dodawać dni, odejmować miesiące itp. Tak, że możemy też sprawdzić czy coś mieści się w danym zakresie.
Danym pochodzącym z sieci dobrze pozwolić na jedną, ale konkretną formę i odrzucać wszystko inne, żeby ograniczyć miejsca, w których coś może się popsuć. Takie hocki klocki z wyrażeniami regularnymi można zrobić w konkretnym momencie, jeśli wymaga tego sytuacja. Regexy są dobre... tam, gdzie są dobre.

0

Może umieść 2 walidacje w jednej metodzie?

    public boolean isValid(String date, ConstraintValidatorContext context) {
        return isCorrectDate(date, context) && isDateWithinForecast(date, context)
    }

    private boolean isCorrectDate(String date, ConstraintValidatorContext context){
        //...
    }

    private boolean isDateWithinForecast(String date, ConstraintValidatorContext context){
      //...
    }
}

jeśli lewa strona returna sie nie wykona to nie dojdzie do drugiej

1 użytkowników online, w tym zalogowanych: 0, gości: 1