Kontrolery

Obrázek 3.1. Hierarchie kontrolerů

Hierarchie kontrolerů

Všechny dosud zmíněné komponenty MVC modulu rámce Spring hrají infrastrukturní či pomocnou roli. Místem, kde se aplikace skutečně vytváří, je kontroler. Ve správně navržené aplikaci kontroler v podstatě jen zpracovává parametry požadavku, přistupuje k objektům servisní vrstvy a čerpá z nich model. Pak zvolí správný pohled a model do něj pošle.

Webová vrstva aplikace by měla být co nejužší a neměla by se v ní vyskytovat žádná aplikační logika. Ta je obsažena pouze v aplikační vrstvě, aby webová vrstva mohla být snadno nahrazena například grafickým rozhraním Swing™ či aby funkce aplikace mohly být snadno zpřístupněny pomocí webové služby.

Rámec Spring™ kontrolerům, jako klíčové části návrhového vzoru MVC, samozřejmě věnuje adekvátní pozornost a poskytuje komplexní hierarchii rozhraní a tříd, které programátorům při tvorbě webové aplikace výrazně ulehčují práci. Stěžejní komponenty této hierarchie jsou zachyceny diagramem 3.1 – „Hierarchie kontrolerů“, který byl publikován v [3].

Hlavní roli hraje rozhraní org.springframework.web.servlet.mvc.Controller, se kterým pracuje DispatcherServlet či přesněji SimpleControllerHandlerAdapter (viz „Cesta požadavku útrobami Spring MVC“). Jedinou metodou tohoto rozhraní je

ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res)

Metodě je předán odkaz na objekty požadavku i odpovědi, čímž se stává stejně mocnou jako metody doGet a doPost třídy javax.servlet.http.HttpServlet. Pokud je návratovou hodnotou null, pak tím autor kontroleru signalizuje, že požadavek byl v kontroleru vyřízen a odpověď byla sestavena. Další kroky životního cyklu požadavku (viz „Cesta požadavku útrobami Spring MVC“) jsou vypuštěny a odpověď je odeslána klientovi.

Reprezentace modelu a pohledu

Třídou návratové hodnoty metody handleRequest je org.springframework.web.servlet.ModelAndView. Tato třída představuje dvousložkovou komponentu, obsahující jak model, tak pohled. Zatímco model je vždy uchováván v podobě instance třídy java.util.Map, pohled může mít dvě různé formy. První z nich je konkrétní objekt implementující rozhraní org.springframework.web.servlet.View, jenž se přímo postará o zobrazení modelu. Druhou je symbolický název pohledu, který bude teprve zpracován detektorem pohledu (objektem typu ViewResolver) a převeden na konkrétní objekt typu View.

Ukažme si teď implementaci triviálního kontroleru na příkladu:

public class HomeController implements Controller {
  public ModelAndView handleRequest(HttpServletRequest req, 
      HttpServletResponse res) throws Exception {

    Map model = new HashMap();
    model.put("now",new Date());
    return new ModelAndView("home",model);
  }
}

Vidíme, že metoda vrací ModelAndView, obsahující symbolický název pohledu home a model s jedinou položkou.

Přestože tvorba kontroleru přímo implementujících rozhraní Controller je možná, vhodnější je využití některé z existujících tříd z uvedené hierarchie. Různé třídy řeší různé běžné situace, takže abychom se mohli pro nějakou rozhodnout, musíme alespoň o těch nejpoužívanějších něco málo vědět. Existují tři základní scénáře použití kontrolerů rámce Spring™.

Kontrolery zobrazení

