31. března 2009

Spring MVC: GET kontroler

Dlouho jsem neuváděl žádný můj zdrojový kód, tak to dnes zkusím napravit. Spring MVC nabízí pro zpracování požadavku GET dva základní kontrolery:

  • ParameterizableViewController - jednoduchý kontroler, který pouze vyžaduje zadání cílového view, které se následně zobrazí.

  • BaseCommandController - kontroler, který pracuje s parametry requestu přes commandy. Tedy kontroler automaticky mapuje parametry requestu do atributů objektu (commandu), je možné využívat validace, bindery apod.

V každé aplikace resp. projektu se hodí mít nějaké své předky-kontrolery, které mohou zprvu být prázdné, ale vždy se časem ukáže, že je potřeba mít nějakou společnou funkcionalitu nebo společná data pro většinu nebo všechny kontrolery. My jsme se na projektu rozhodli mít dva předky-kontrolery, jeden pro práci s formuláři (zpracování POSTu) a jeden pro práci s GETem. A právě u GETu jsem narazil na to, že bych rád měl možnost výběru - jednou mi stačí si vytáhnout parametr přímo z requestu a jindy využiji command s validací. Takže z tohoto důvodu jsem udělal následující mix dvou výše uvedených kontrolerů. Pro úplnost ještě musím zmínit použití rozhraní IDataModelProvider, které slouží k předávání modelu resp. dat společných jak pro GET, tak pro POST kontrolery.


/**
* Zakladni implementace kontroleru pro zobrazovani stranek pres GET metodu, tj. zobrazeni
* JSP stranky, pripadne predani promennych na stranku.
*
* <p>Kontroler muze pracovat ve dvou "rezimech" - s commandem a bez nej.
* Pokud chceme pracovat s commandy a tedy mapovat parametry requestu do commandu, tak pak
* je vhodne v podtride implementovat metodu
* {@link #handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object, org.springframework.validation.BindException)}
* a nastavit tridu commandu {@link #commandClass}.
*
* <p>Pokud nam staci pristup k parametrum requestu (bez commandu), tak pak je vhodne implementovat
* v podtride metodu {@link #handleRequestInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}.
* Toto chovani je defaultni.
*
* @see BaseFormController
*/
public class BaseGetController extends AbstractCommandController {
private static final Logger logger = Logger.getLogger(BaseGetController.class);

private IDataModelProvider dataModelProvider;
private String viewName;
private boolean supressBinding = false;
private boolean supressValidation = false;

protected void initApplicationContext() {
if (this.viewName == null) {
throw new IllegalArgumentException("Property 'viewName' is required");
}
}

/**
* Metoda resi, zda se bude pracovat s commandem (a tedy request parametry se budou
* napojovat na command) a nebo zda se bude pracovat bez commandu (a tedy je nutne pracovat samostatne
* primo s parametry requestu).
*
* <p><b>Tato metoda je vhodna k implementaci v podtridach, pokud nepracujeme s commandem
* a staci nam pristup k requestu.</b>
*/
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (getCommandClass() == null) {
//pracujeme bez commandu
this.supressBinding = true;
this.supressValidation = true;
return this.handle(request, response, null, null);
}
else {
//pracujeme s commandem
return super.handleRequestInternal(request, response);
}
}

/**
* Metoda vklada spolecna data do vysledneho modelu.
*
* <p><b>Tato metoda je vhodna k implementaci v podtridach, pokud pracujeme s commandem</b>.
* Pokud neni {@link #commandClass} definovano, tak pak vstupni parametry <i>command</i> a <i>errors</i> jsou null.
*/
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object command,
BindException errors) throws Exception {
ModelAndView mav = new ModelAndView(getViewName());

mav.addAllObjects(dataModelProvider.getModelData());

if (logger.isDebugEnabled()) {
logger.debug("BaseGetController returns: " + mav);
}

return mav;
}

protected boolean suppressBinding(HttpServletRequest request) {
return this.supressBinding || super.suppressBinding(request);
}

protected boolean suppressValidation(HttpServletRequest request, Object command) {
return this.supressValidation || super.suppressValidation(request, command);
}

public void setDataModelProvider(IDataModelProvider dataModelProvider) {
this.dataModelProvider = dataModelProvider;
}

/**
* Set the name of the view to delegate to.
*/
public void setViewName(String viewName) {
this.viewName = viewName;
}

/**
* Return the name of the view to delegate to.
*/
public String getViewName() {
return this.viewName;
}
}

4 komentáře:

littleli řekl(a)...

Doporučil bych v nové aplikaci rozhodně využít anotovaných kontrolerů ze Spring MVC 2.5+. Tohle staré rozhraní, ne že by bylo špatné, ale zkrátka ten kód je ošklivý a nepřehledný.

Petr Jůza řekl(a)...

Nejsem zrovna přívrženec stylu "anotace všude, kde se dá" a zrovna v tomto případě dávám přednost klasickému stylu.

Kamil řekl(a)...

Ja bych taky doporucil novou syntax s @RequestMapping. Je to uzasne citelne a prehledne. Jo a aby nedoslo k mylce. Zadny component-scan ani @Autowired tam nemusi a ani nema co delat.Pak je to opravdu nejprehlednejsi a nejelegantnejsi mozna verze.

Kamil řekl(a)...

Zapomnel jsem rict to hlavni: Diky flexibilite parametru u @RequestMapping je cela funkcionalita tohoto kontrolleru uplne zbytecna...