Help with spring-json using annotated controllers - ajax

I've scourged the internet for an example that would use both spring-json and annotated controllers, I'm new to spring so I've had no luck adapting the configuration on spring-json's samples (it uses SimpleController et. al.)
Currently I have a controller with 2 mappings, one lists results in html (and works), the other should render json for some ajax calls but when I access the url it returns a 404 and asks for /myapp/jsp/jsonView.jsp. The code on the show method does execute and it even validates the presence of the id param, so it seems that the problem is that it doesn't know how render, as far as I know that's what the viewResolver bean does.
Thanks in advance for any help :)
Here's what I've got:
#Controller
public class ItemController {
//This one works
#RequestMapping(value = "/items", method = RequestMethod.GET)
public ModelMap list() {
ModelMap map = new ModelMap();
map.addAttribute("item", "value");
return map;
}
//This one returns 404, asks for jsonView.jsp
#RequestMapping(value = "/items.json", method = RequestMethod.GET)
public ModelAndView show(#RequestParam(value = "id", required = true) String id) {
Map model = new HashMap();
model.put("firstname", "Peter");
model.put("secondname", "Schmitt");
return new ModelAndView("jsonView", model);
}
}
on myapp-servlet.xml:
<bean name="viewResolver" class="org.springframework.web.servlet.view.XmlViewResolver"/>
on views.xml:
<beans>
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="encoding">
<value>UTF-8</value>
</property>
<property name="contentType">
<value>application/json</value>
</property>
<property name="jsonWriter">
<ref bean="sojoJsonWriter"/>
</property>
<property name="jsonErrors">
<list>
<ref bean="statusError"/>
<ref bean="modelflagError"/>
</list>
</property>
</bean>
<bean name="sojoJsonWriter" class="org.springframework.web.servlet.view.json.writer.sojo.SojoJsonStringWriter">
<property name="convertAllMapValues">
<value>true</value>
</property>
</bean>
<bean name="statusError" class="org.springframework.web.servlet.view.json.error.HttpStatusError">
<property name="errorCode">
<value>311</value>
</property>
</bean>
<bean name="modelflagError" class="org.springframework.web.servlet.view.json.error.ModelFlagError">
<property name="name">
<value>failure</value>
</property>
<property name="value">
<value>true</value>
</property>
</bean>
web.xml:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/myapp/*</url-pattern>
</servlet-mapping>

There could be another alternative: Are you able to upgrade to spring 3 (it has release state now)? There is a fantastic ContentNegotiationResolver which helps a lot when it comes to content-negotatioon and view-resolving.
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
...
If now appending .json to your URL path or using respective 'Accept' HTTP header, passed object (see model.put(...)) is serialized accordingly. For json spring 3 is using jackson by default.

The problem was with the view resolver on the servlet.xml, added a p:order attribute so it would load before the InternalResourceViewResolver
<bean name="viewResolver" class="org.springframework.web.servlet.view.XmlViewResolver" p:order="1"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/jsp/"p:suffix=".jsp" p:order="10"/>

Related

Spring MVC REST produces XML on default

I have a problem with Spring MVC and REST. The problem is that when i post a url without extension or whatever extension other then json or html or htm i am always getting an xml response. But i want it to default to text/html response. I was searching in many topics and cant find the answear to this.
Here is my Controller class :
#RequestMapping(value="/user/{username}", method=RequestMethod.GET)
public String showUserDetails(#PathVariable String username, Model model){
model.addAttribute(userManager.getUser(username));
return "userDetails";
}
#RequestMapping(value = "/user/{username}", method = RequestMethod.GET,
produces={"application/xml", "application/json"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody
User getUser(#PathVariable String username) {
return userManager.getUser(username);
}
Here is my mvc context config:
<mvc:resources mapping="/resources/**"
location="/resources/"/>
<context:component-scan
base-package="com.chodak.controller" />
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="defaultContentType" value="text/html" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json"/>
<entry key="xml" value="application/xml"/>
</map>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.tiles3.TilesView
</value>
</property>
</bean>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
Actually when I tried the built in Eclipse browser it works fine, but when I use firefox or chrome it shows xml response on a request with no extension. I tried using ignoreAcceptHeader, but no change.
Also works on IE :/
If anyone has an idea please help, Thank you.
I actually found out how to do it, i dont really understand why but it is working now, I added default views to the contentresolver like :
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
</bean>
<!-- JAXB XML View -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.chodak.tx.model.User</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
</list>
</property>
and removed the getUser method, the one annoted to produce xml and json. If I leave it with the added default views its still not working. If anyone can explain why it would be awesome :)
You can do
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
// #EnableWebMvc already autoconfigured by Spring Boot
public class MvcConfiguration {
#Bean
public WebMvcConfigurer contentNegotiationConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false)
.favorParameter(true)
.parameterName("mediaType")
.ignoreAcceptHeader(true)
.useJaf(false)
.defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("json", MediaType.APPLICATION_JSON);
// this line alone gave me xhtml for some reason
// configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);
}
};
}
(tried with Spring Boot 1.5.x)
see https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
"What we did, in both cases:
Disabled path extension. Note that favor does not mean use one approach in preference to another, it just enables or disables it. The order of checking is always path extension, parameter, Accept header.
Enable the use of the URL parameter but instead of using the default parameter, format, we will use mediaType instead.
Ignore the Accept header completely. This is often the best approach if most of your clients are actually web-browsers (typically making REST calls via AJAX).
Don't use the JAF, instead specify the media type mappings manually - we only wish to support JSON and XML."

Spring 3.2 with MVC, ContentNegotation, REST and PDF Generator

Let's say, I have a REST styled controller mapping
#RequestMapping(value="users", produces = {MediaType.APPLICATION_JSON_VALUE})
public List<User> listUsers(#ReqestParams Integer offset, #ReqestParams Integer limit, #ReqestParams String query) {
return service.loadUsers(query, offset, limit);
}
Serving JSON (or even XML) is not an issue, this is easy using ContentNegotation and MessageConverters
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true" />
<property name="favorParameter" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="mediaTypes" >
<value>
html=text/html
json=application/json
xml=application/xml
</value>
</property>
</bean>
Now, I need to add support for PDF. Naturally, I want to use (Spring) MVC + REST as much as possible. Most examples I have found implement this with an explicit definition not using REST style, e.g.
#RequestMapping(value="users", produces = {"application/pdf"})
public ModelAndView listUsersAsPdf(#ReqestParams Integer offset, #ReqestParams Integer limit, #ReqestParams String query) {
List<User> users = listUsers(offset, limit, query); // delegated
return new ModelAndView("pdfView", users);
}
That works, but is not very comfortable because for every alternate output (PDF, Excel, ...) I would add a request mapping.
I have already added application/pdf to the content negotation resolver; unfortunately any request with a suffix .pdf or the Accept-Header application/pdf were be responded with 406.
What is the ideal setup for a REST/MVC style pattern to integrate alternate output like PDF?
You can create a WEB-INF/spring/pdf-beans.xml like below.
<bean id="listofusers" class="YourPDFBasedView"/>
And your controller method will return view name as listofusers.
#RequestMapping(value="users")
public ModelAndView listUsersAsPdf(#ReqestParams Integer offset, #ReqestParams Integer limit, #ReqestParams String query) {
List<User> users = listUsers(offset, limit, query); // delegated
return new ModelAndView("listofusers", users);
}
And you can use contentNegotiationViewResolver in this way:
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="WEB-INF/spring/pdf-views.xml"/>
</bean>
<!--
View resolver that delegates to other view resolvers based on the content type
-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<!-- All configuration is now done by the manager - since Spring V3.2 -->
<property name="contentNegotiationManager" ref="cnManager"/>
</bean>
<!--
Setup a simple strategy:
1. Only path extension is taken into account, Accept headers are ignored.
2. Return HTML by default when not sure.
-->
<bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="ignoreAcceptHeader" value="true"/>
<property name="defaultContentType" value="text/html" />
</bean>
For JSON: Create a generic JSON view resolver like below and register it as bean in context file.
public class JsonViewResolver implements ViewResolver {
/**
* Get the view to use.
*
* #return Always returns an instance of {#link MappingJacksonJsonView}.
*/
#Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
MappingJacksonJsonView view = new MappingJacksonJsonView();
view.setPrettyPrint(true); // Lay the JSON out to be nicely readable
return view;
}
}
Same for XML:
public class MarshallingXmlViewResolver implements ViewResolver {
private Marshaller marshaller;
#Autowired
public MarshallingXmlViewResolver(Marshaller marshaller) {
this.marshaller = marshaller;
}
/**
* Get the view to use.
*
* #return Always returns an instance of {#link MappingJacksonJsonView}.
*/
#Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
MarshallingView view = new MarshallingView();
view.setMarshaller(marshaller);
return view;
}
}
and register above xml view resolver in context file like this:
<oxm:jaxb2-marshaller id="marshaller" >
<oxm:class-to-be-bound name="some.package.Account"/>
<oxm:class-to-be-bound name="some.package.Customer"/>
<oxm:class-to-be-bound name="some.package.Transaction"/>
</oxm:jaxb2-marshaller>
<!-- View resolver that returns an XML Marshalling view. -->
<bean class="some.package.MarshallingXmlViewResolver" >
<constructor-arg ref="marshaller"/>
</bean>
You can find more information at this link:
http://spring.io/blog/2013/06/03/content-negotiation-using-views/
Using all view resolver techniques, you can avoid writing duplicate methods in controller, such as one for xml/json, other for excel, other for pdf, another for doc, rss and all.
Knalli, if you replace #ResponseBody with ModelAndView(), you can achieve both the features.
Is there any reason you want to keep #ResponseBody ? I just want to know if I am missing anything, just want to learn.
Other option is to write HttpMessageConverters then:
Some samples are here.
Custom HttpMessageConverter with #ResponseBody to do Json things
http://www.javacodegeeks.com/2013/07/spring-mvc-requestbody-and-responsebody-demystified.html
This is working sample. I have configured contentnegotiationviewresolver for this, and give highest order. After that I have ResourceBundleViewResolver for JSTL and Tiles View, then XmlViewResolver for excelResolver, pdfResolver, rtfResolver. excelResolver, pdfResolver, rtfResolver. XmlViewResolver and ResourceBundleViewResolver works only with MAV only, but MappingJacksonJsonView and MarshallingView takes care for both MAV and #ResponseBody return value.
<bean id="contentNegotiatingResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order"
value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="pdf" value="application/pdf" />
<entry key="xlsx" value="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<entry key="doc" value="application/msword" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<!-- XML View -->
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg>
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>Employee</value>
<value>EmployeeList</value>
</list>
</property>
</bean>
</constructor-arg>
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
id="resourceBundleResolver">
<property name="order" value="#{contentNegotiatingResolver.order+1}" />
</bean>
<bean id="excelResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/tiles/spring-excel-views.xml</value>
</property>
<property name="order" value="#{resourceBundleResolver.order+1}" />
</bean>
<bean id="pdfResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/tiles/spring-pdf-views.xml</value>
</property>
<property name="order" value="#{excelResolver.order+1}" />
</bean>
<bean id="rtfResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/tiles/spring-rtf-views.xml</value>
</property>
<property name="order" value="#{excelResolver.order+1}" />
</bean>
And our XMLViewResolver spring-pdf-views.xml looks like this.
<bean id="employees"
class="EmployeePDFView"/>
And EmployeePDFView will have code for generating pdf and writing pdf byte stream on Response object. This will resolve to rest url that will end with .pdf extension, and when you return MAV with "employees" id.