Nejběžnějším scénářem je ten, kdy chceme na uživatelský HTTP GET požadavek reagovat prostým zobrazením určitých dat. V takovém případě je vhodné při tvorbě vlastního kontroleru rozšířit abstraktní třídu org.springframework.web.servlet.mvc.AbstractController a implementovat abstraktní metodu ModelAndView handleRequestInternal(HttpServletRequest, HttpServletResponse). Tato abstraktní třída obsahuje často používanou funkcionalitu a pomocí konfigurace lze snadno nastavit zejména seznam povolených metod HTTP požadavků (GET, POST, ...) a pravidla kešování zobrazené stránky. Užitečným rozšířením této abstraktní třídy je konkrétní třída org.springframework.web.servlet.mvc.ParameterizableViewController, která navíc obsahuje vlastnost viewName pro možnost konfigurace logického názvu pohledu v konfiguračním souboru. Příklad implementace převezmeme z přiložené vzorové aplikace, konkrétně z třídy cz.morosystems.sportportal.controllers.ClientUsersListController.

public class ClientUsersListController extends ParameterizableViewController{

  private UserManager userManager;
  
  public ModelAndView handleRequestInternal(HttpServletRequest request,
              HttpServletResponse response) 
              throws ServletException, IOException {

    Map model = new HashMap();
    List<User> users = getUserManager().getAllClientUsers();
    Collections.sort(users);
    model.put("users", users);
    return new ModelAndView(getViewName(), "model", model);
  }

... get/set metody pro atribut userManager ...
}

A příslušné deklarace v konfiguračním souboru aplikačního kontextu (v přiložené aplikaci v souboru WEB-INF\spring\webApplicationContext-admin.xml) vypadají takto:

<bean id="urlMappingForAdmin" 
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">   
  <property name="order" value="3" />
  <property name="mappings">
    <props>
      ... 
      <prop key="/secure/admin/client-users/list.html">
        clientUsersListController
      </prop>
      ...
    </props>
  </property> 
  <property name="alwaysUseFullPath" value="false" />
</bean> 
    
<bean id="clientUsersListController" 
  class="cz.morosystems.sportportal.controllers.ClientUsersListController" >
  <property name="supportedMethods" value="GET,HEAD" />
  <!-- hodnota 0 představuje zákaz cachování, při -1 nebudou nastaveny 
       žádné cachovací HTTP hlavičky -->
  <property name="cacheSeconds" value="60" /> 
  <property name="userManager" ref="userManager" />
  <property name="viewName" value="secure/admin/users/client/list"/>
</bean>

V příkladu v zájmu zestručnění neuvádíme deklaraci servisního objektu s id userManager. V naší třídě ClientUsersListController jsme definovali JavaBeans™ vlastnost userManager, vlastnost viewName je součástí rozšiřované třídy ParameterizableViewController, ostatní vlastnosti jsou součástí třídy AbstractController. Vlastnost supportedMethods definuje čárkou oddělený seznam povolených typů HTTP požadavků, atribut cacheSeconds pak říká, jak dlouhé kešování dané stránky bude nastaveno v hlavičkách HTTP odpovědi.

Přistupovat k modelu v JSP/JSTL šabloně lze pomocí standardních značek JSTL:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

<html><head><title>Seznam uživatelů</title></head>
<body>
  <h1>Seznam uživatelů</h1>
  <ul>
    <c:forEach items="${model.users}" var="user">
      <li><c:out value="${user.name}"/></li>
    </c:forEach>
  </ul>
</body></html>

Jednoduchý formulářový kontroler

