Right now, my web.xml is configured in the way that Spring MVC will take over anything that has *.html suffix. I have a few controllers that generate the web links based on current user state.
For instance, I have the following code to determine what the "Next" button should point to:
if (nextSlide != null) {
nextLink = String.format("/%s/module/%d/slide/%d.html", studyName, moduleId, nextSlide.getKey());
}
else {
nextLink = String.format("/%s/module/all.html", studyName);
}
But, I don't like this approach because I'm hardcoding the ".html" to the links here. I could certainly create a factory that generates links with ".html" suffix to hide from all the controller code, but that still requires me to hardcode ".html" once in my Java code... not too DRY.
I'm basically hoping Spring MVC has some APIs that allow me to construct the links. If these APIs don't exist, do you hardcode the suffix in the Java code like what I do here?
There is not. Given what you currently have, you have three options. Two of these are your option, and dwb's option. The other is to create your own taglib.
It seems like you're doing some kind of wizard functionality (where screen 2 is dependent on a click from screen 1). If that's the case, I would look into Spring Webflow.
Another possibility is to perform an AJAX callout to some sort of decision method, or you could handle this all on the client side with JavaScript.
Also, FWIW, I don't think coding .html is breaking DRY, but I would be concerned about maintenance in case your servlet ever matched on something other than this suffix. You could most likely do something similar to the below example by just essentially returning a new ModelAndView or redirecting to one at least:
#RequestMapping("slide/{id}")
public String goToSlide(#PathParam("id") String id, ModelMap model) {
model.adAttribute("slide", slideService.findById(id));
return "slides/slide";
}
#RequestMapping("all")
public String getAllSlides() {
return "slides/all";
}
#RequestMapping(value="slideshow/{id}", method=RequestMethod.GET)
public String getSlideshow(#PathParam("id") String slideshowId, ModelMap model) {
model.attAttribute("slideshow", slideshowService.findById(slideshowId));
return "slides/slideshow";
}
#RequestMapping(value="slideshow", method=RequestMethod.POST)
public String postSlideshow(#QueryParam("slideId") String slideId) {
if(slideId != null) {
return "slide/" + slideId;
}
return "redirect:all";
}
It seems like building URLs should be handled in the view rather than the controller. Moving it to the view would eliminate the need to hard code anything in Java. You'd also be able to make use of Spring's URL tag. From the Spring API docs:
<spring:url value="/url/path/{variableName}">
<spring:param name="variableName" value="more than JSTL c:url" />
</spring:url>
// Results in: /currentApplicationContext/url/path/more%20than%20JSTL%20c%3Aurl
Related
I am writing a web app with Spring 4.0.
I have written my controllers in what I believe to be the normal way using the #RequestMapping annotation to define the url pattern which the controller handles.
The snippet below illustrates this for a controller which displays a testimonial ...
#Controller
#RequestMapping("/testimonialView")
public class TestimonialRequestController {
#RequestMapping(value="/{testimonialName}", method=RequestMethod.GET)
public ModelAndView testimonialRequest(#PathVariable String testimonialName, ModelAndView modelAndView) throws FileNotFoundException {
Testimonial testimonial;
. . .
}
}
Elsewhere in my application I want to generate a link bar which includes all the testimonials to include in my left hand nav.
At the moment, when I construct the href for the anchor element to go into the link bar, I am hardcoding the url, like this:
String href="/testimonialView/" + testimonialName;
This does not seem right. If later on I want to change the url structure I have to change it in at least two places - possibly more. Once where the incoming URL is matched to the controller, and once to construct the anchor element which a user will click to invoke that URL.
Is there a best practice way of dealing with this problem? It must be a common one. Is it as simple as using Constants to represent the URLs and accessing these from different places? I know my example is simple but I am assuming the problem must exist for much larger web apps with complex URL structure so I want to understand what best practice is.
I hope this isn't a stupid question. I am keen to ensure that I implement best practice right from the beginning. I have looked through Stackoverflow and Google but nothing quite answers this specific question.
Any help gratefully received.
The short answer is that you can't do this dynamically because #RequestMapping puts data into the code at compile time.
However, there are a couple of options that work.
You can define the string constants in a separate class - this will make it easier for you to change the names of URLs
You can explore the request mappings at runtime within Spring, so could have some code that found URLs you'd coded elsewhere - I've done this for identifying when a URL is dynamic content, vs coded content.
My recommendation is
public class URLs {
public static final String TESTIMONIAL_VIEW = "/testimonialView";
}
with
#RequestMapping(URLs.TESTIMONIAL_VIEW)
and
String href= URLs.TESTIMONIAL_VIEW + "/" + testimonialName;
There isn't any better practice for this afaik. Most you can do is, as Ashley said, is to use constants. But as with any other client-server situation such as the Web, if you change the contract (the url in this case) you'll have to do so for both the client (i.e. the links) and the server (the controller mappings).
I would also mention that your controller can be more general, for example have a "Testimonials" controller and "view/{name}" as an action within that controller.
Hope this helps
How can I set the language (i18n) not only from the users browser settings but also from subdomains (which should have higher priority) in playframework 2.2 (java)?
The following should work:
example.com -> english or german, depending on browser settings
en.example.com -> force english
de.example.com -> force german
The user should be able to switch between subdomains without losing the session.
Because I have a lot of java controllers, it would be great if the solution works in a centralized place (like Global.java with a filter which may be in scala).
You can use i.e. changeLang(String code) method of play.mvc.Controller superclass for this.
Then you need to tell to your language resolver, which domain uses which language us default, probebly using application.conf or databse records for this. Next depending on what you want to achieve, just use Global class to intercept all your request, or create a simple action which will change the language and they will return to the same page (so user can decide himself which language he wants to use).
It's some kind of cookie based machanism, so I'm not sure if it isn't required to perform forced page reload (ie. by redirecting to the same path) anyway I assume that you will tell us when you'll check it ;)
Edit:
that could be ie. like
public Action onRequest(final Http.Request request, final Method actionMethod) {
if (request.host().equals("de.yourdomain.tld")
&& (request.cookie("PLAY_LANG") == null || !request.cookie("PLAY_LANG").value().equals("de"))) {
return new Action.Simple() {
public Result call(Http.Context ctx) throws Throwable {
ctx.changeLang("de");
return redirect(request.path());
}
};
} else {
return super.onRequest(request, actionMethod);
}
}
Just make sure that you have de lang added in application.conf, otherwise you'll get beautiful, endless redirection loop. the PLAY_LANG is typical Play's cookie name for storing selected language.
Im trying to understand how the Passive View design pattern works for simple web apps.
Can someone provide a simple example of this pattern using these requirements:
View is a JSP that prints HELLO WORLD!
Data is persisted in the data store as "hello world", call to retrieve data can be a stub
Provide example files for pieces (presenter, view, etc) and denote which piece of the pattern each file represents.
No frameworks/DSLs should be used other than jstl/el (which are optional)
Thanks
UPDATE 1: Adding my understanding of how this would be structured.
// Presenter; responsible for multiple "rendtions" of a particular view (show, index, edit, summary, etc.)
public class HelloWorldPresenter {
private HttpServletRequest request;
private DataStore dateStore;
public HelloWorldPresenter(HttpServletRequest request) {
this.request = request;
this.dataStore = DataStoreUtil.getDataStore(request);
// Do a bunch of other inits that all getXXXModels() will use
}
public getShowModel() {
HelloWorldShowModel model = new HelloWorldShowModel();
String tmp = makeLoud(this.dataStore.getMyData()); // Stub method
model.setText(tmp);
return model;
}
private String makeLoud(String str) {
return str.toUpperCase() + "!";
}
}
// Model used by view
public class HelloWorldShowModel {
private String text;
public getText() { return this.text };
public setText(String text) { this.text = text; }
}
// View
show.jsp
<c:set var="model" value="new HelloWorldPresenter.getShowModel()"/>
${model.text} -> HELLO WORLD!
or
<% HelloWorldShowModel model = new HelloWorldPresenter(request).getShowModel() %>
<%= model.getText() %>
The things I'm unsure about are:
How the Presenter would be exposed to the View (JSP) since the View shouldnt know about the presenter. I may be mixing semantics though, and the HelloWorldShowModel (which is acting as a "ViewModel" of sorts, is what shouldnt know about the Presenter).
Should I even have the HelloViewShowModel abstraction, or should I simply have a method getText() on my Presenter which is called within the JSP to get the requested text.
If I do have multiple "views" for a resource (ex. Show, Index, Edit, Summary, etc.), should I have multiple Presenters? How should this logic be broken up? Multiple presenters that inherit from a Shared presenter? Should each presenter only be responsible for returning one ViewModel?
I've read through Fowlers articles as well as a number of other write-ups - the problem (for me) is they are written in the context of .NET apps, and I dont understand how all their objects get wired up.
I hope this will aleve concerns of me being "lazy" and looking for a "hand-out" answer :)
With the requirements you state I would say the pattern can't be implemented. If you consider the view to be a JSP then there are no means a controller could actively set any values of UI components. (To me this is the core of the pattern: the controller actually updates the view actively by setting values of input / output UI components, not the other way round (view getting fields from a model object). This can't be done with the above techniques as a JSP has no means to be accessesd this way.
It could be implemented in a web environment based on Javascript though. Consider your view being the DOM and your controller being a Javascript component. The JS controller has direct write access to the DOM and therefore could update single fields actively like the pattern suggests. To update / get the model the JS controller could talk to a server-side system e. g. based on a REST API via Ajax.
A plain templating solution like JSP cannot be used to offload all logic to controller, at least in real world cases. I think this kind of thing can be achieved with JSF.
If you want to learn about how things are done I recommend you to take a look at Spring MVC. It's source code can teach you a lot.
I have a controller with a method that handles incoming GET data, stores some things in the model, and then redirects to another page that deals with these objects.
I can't seem to find any good way of getting the object stored in the first method back out of the model to use in the second method. How can I do this?
Here's the top of the controller:
#Controller
#RequestMapping("/reviews")
#SessionAttributes({"review", "externalReview"})
public class ReviewController {
// [SNIP]
}
Here's the code that adds the objects I'm after to the model:
#RequestMapping(value="/new", params="UName", method=RequestMethod.GET)
public String newFormFromExternal(#ModelAttribute("externalReview") ExternalReview externalReview, Model model) throws IncompleteExternalException {
// Convert the inbound external
Review fromExternal = ExternalReviewUtil.reviewFromExternalReview(externalReview, externalDAO);
// Add the externalReview to the session so we can look to see if we got a reviewee on the way in
model.addAttribute("externalReview", externalReview);
model.addAttribute("review", fromExternal);
return "redirect:/reviews/newFromExternal";
}
You are in luck.
If you are using or have ability to update to the newly released Spring 3.1, you can make use of the newly scoped Flash variables.
http://static.springsource.org/spring/docs/3.1.0.RC1/spring-framework-reference/html/mvc.html#mvc-flash-attributes
If you can't use 3.1, you probably can implement the solution yourself. Essentially you want to capture the model object required to be present in the redirect, put in the session, and remove it once it is retrieved to keep your session from bloating.
Currently, I'm just getting a Map of the model, getting the object I want out by it's key (the String name), and then casting it to the object it really is (rather than just Object).
Here's the code:
#RequestMapping(value="/newFromExternal", method=RequestMethod.GET)
public String newExternalForm(Model model) {
// Get the review from the model
Review review = (Review) model.asMap().get("review");
/*** Do stuff with the review from the model ****/
return "reviews/newFromPacs";
}
This way works, but it seems hacky and clunky. Is this really the only way?
One possible solution is to use #ModelAttribute, though it's quite ugly since you'll need to disable databinding for that attribute (for security):
#RequestMapping(value="/newFromExternal", method=RequestMethod.GET)
public String newExternalForm(#ModelAttribute Review review) {
...
}
#InitBinder("review")
public void disableReviewBinding(WebDataBinder b) {
b.setAllowedFields();
}
So, we are getting to the point in our Spring application where we need to decide how to handle views and content negotiation. Previously, we've only supported one specific content type in our endpoints.
I'm going to throw our what I think our the three approaches.
My question: Which one is generally considered best practice/least amount of maintenance? We are aiming for a solid convention to follow in our application, which can be broken where needed to provide flexibility if desired.
First approach:
Use ContentNegotiatingViewResolver. This would involve a mapping defined in the servlet file... in the controller, each controller action would need to explicitly set the object in a map using some magic string. The controller action would return a string which refers to a template name... sort of like the following:
#RequestMapping(value = "/someMapping/source", method = RequestMethod.GET)
public String foo(Model model)
{
// more stuff here
model.addAttribute(SOME_MODEL_KEY, new ArrayList<String>() { "hello world"});
return "someDummyJsonString";
}
Drawbacks:
View resolvers seem a little unwieldy... they have priorities, you need to override them often times, etc. Also, I don't like the idea of the "magic strings" which are used to refer to template/View bean names.
Second approach:
I think this is new in Spring 3.0, but in RequestMapping you can match on headers... so you can match on the Accept header like so:
#RequestMapping(value="/someMapping", method = RequestMethod.GET, headers="Accept=application/json")
public #ResponseBody SomeBar foo()
{
// call common controller code here
return buildBar();
}
#RequestMapping(value="/someMapping", method = RequestMethod.GET, headers="Accept=text/xml")
public String foo(Model model)
{
model.addAttribute("someModelName", this.buildBar());
return "someTemplateNameProcessedByViewResolver";
}
SomeBar buildBar()
{
return new SomeBar();
}
Drawbacks:
Might not be flexible enough? I'm not sure, but I think I really like the headers approach... I've seen other frameworks (RESTLet, Rails) use something similar.
Third approach:
The third approach involves making a custom View which will negotiate the content based on the Accept header, and throw the model through the appropriate method. This content negotiating view would have to know a template, and load the template etc.:
#RequestMapping(value="/someMapping", method = RequestMethod.GET, headers="Accept=text/xml")
public View foo()
{
SomeBar bar = new SomeBar();
ContentNegotiatingView view = new ContentNegotiatingView(bar, "templateName");
return return view;
}
Drawbacks:
It seems like the view is doing too much in this case... the view would be looking at headers, and setting the response body itself. It might need to set http statuses also.
So, sorry for the wall of text, let me know your thoughts on this. Thanks
Someone else just asked this. See my answer.