Powieolone metody kontrolera w Springu.

0

Załóżmy, że mam dwa typy kont, które mają dostęp do tej samej operacji w systemie. Mam dwa kontrolery kontroler dla admina i dla pracownika. Więc muszę dodać w obu kontrolerach praktycznie takie same metody, różniące się tylko tym, że w requestMapping dostają admin lub employee, tak samo w zwracanych widokach zwracają z admin lub employee. Jaki jest najlepszy sposób na ominięcie tego żeby nie powielać praktycznie tego samego kodu?

	@RequestMapping(value = "/admin/createStudentAccount", method = RequestMethod.POST)
	public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result) {

		AddClientAccountValidator addClientAccountValidator = new AddClientAccountValidator();
		addClientAccountValidator.validate(userDto, result);

		if (result.hasErrors()) {
			return "admin/createstudentaccount";
		} else {
			if ((userAccountService.createPersonalAccout(userDto, "Konto student").getError()) != null) {
				return "admin/createstudentaccount";
			} else {
				return "admin/createaccount";
			}
		}
	} 
	@RequestMapping(value = "/employee/createStudentAccount", method = RequestMethod.POST)
	public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result) {

		AddClientAccountValidator addClientAccountValidator = new AddClientAccountValidator();
		addClientAccountValidator.validate(userDto, result);

		if (result.hasErrors()) {
			return "employee/createstudentaccount";
		} else {
			if ((userAccountService.createPersonalAccout(userDto, "Konto student").getError()) != null) {
				return "employee/createstudentaccount";
			} else {
				return "employee/createaccount";
			}
		}
	} 
1

@RequestMapping jako value nie przyjmuje tablicy ?
A jak nie choć wątpie...to zawsze UWAGA można robić metody w klasie wyciągając w ten sposób wspólny kod

1

Hmm...

    @RequestMapping(value = "/{source}/createStudentAccount", method = RequestMethod.POST)
    public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result, @PathVariable("source") String source) {
 
        AddClientAccountValidator addClientAccountValidator = new AddClientAccountValidator();
        addClientAccountValidator.validate(userDto, result);
 
        if (result.hasErrors()) {
            return source + "/createstudentaccount";
        } else {
            if ((userAccountService.createPersonalAccout(userDto, "Konto student").getError()) != null) {
                return source + "/createstudentaccount";
            } else {
                return source + "/createaccount";
            }
        }
    } 

? Nie mam zbyt dużego doświadczenia w Springu.

Ewentualnie: http://stackoverflow.com/questions/3796545/how-do-i-get-the-requestmapping-value-in-the-controller i zrobić na tym split.

1

@Wizzie problem może być z security.

W ogóle, to Twój kontroler robi za dużo, jak widzisz że kod się powtarza, a musisz mieć dwie różne metody kontrolera, bo cośtam, to powinno na początku wyglądać jakoś tak

    @RequestMapping(value = "/admin/createStudentAccount", method = RequestMethod.POST)
    public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result) {
        //dlaczego olewasz DI ?
        result = validationFacade.validate(userDto,result);
 
        //tutaj mozesz miec jakis util
        return viewBasedOnResult(userDto,result);
    } 


    @RequestMapping(value = "/employee/createStudentAccount", method = RequestMethod.POST)
    public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result) {
 
        result = validationFacade.validate(userDto,result); 
        return viewBasedOnResult(userDto,result);
    } 

Ale jeżeli masz taką możliwość i nie aplikujesz różnych ROLE dla usera i admina to zrób tak jak napisał @Wizzie

P.S nie podoba mi się że tworzenie PersonalAccount wywołujesz w jakimś łańcuszku ifów,
to powinna być główna akcja, a sama validacja tylko przy okazji.

0

Korzystam ze Spring Security akurat, czyli w sposób Wizziego nie zadziała i muszę to dublować? Ify są dlatego, że jest sprawdzane czy dane klienta są ok jeśli nie to zwracamy widok z błędami, jeśli jest ok to przechodzimy do głównej akcji. Robi to coś mniej więcej takiego:

      //sprawdzamy czy walidacja zwróciła jakieś błędy, np imie = 1044324
      if (result.hasErrors()) {
            jeśli mamy błędy to zwracamy widok z informacjami o błędach
            return "employee/createstudentaccount";
        } else {
            //jeśli walidacja ok wywołujemy metodę tworzącą konto, która może zwrócić info o błędzie np. login już jest wykorzystywany w //bazie(tutaj jest coś innego niż wcześniejsza walidacja, gdzie sprawdzaliśmy tylko poprawność wprowadzonych danych, sprawdzamy tutaj //dane z bazą danych)
            if ((userAccountService.createPersonalAccout(userDto, "Konto student").getError()) != null) {
                //jeśli błąd zwracamy widok z informacją o błędzie
                return "employee/createstudentaccount";
            } else {
                //jeśli ok zwracamy widok z informacją o pomyślnym założeniu konta.
                return "employee/createaccount";
            }
        }