Poněkud složitější situace nastává při dalším běžném scénáři, při manipulaci s HTML formuláři. Pro tyto případy existuje v uvedené hierarchii kontrolerů abstraktní třída org.springframework.web.servlet.mvc.AbstractFormController, kterou ovšem rozšiřují dvě užitečnější třídy. První z nich je třída org.springframework.web.servlet.mvc.SimpleFormController, která je vhodná pro práci s jednoduchými jednostránkovými formuláři. Použijeme-li jako předka pro náš kontroler tuto třídu, můžeme využít kustomizace kteréhokoliv z kroků následujícího toku řízení:

  1. Na URL, kterou obsluhuje daný kontroler, přichází HTTP GET požadavek. Kontroler tedy ví, že jde o požadavek na první zobrazení formulářového pohledu.

  2. Je zavolána metoda kontroleru formBackingObject, která vrací tzv. příkazový (command) neboli formulářový (form) objekt, jehož úkolem bude zapouzdřit data manipulovaná ve formuláři. Tento objekt opět musí vyhovovat standardu JavaBeans™. Vrácený objekt může být buď prázdný (prázdný formulář) nebo získaný například z databáze (formulář s předvyplněnými daty již při prvním zobrazení).

  3. Je zavolána metoda kontroleru initBinder, jejímž překrytím lze upravit mechanismus konverze řetězcových dat formuláře na atributy (často neřetězcového typu) formulářového objektu a obráceně. Při konverzi jsou opět použity editory vlastností (viz „Atributy běžných typů“) a v metodě initBinder je v podstatě možno registrovat konkrétní editor vlastností na konkrétní vlastnost formulářového objektu.

  4. Je zavolána metoda kontroleru referenceData, vracející model, jenž je potřeba pro zobrazení formulářového pohledu.

  5. Je zobrazen formulářový pohled definovaný v konfiguračním souboru pomocí vlastnosti formView. V tomto pohledu jsou svázány vlastnosti formulářového objektu s příslušnými formulářovými poli (toto svázání je řešeno prostřednictvím speciální knihovny značek, kterou představíme v následujícím oddíle) a hodnoty vlastností jsou zobrazeny jako implicitní hodnoty v příslušných formulářových polích.

  6. Uživatel edituje formulářová data a odesílá formulář, při čemž POST požadavek je odeslán na tutéž URL.

  7. Kontroler detekuje HTTP POST požadavek, což je pro něj příkaz, aby spustil druhou část toku řízení.

  8. Formulářový objekt je pozměněn na základě uživatelem zadaných dat ve formuláři.

  9. Je zavolána metoda kontroleru onBind.

  10. Pokud je nastavena vlastnost kontroleru validateOnBinding na true, tak jsou na formulářový objekt aplikovány všechny registrované validátory, které mají za úkol ověřit přípustnost uživatelem zadaných dat. Validátory musí implementovat rozhraní org.springframework.validation.Validator a zejména metodu validate(Object, Errors). Všechny chyby při validaci jsou zaznamenány v objektu s rozhraním org.springframework.validation.Errors.

  11. Je zavolána metoda kontroleru onBindAndValidate, kde lze provést další validace a registrovat další chyby do objektu typu Errors.

  12. Jsou-li v objektu Errors registrovány nějaké chyby, pak je znovu zobrazen formulářový pohled spolu s uživatelem zadanými daty v příslušných formulářových polích. Na základě informací v objektu Errors lze vypsat hlášení o chybách vstupu. Následují kroky od bodu 4.

  13. Nejsou-li v objektu Errors registrovány žádné chyby, pak je zavolána metoda onSubmit, která provede prostřednictvím aplikační vrstvy finální zpracování formulářového objektu, například jeho uložení v databázi.

Výhodou tohoto řešení je možnost použití objektu z doménového modelu jako formulářového objektu, aniž bychom vždy museli využívat speciálních transferových objektů a převádět data z doménového objektu do transferového a naopak.

Například chceme-li editovat údaje systémového uživatele reprezentovaného třídou User, tak překryjeme metodu formBackingObject tak, aby získala příslušnou instanci třídy User z databáze a vrátila ji. Aktuální údaje systémového uživatele budou zobrazeny ve formuláři. Po jejich editaci uživatelem aplikace bude instance třídy User poslána zpět kontroleru, který ji pouze předá servisní vrstvě k aktualizaci v databázi. Odpadá tak nutnost manuálního zpracování parametrů HTTP požadavku či použití DTO (Data Transfer Object).