Springs dispatcher servet searching a jsp page instead of calling a controller method

Hi I am trying to authenticate the user but seems like its calling a jsp page instead of another controller mapping.
My dispatcher servlet is
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.user}" />
<property name="password" value="${database.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.beingjavaguys.domain.User</value>
<value>com.beingjavaguys.domain.Chat</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
<bean id="hibernateTransactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
My web.xml is
dispatcher
org.springframework.web.servlet.DispatcherServlet
1
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
and my controller is
#RequestMapping(value="/authenticate",method=RequestMethod.POST)
public ModelAndView getAuthenticateResult(#ModelAttribute("user") User user,
BindingResult result) {
if(userService.authenticate(user))
{
return new ModelAndView("/userList");
}
else{
return new ModelAndView("Login");
}
}
#RequestMapping(value="/userList", method=RequestMethod.GET)
public ModelAndView getUserList() {
Map<String, Object> model = new HashMap<String, Object>();
model.put("chat", userService.getChat());
return new ModelAndView("UserDetails", model);
}
I am calling authenticate.html from my login file using POST method but my problem is this error
HTTP Status 404 - /Spring-hibernate-integration-helloworld/WEB-INF/view/userList.jsp
type Status report
message /Spring-hibernate-integration-helloworld/WEB-INF/view/userList.html.jsp
description The requested resource is not available.
Why is it searching for the jsp file instead of redirecting it to a controller method?
But if i use redirect:/userList.html it works then.Whats the logic behind it?
If you return a string that is interpreted as a name of a view to render. The name of the view is passed no the a ViewResolver (in your case probably an InternalResourceViewResolver) which will generate an (internal) URL to forward to. In this case that will be a JSP.
Now the redirect: and forward: prefixes are 2 special cases. The redirect: will result in a client side redirect to that URL, which in turn will call your controller due to your configuration. A forward: is handled on the server side and not the client side.
To be exact. The logic behind it is:
org.springframework.web.servlet.view.UrlBasedViewResolver
/**
* Overridden to implement check for "redirect:" prefix.
* <p>Not possible in {#code loadView}, since overridden
* {#code loadView} versions in subclasses might rely on the
* superclass always creating instances of the required view class.
* #see #loadView
* #see #requiredViewClass
*/
#Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}