Co do "//dlaczego olewasz DI ?" DI mam na początku klasy kontrolera:

@Controller
public class AdminController {

	@Autowired
	private UserService userService; 
...

Zastanawiam się, też czy metoda zwracania informacji o powodzeniu lub nie operacji jest ok:

	// metoda tworząca nowe konto osobiste
	@Override
	public UserAccountInfoObject createPersonalAccout(UserDto userDto, String name) {

		UserAccountInfoObject infoObject = new UserAccountInfoObject();
		// utworzenie konta klienta i zwrócenie ewentualnych błędów
		UserInfoObject userInfo = userService.createUser(userDto, "ROLE_CLIENT");

		if (userInfo.getError() != null) {
			infoObject.setError(userInfo.getError());
		} else {
			User user = userService.findByUsername(userDto.getUsername());
			// utworzenie konkretnego typu konta osobistego
			createUserAccount(createConcretePersonalAccount(user, name, userDto.getContribution()));
		}
		return infoObject;
	}

zwracam obiekt typu infoObject:

public class UserInfoObject {

	UserDto userDto;
	String error;
	String success;

	public UserDto getUserDto() {
		return userDto;
	}

	public void setUserDto(UserDto userDto) {
		this.userDto = userDto;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public String getSuccess() {
		return success;
	}

	public void setSuccess(String success) {
		this.success = success;
	}

}
0

Co do "//dlaczego olewasz DI ?" DI mam na początku klasy kontrolera:

Ale masz

AddClientAccountValidator addClientAccountValidator = new AddClientAccountValidator();

W takim razie jeżeli kod Ci się powtarza, zrób tak jak napisałem, wszystko wyciągnij wyżej i zrób z tego klasę, problem solved.

0

@niezdecydowany tak przemyślałem, tą propozycję @Wizzie to w jaki sposób może mi się to popsuć przez security? Tworzę metodę:

 @RequestMapping(value = "/{source}/createStudentAccount", method = RequestMethod.POST)
    public String createStudentAccount(@ModelAttribute("user") UserDto userDto, BindingResult result, @PathVariable("source") String source) 

i na stronach wywołuję po prostu:

<li class="dropdown"><a href="<@spring.url '/employee/about' />">O nas</a></li>

i zależnie od konta użytkownika jest tam admin, employee albo jeszcze client w moim przypadku to zawsze jedna metoda zamiast trzech. Widzę tylko, że będę musiał stworzyć jedną dodatkową metodę dla konta gościa przy ogólnych stronach, ponieważ ona nie ma przedrostka z typem konta tylko idzie po prostu nazwa strony:

@RequestMapping(value = "/about", method = RequestMethod.GET)

a nie można wrzucić pustego @PathVariable

 i wywala błąd.

@niezdecydowany ta metoda 
```java
createStudentAccount

z wcześniejszych postów rzeczywiście tak źle wygląda? Za duża ta ifologia? Nie wiem jak mógłbym to lepiej rozwiązać, gdyż muszę to wszystko posprawdzać.

2

Ja problem z securityu miałem na myśli na poziomie ról jakie chcesz nadawać userowi, w sensie:
dla danego

 <intercept-url pattern="/admin/createStudentAccount" access="hasRole('ROLE_ADMIN')" />
 <intercept-url pattern="/employee/createStudentAccount" access="hasRole('ROLE_EMPLOYEE')" />

musisz mieć dwa osobne urle żeby je mapować, chociaż możesz sobie poszperać w docach bo widziałem parę fajnych trików z wykorzystaniem SpEL'a ( Spring Expression Language ) także to może rozwiązać Twój problem. Chociaż nie widzę problemu z trzymaniem dwóch osobnych urlów - bo to już jest zbytnie DRY - i może zacząć śmierdzieć w pewnym momencie.

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