Právě popsaný příklad ilustrujeme na skutečném, jen mírně zjednodušeném formulářovém kontroleru (cz.morosystems.sportportal.controllers.UserEditFormController) z přiložené vzorové aplikace.

public class UserEditFormController extends SimpleFormController {

  private UserManager userManager;

  protected Object formBackingObject(HttpServletRequest request) 
         throws Exception {
    
    String userID = request.getParameter("id");
    //na základě parametru požadavku id získáme uživatele z databáze k editaci
    User user = userManager.getUserByID(userID);
    /* není-li uživatel pro dané id v databázi, vyhodíme výjimku, která bude 
       zpracována prostřednictvím objektu typu ExceptionResolver definovaného 
       v konfiguračním souboru (viz oddíl 4.8)
    */
    if (user==null) throw new NoSuchAddressException();
    return user;
  }

  public ModelAndView onSubmit(HttpServletRequest request, 
        HttpServletResponse response, Object command, BindException errors) 
        throws ServletException {
    
    /* formulářový objekt je typu uživatel a je následně prostřednictvím 
       manažera uložen do databáze */
    User user = (User)command;
    getUserManager().setUser(user);
    /* následně zobrazený pohled je definován prostřednictvím vlastnosti 
       'successView' v konfiguračním souboru */
    return new ModelAndView(getSuccessView());
  }

... get/set metody pro atribut userManager ...
}

Třída User ve své nejjednodušší podobě by vypadala takto:

public class User {
  private String id;
  private String firstname;
  private String surname;
  private String email;

... get/set metody všech atributů ...
}

Skutečná třída cz.morosystems.sportportal.pojo.User ovšem obsahuje množství dalších vlastností a metod. Příslušné definice v konfiguračním XML souboru jsou následující:

<bean id="urlMappingForAdmin" 
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">   
  <property name="order" value="3" />
  <property name="mappings">
    <props>
      ... 
      <prop key="/secure/admin/admin-users/edit.html">
        adminUserEditFormController
      </prop>
      ...
    </props>
  </property> 
  <property name="alwaysUseFullPath" value="false" />
</bean> 

<bean id="adminUserEditFormController" 
      class="cz.morosystems.sportportal.controllers.UserEditFormController">
  <property name="sessionForm" value="true"/>
  <property name="commandName" value="user"/>
  <property name="commandClass" value="cz.morosystems.sportportal.pojo.User"/>
  <property name="validator" ref="userEditFormValidator"/>
  <property name="formView" value="secure/admin/users/admin/edit"/>
  <property name="successView" value="redirect:list.html"/>
  <property name="userManager" value="userManager" />
</bean>
 
<bean id="userEditFormValidator"
      class="cz.morosystems.sportportal.validators.UserEditFormValidator" >
  <property name="userManager" value="userManager" />
</bean>

Atribut sessionForm indikuje, zda má být pro uložení formulářového objektu mezi GET a POST požadavkem uživatele použito uživatelské sezení. V případě, že bychom tuto možnost zakázali, nebylo by možno editovat již existující údaje získané z databáze, tak jako jsme to předvedli v předcházejícím příkladě, a bylo by možno pracovat v podstatě pouze s iniciálně prázdnými formuláři. Atribut commandName určuje název, pod kterým bude formulářový objekt přístupný ve formulářovém pohledu jako atribut HTTP požadavku. Atribut commandClass definuje třídu formulářového objektu, atributy formView a successView pak specifikují formulářový a finální pohled. No a konečně atribut validator se odkazuje na instanci s rozhraním Validator, která vypadá takto:

import org.apache.commons.validator.EmailValidator;
import org.springframework.validation.ValidationUtils;
import cz.morosystems.sportportal.pojo.User;

public class UserEditFormValidator implements Validator {

  private UserManager userManager;

  public boolean supports(Class clazz) {
    return clazz.equals(User.class);
  }