Mapping .jsp Spring

Today I'm working on a project in Java Spring, especially in CONTEXT-SERVLET.xml (context) where normally declare a bean to link a .jsp with a Java class or controller (mapping).
Traditional workflow is: a viewA.jsp is linked (mapping) to controller.java (controller) and this controller.java dispatches another viewB.jsp.
Can you link a viewA.jsp to another viewB.Jsp without going through a controller?
How do this in CONTEXT-SERVLET.xml?
You can use ParameterizableViewController to redirect a request to jsp file without visiting controller.
For example
1. Mapping /welcome.htm to welcomeController
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome.htm">welcomeController</prop>
</props>
</property>
</bean>
2. Mapping viewName property of welcomeController to WelcomePage
<bean name="welcomeController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="WelcomePage" />
</bean>
3. Defining view resolver
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
It will map /welcome.htm to /WEB-INF/pages/welcomePage.jsp.
Source for more details.
< mvc:view-controller path="/" view-name="home" />
This is a shortcut for defining a ParameterizableViewController that immediately forwards to a view when invoked. Use it in static cases when there is no Java controller logic to execute before the view generates the response.
see link http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-view-controller

Spring's Java Based configuration not working for me

I am new to Spring programming and currently struggling with Spring 3.1's Java Based Configuraion" I have created following Configuration class
#Configuration
#ImportResource("classpath:/resources/jdbc.properties")
public class AppConfig {
#Autowired
Environment env;
private #Value("${jdbc.url}")
String url;
private #Value("${jdbc.username}")
String username;
private #Value("${jdbc.password}")
String password;
#Bean
public DataSource dataSource() {
System.out.println("Creating data Source.");
return new DriverManagerDataSource(url, username, password);
}
#Bean
public SessionFactory sessionFactory () throws Exception {
return new AnnotationSessionFactoryBuilder().setDataSource(dataSource()).setPackagesToScan("com.argusoft.loginmodule.domain").buildSessionFactory();
}
}
now when I try to run the project I get following error.
OUTPUT
SEVERE: Exception while loading the app :
java.lang.IllegalStateException: ContainerBase.addChild: start:
org.apache.catalina.LifecycleException:
java.lang.IllegalArgumentException: javax.servlet.ServletException:
java.lang.NoClassDefFoundError:
org/springframework/core/env/EnvironmentCapable
stuck into it, Cant solve it..... I am following Spring Source Blog.
please also suggest some good tutorial in which Spring's latest Java based configuration is explained by easy to understand examples...
Thanks in advance,
From the perspective of the exception:
java.lang.NoClassDefFoundError: org/springframework/core/env/EnvironmentCapable
This question is equals to the question: Spring class EnvironmentCapable
So the correct answer might be:
I think that need use version 3.1.0 - in package
org.springframework.core-3.1.0.M2.jar this class presents.
given by user810430 here: original answer.
you can puth configuration like this
inside application context:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/configuration.properties</value>
</list>
</property>
</bean>
<import resource="db-config.xml" />
and
db-config.xml is:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${jdbc.driver.className}</value>
</property>
<property name="jdbcUrl">
<value>${jdbc.url}</value>
</property>
<property name="user">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" autowire="byName">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="packagesToScan" value="com.huawei.sa.album" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- uncomment this for first time run-->
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<tx:annotation-driven />
</beans>

Resources