  public void validate(Object object, Errors errors) {
    User user = (User) object; 
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", 
      "error.user.email.required", "Value can't be empty");

    if (!EmailValidator.getInstance().isValid(user.getEmail())) {
      errors.rejectValue("email", "error.user.email.not-valid", null, 
                         "Value must be in correct format.");
    }        
  }

... get/set metody pro atribut userManager ...
}

Metoda supports určuje, pro které třídy formulářových objektů je tento validátor určen. V metodě validate pak kontrolujeme pouze vlastnost email formulářového objektu. Nejdříve ověříme, zda uživatel nezadal prázdnou hodnotu a pokud ano, tak v objektu errors zaregistrujeme chybu pro vlastnost email s chybovým hlášením specifikovaným pomocí i18n klíče error.user.email.required. Při zobrazení chyby ve formulářovém pohledu pak bude tento klíč využit pro internacionalizaci hlášení. Při registraci chyby jsme navíc ještě uvedli defaultní zprávu, která bude zobrazena v případě, že pro daný klíč nebude nalezena žádná hodnota ve zdroji zpráv (viz „Zdroje zpráv“). Následně pak podobně ověříme i správnost formátu zadaného e-mailu.

Třída cz.morosystems.sportportal.controllers.ExistingCompetitorsFormController v přiložené vzorové aplikaci představuje zdokumentovaný příklad komplexního formulářového kontroleru.

Knihovny značek JSP

Vzhledem k blízké příbuznosti s tématem formulářového kontroleru, který jsme si představili v předchozím oddíle, si teď ukážeme práci s knihovnou značek JSP rámce Spring pro manipulaci s formuláři. Tato knihovna je novinkou rámce Spring™ od verze 2.0 a je identifikována pomocí URI http://www.springframework.org/tags/form. Jde v podstatě o sadu značek, které zjednodušují provázání atributů (vlastností) formulářových objektů s formulářovými poli.

V předchozích verzích rámce Spring™ se k tomuto účelu používala obecná knihovna značek JSP identifikovaná prostřednictvím URI http://www.springframework.org/tags, která je i ve verzi 2.0 stále přítomna. K provázání vlastností formulářového objektu s formulářovými poli sloužily značky bind, nestedPath a hasBindErrors.

Náš příklad s editací údajů uživatele tedy uzavřeme jednoduchým poupraveným příkladem JSP/JSTL šablony (/WEB-INF/jsp/secure/admin/users/admin/edit.jsp v přiložené vzorové aplikaci), kde bude demonstrováno použití značek bind a hasBindErrors z obecné knihovny značek JSP.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %> 
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html><head><title>Uživatel - editace údajů</title></head>
<body>
  
  <div class="chyby">
<%-- název formulářového objektu 'user' byl definován vlastností 
'commandName' v definici kontroleru v konfiguračním souboru (viz výše) --%>
    <spring:hasBindErrors name="user">
      <p class="chyba"><fmt:message key="error.email.index.chyby"/></p>
      <ul>
        <c:forEach items="${errors.allErrors}" var="error" >
          <li style="color:red"><fmt:message key="${error.code}" /></li>
        </c:forEach>
      </ul>
    </spring:hasBindErrors>
  </div>

  <form method="post" action="" >
    <spring:bind path="user.firstname">
      <input type="text" name="<c:out value="${status.expression}" />" 
             value="<c:out value="${status.value}"/>" />
      <c:out value="${status.errorMessage}"/>
    </spring:bind>
    <spring:bind path="user.surname">
      příjmení: <input type="text" 
                       name="<c:out value="${status.expression}"/>" 
                       value="<c:out value="${status.value}"/>" />
      <c:out value="${status.errorMessage}"/>
    </spring:bind>
    <spring:bind path="user.email">
      e-mail: <input type="text" 
                     name="<c:out value="${status.expression}" />" 
                     value="<c:out value="${status.value}"/>" />
      <c:out value="${status.errorMessage}"/>
    </spring:bind>
    
    <input type="submit" value="Upravit" />
  </form>

</body></html>

V první části jsme souhrnně zobrazili všechny chybové zprávy, pokud nějaké existují, v samotném formuláři jsme pak definovali jednotlivá formulářová pole a pomocí značky bind a jejího atributu path jsme je namapovali na vlastnosti firstname, surname a email formulářového objektu s názvem user. Proměnná s názvem status, dostupná uvnitř značky bind, představuje danou mapovanou vlastnost formulářového objektu. Proměnná status.value obsahuje aktuální hodnotu vlastnosti, status.expression plnou cestu daného atributu (např. user.email) a status.errorMessage pak případné chybové hlášení registrované k dané vlastnosti.

Pomocí nové knihovny značek pro manipulaci s formuláři bychom tutéž šablonu navrhli takto:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %> 
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html><head><title>Uživatel - editace údajů</title></head>
<body>

  <form:form commandName="user" action="" method="post">
    <div class="chyby">
      <form:errors path="*" cssStyle="color:red" />
    </div>
    jméno:    <form:input path="firstname" />
              <form:errors path="firstname" />
    příjmení: <form:input path="surname" />
              <form:errors path="surname" />
    email:    <form:input path="email" />
              <form:errors path="email" />
    <input type="submit" value="Upravit" />
  </form:form>

</body></html>

Je vidět výrazné zjednodušení a odstranění opakujícího se kódu. Bohužel v této práci nemáme prostor pro demonstraci složitějších možností obou knihoven značek ve spolupráci s formulářovými kontrolery, které jsou ovšem k vidění v přiložené vzorové aplikaci. Jde zejména o demonstraci dalších formulářových prvků (select boxy, tlačítka typu radio atd., viz např. WEB-INF\jsp\secure\client\competitors\create-team.jsp), mapování parametrů požadavku na kolekce objektů (cz.morosystems.sportportal.controllers.CompetitorCreateTeamFormController.initBinder() ve spolupráci s editorem vlastností cz.morosystems.sportportal.editors.SportBranchEditor) apod.

Kontroler pro vícekrokové formuláře

Chceme-li v aplikaci použít vícestránkové či vícekrokové formuláře, pak je vhodné sáhnout po druhém z formulářových kontrolerů a rozšířit třídu org.springframework.web.servlet.mvc.AbstractWizardFormController. Tok řízení tohoto kontroleru je ještě výrazně složitější než je tomu u třídy SimpleFormController a je možné jej nastudovat v javadoc dokumentaci třídy. Shrňme jen stručně jeho funkcionalitu.

Návrhář kontroleru definuje sadu pohledů, které představují jednotlivé kroky vícekrokového formuláře. Tyto kroky jsou identifikovány svým pořadím, a to nulou počínaje. První krok má tedy index 0, druhý index 1 atd. Přechod mezi jednotlivými kroky je realizován pomocí detekce parametru požadavku s názvem _targetX, kde X je index následujícího kroku. Toho je typicky dosahováno pomocí odesílacího prvku dané části formuláře, tedy například takto:

<input type="submit" name="_target2" value="Další krok" />
<input type="submit" name="_target0" value="Krok zpět" />

Podobně je tomu s parametrem požadavku s názvem _finish, který indikuje definitivní ukončení a zpracování všech dosud sebraných dat v jednotlivých krocích (v kontroleru je zavolána metoda onFinish), a s parametrem požadavku s názvem _cancel, jehož přítomnost kontroler interpretuje jako požadavek ukončení práce s formulářem bez zpracování sebraných dat (je zavolána metoda onCancel).

Na pozadí práce s formulářem je rovněž v akci formulářový objekt, na nějž jsou jednotlivá formulářová pole jednotlivých kroků mapována. Tento objekt je průběžně validován a ve finále zpracován a například uložen do databáze.

Příkladem komplexního a zdokumentovaného vícekrokového kontroleru je třída cz.morosystems.sportportal.controllers.SportEventCreateFormController z přiložené vzorové aplikace.

Kontroler pro více podobných akcí

Tento kontroler, tedy kontroler třídy org.springframework.web.servlet.mvc.multiaction.MultiActionController, je jako předek využíván v případech, kdy by jinak bylo nutno nasadit několik jednoduchých kontrolerů, jejichž funkčnost by byla do značné míry podobná. Příkladem takového kontroleru je třída cz.morosystems.sportportal.controllers.PublicSportEventRenderStructureController z přiložené vzorové aplikace (viz také příslušné definice v konfiguračním XML souboru).

Jednorázový kontroler

Rozhraní org.springframework.web.servlet.mvc.throwaway.ThrowawayController je vyděleno z celé dosud popisované hierarchie kontrolerů. Dosud jsme předpokládali, že naše implementace kontrolerů jsou bezstavové a že tedy budou v rámci naší aplikace definovány jako jedináčci (Singleton). Tyto kontrolery (a to se týká zejména formulářových kontrolerů) pak pracují s dočasnými formulářovými objekty, které drží stav.

Návrh rozhraní ThrowawayController byl inspirován zejména pojetím kontrolerů (neboli akcí) v aplikačních rámcích WebWork™ či Maverick. ThrowawayController je vytvářen vždy znovu pro každý požadavek a obsahuje dočasná data stejně jako aplikační logiku, která s nimi pracuje. Jde tedy o jakýsi kontroler a příkazový či formulářový objekt v jednom.

Tento typ kontroleru je však součástí rámce Spring™ přece jen spíše pro ty uživatele, kteří jsou na podobnou filozofii zvyklí, než pro začátečníky, kteří se s webovým MVC návrhovým vzorem teprve seznamují.

Komentáře

komentoval: Ivan Polak, dne: 06. 01. 2008, 23:13

neviem, ci v tejto casti nie je chyba:

<bean id=„adminUserE­ditFormContro­ller“ class=„cz.moro­systems.sportpor­tal.controller­s.UserEditFor­mController“> <property name=„sessionForm“ value=„true“/> <property name=„commandName“ value=„user“/> <property name=„commandClass“ value=„cz.moro­systems.sportpor­tal.pojo.User“/> <property name=„validator“ ref=„userEdit­FormValidator“/> <property name=„formView“ value=„secure­/admin/users/ad­min/edit“/> <property name=„successView“ value=„redirec­t:list.html“/> <property name=„userManager“ value=„userManager“ /> </bean>

predpokladam ze userManager je bean, definovany niekde ako:

<bean id=„userManager“…­./>

nemalo by byt ? :

<property name=„userManager“ ref=„userManager“ />

dakujem

Ivan

komentoval: Tomáš Páral [http://morosystems.cz], dne: 07. 01. 2008, 10:07

Dobrý den, ano, máte pravdu, opravdu je to chyba. Děkujeme za upozornění. Tomáš

komentoval: Pavel Macík, dne: 20. 01. 2008, 20:23

Zdravim…

V textu máte špatně uvedenu cestu k obrázku 3.1 „Hierarchie kontrolerů“

Máte tam src=„../kontrolery“ místo src=„../kontro­lery.png“ :)

komentoval: Tomáš Páral [http://morosystems.cz], dne: 23. 01. 2008, 09:47

Děkuji, Pavle, opraveno

komentoval: MrHappy, dne: 24. 07. 2009, 13:44

Dotaz: zajima mne jak udelat toto: zobrazi se stranka s formularem search.html (SearchController), Uzivatel vyplni formular a klikne na submit. Formular se zpracuje a zobrazi se do successView (resultView). V url vsak zustane search.html aniz by doslo k presmerovani na result.html. Vidim, ze pro presmerovani pouzivate znacku <property name=„successView“ value=„redirec­t:list.html“/>, ktera vsak provede RedirectView, tj. v url to vygeneruje vsechny parametry, ktere chceme predat. Ja bych vsak potreboval, aby se mi misto search.html po zpracovani formulare zobrazilo result.html v url. Evidentne je treba udelat nejak presmerovani, ale potrebuji, aby bylo pomoci POST a ne pomoci GET pres url. Chci mit kratke url bez parametru. Jde to ? Zjistil jsem, ze nekteri si nejak napsali svuj kod. A pak vim, ze toto by melo byt implementovano az ve Spring v3.0. Prosim o radu.

komentoval: Tomáš Páral [http://morosystems.cz], dne: 24. 07. 2009, 14:24

Dobrý den,

to co popisujete se dá vyřešit uložením potřebných dat do session (pokud tomu nebrání architekutra aplikace). V kontroleru uložíte data do session a přesměrujete na result.html, kde to ze session přečtete, smažete a zobrazíte. Druhou možností je předávat si tato data v url, ale o to nemáte zájem.

Dobrou praxí je, že pokud je formulář odeslán POST, potom vždy dochází k přesměrování pomocí GET na nějakou výslednou stránku. Pokud to neuděláte, může to mít dva problémy:

  • pokud po odeslání dáte v IE zpět, tak se vám zobrazí, že vypršela platnost dané stránky
  • pokud dáte refresh, tak se vám začnou data znovu zpracovávat, což pravděpodobně povede k nechtěnému zpracování a uložení dat do databáze.

Z těchto dvou důvodů nevidím důvod, proč zavádět ve Springu přeposílání dat metodou POST – chápu to jako kontraproduktivní akci a ještě jsem o tom neslyšel, nicméně si dokážu představit, že tam tato možnost bude, ale ne k tomuto účelu, který popisujete.

Snad jsem vám pomohl,

Hezký den,

Tom

komentoval: MrHappy, dne: 24. 07. 2009, 15:32

jj pomohl Diky ! Poradilo se mi zajistit spravne zobrazena url, tak jak se ocekava trosku jinym zpusobem, avsak dochazi tam k problemu s refresh jak zminujete. Prekopu to tedy na ukladani dat do session a pote presmeruji.

komentoval: Jakub, dne: 15. 02. 2010, 10:18

Dobrý den, chtěl jsem se zeptat na takovou věc, nicméně nevím jestli to souvisí s tématem a zda je ještě vůbec tato diskuze aktuální.

Plánuji využít Spring na jeden projekt a chtěl jsem se zeptat jestli je technicky možné nemuset deployovat na server vždy celou aplikaci pro každou její běžící instanci, ale mít nějaké aplikační „core“ nebo seznam knihoven, které by byly ve webovém containeru sdílené pro menší deployované aplikace a tyto aplikace by sdíleli core knihovny v nějaké verzi aniž by je v sobě přímo obsahovaly?

Například mít základ nějakého publikačního systému, který bude v jedné instanci a několik webů, které budou mít každý svou instanci a deploy? Čili budou všechny sdílet jedno core a budou obsahovat jen věci specializované pro daný konkrétní web. Je to nějak možné se Springem?

děkuji za informaci a přeji hezký den! Jakub

komentoval: Tomáš Páral [http://morosystems.cz], dne: 15. 02. 2010, 11:15

To, co popisujete by šlo určitě udělat pravděpodobně i bez Springu.

Otázkou je, jestli k tomu přistoupit tímto způsobem nebo připravit aplikaci tak, aby byla nasazena jednou a byla schopna obsloužit různé domény i jejich jazykové mutace. Tohle je podle mě standardní řešení.

Vložit komentář

Můžete používat značkovací jazyk Texy!


Jméno:
E-mail:
Url:
Komentář:
1 + 2 =
 
MoroSystems, s.r.o.