#RequestBody or #ModelAttribute with Spring+REST web services - spring

I am creating a Restful website and Web services for iPhone and android apps with Spring 3.1. In my application, i am using Spring Message Convertors (org.springframework.http.converter.json.MappingJacksonHttpMessageConverter) to converting JSON into Java object and vice-versa.
My objective is that there should be only single controller
method(same URL) that should be used by JSP page, Iphone/Andois app.
I am using Spring form tag for object binding from JSP to controller with the help of #ModelAttribute like below.
#RequestMapping(value = "reset-password", method = RequestMethod.POST)
public ModelAndView resetPassword(#ModelAttributeForgot forgotPassword,
HttpServletRequest request) {
System.out.println("data recived=="+forgotPassword.getNewPassword());
}
But the same is NOT working in the case if the data is being posted from iPhone/Android app and the result is:
data recived==null;
So to overcome this problem i have used #RequestBody annotation at place of #ModelAttribute.
So my controller looks like below:
#RequestMapping(value = "reset-password", method = RequestMethod.POST)
public ModelAndView resetPassword(#RequestBody Forgot forgotPassword,
HttpServletRequest request) {
System.out.println("data recived=="+forgotPassword.getNewPassword());
}
It works then and the result i got is:
data recived==somedata;
But #RequestBody then doesn't work with spring form on JSP page and the data doesn't get converted into object and i got null values.
Can't i use #RequestBody annotation to post data in form of JSON
with spring form tag from JSP page??
Is there any way by using which i can post data from my JSP form as
well as from I phone App by using only a single controller method(either #ModelAttribute or #RequestBody).
EDIT:
While writing String in place of Bean class, i am able to get the content in form of plain text, as below:
#RequestMapping(value = "reset-password", method = RequestMethod.POST)
public ModelAndView resetPassword(#RequestBody String string,
HttpServletRequest request) { }
Result from web page call:
uid=11&confirmPassword=somepassword&newPassword=somepassword
Result from iPhone using web service call(in **JSON)**
{"newPassword":"somepassword","confirmPassword":"somepassword","uid":"11"}
But problem is that using this approach i have to parse the JSON string into Java object manually. And in web page content i have to find the values manually that i don't want.
Please help.
Regards,
Arun Kumar

Sorry, but I don't believe there is a way, because #ModelAttribute is bound from form post parameters and #RequestBody passes the body straight to the Json converter. You could replace the spring form tag with a simple json post, but that is probably less convenient than having two #RequestMapping methods.

Its #RequestBody. I feel its better to specify the mime type that you are expecting and producing as output using #RequestMapping as,
#RequestMapping(value="/authenticate",produces="application/json",
consumes="application/json",method=RequestMethod.POST)
Then register appropriate message converters with AnnotationMethodHandlerAdapter
This message converter is responsible for Marshalling & unmarshalling of your request & response entity based on produces & consumes attributes.
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
<property name="order" value="1" />
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" >
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
</bean>
</list>
</property>
</bean>

Related

Spring MVC maven Controller called twice

I am new to Spring mvc, maven and tomcat. I have written a controller for which I have defined a bean. Now when I type the url in broswer like: localhost:8080/hello.htm my controller is called and it is called again when I hit this URL.
Eg
<bean name="/hello.htm" class="com.paytm.controller.InventoryController">
<property name="jdbcProductDao">
<bean class="com.paytm.repository.JdbcProductDao">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
</property>
</bean>
And in the controller
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Product product = new Product();
product.setPrice(23.45);
product.setDescription("Test Product");
this.jdbcProductDao.saveProduct(product);
...
}
As a result the product is saved twice in the DB. My query is why is the controller called when I type the URL(and not hit it)?
Your browser caches your url when it sees
the same url in the omnibox every now and then. Clear your cache and it will fix your issue. You can check your chrome prerenders by typing this in your chrome browser.
chrome://net-internals/#prerender
For more details your can take a look at this source

Add JSON message converter for multipart/form-data

In my Spring MVC server I want to receive a multipart/form-data request containing both a file (an image) and some JSON metadata.
I can build a well-formed multipart request where the JSON section has Content-Type=application/json.
The Spring service is in the form:
#RequestMapping(value = MY_URL, method=RequestMethod.POST, headers="Content-Type=multipart/form-data")
public void myMethod(#RequestParam("image") MultipartFile file, #RequestParam("json") MyClass myClass) {
...
}
The file is correctly uploaded, but I'm having problems with the JSON part. I get this error:
org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'myPackage.MyClass'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [myPackage.MyClass]: no matching editors or conversion strategy found
If I don't use multipart request JSON conversion works well using Jackson 2, but when using multipart I get the previous error. I think I have to configure the multipart message converter to support JSON as part of the message, but I don't know how. Here is my configuration:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
All works well if I use String as type of myClass instead of MyClass, but I want to use the Spring MVC support for parameter conversion.
If you use the #RequestPart annotation instead of #RequestParam, it will actually pass the parameters through the message converters.
So, if you change your controller method to the following, it should work as you describe:
#RequestMapping(value = MY_URL, method=RequestMethod.POST, headers="Content-Type=multipart/form-data")
public void myMethod(#RequestParam("image") MultipartFile file, #RequestPart("json") MyClass myClass) {
...
}
You can read more about it in the Spring reference guide: http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/mvc.html#mvc-multipart-forms-non-browsers
i don't have any idea how do it this but i know #RequestParam("json") MyClass myClass u can change to #RequestParam("json") String myClass and build object class by JSON converted! It's not good but it's works

getting stack overflow during return page from spring mvc controller

I have spring mvc controller, contain method like
#requestmapping("jsps/welcome.jsp")
public String handleRequest(){
system.out.println("inside the handler");
return "welcome";}
when I am trying to run the welcome.jsp page, I am getting stack overflow, it seems like the page is going to the controller then the controller return the page then it will go again and so on.
my configuration for spring is
<bean id="viewResolver"
class="------"
<property name="prefix">
<value> /jsps/ </value>
</property>
<property name="suffix">
<value> .jsp </value>
</property>
and the urlmapping
<map>
<entry key="jsps/welcome.jsp">
<ref bean="mycontroller"/>
Two things
For URL mapping, better not to use *.jsp. Use something like jsps/welcome or jsps/welcome.htm
To return a view use ModelAndVeiw.
UPDATE
Make these changes
#requestmapping("jsps/welcome")
public String handleRequest(){
system.out.println("inside the handler");
return new ModelAndView("yourpathtojspfile");} // can be "jsps/welcome"
Here, this page will return the view (with 'jsps/welcome' you will get welcome.jsp)
When you return a string, the option you have is to use
return "redirect:someUrl"; //But this will not return a view, instead search for the mapping someUrl.
Yes, Controller is returning string "welcome" and view resolver is modifying it to "jsps/welcome.jsp" this is again calling the method.
change #requestmapping("jsps/welcome.jsp") to something other. Usually we have welcome.htm something like this
and then hit welcome.htm it will call the handleRequest method, method will return "welcome" and view resolver coverts it to "jsps/welcome.jsp".
Reason is obvious. A request to jsps/welcome.jsp calls the controller method, which forwards to a view named "welcome", which is resolved to url jsps/welcome.jsp again, calling the method again. So it goes on untill StackOverFlow is thrown.
Solution is to change url mapping to another url without .jsp like
#Requestmapping("/welcome")
public String handleRequest(){
system.out.println("inside the handler");
return "welcome";
}

Adding custom RequestCondition's in Spring mvc 3.1

I have a Spring mvc (3.1.1) app, and I want to define conditions beyond what's available in RequestMapping. I have a couple of things I want to use it for.
First, it would be nice if I could show a different home page for different user types:
#Controller
public class HomepageController {
#RequestMapping(value = "/")
#CustomCondition(roles = Guest.class)
public String guestHome() { /*...*/ }
#RequestMapping(value = "/")
#CustomCondition(roles = Admin.class)
public String adminHome() { /*...*/ }
}
Second, I want the app to function both as a web site and as a REST service (e.g. for mobile apps), so I'd want to let the website access both html and json actions, and let the service (different subdomain) only access json actions (some kind of #CustomCondition(web = true) which only matches website urls)
Can this work for any of the two uses I'm planning?
I found very little documentation about custom conditions, but I did find one example that implements custom conditions which might be what I want, but it uses a #Configuration class instead of the XML configuration which I'm using and I don't want to move my entire spring xml definitions to a #Configuration class.
Can I define a customMethodCondition for RequestMappingHandlerMapping in the XML?
I tried subclassing RequestMappingHandlerMapping and override getCustomMethodCondition, to return my custom RequestCondition, but it didn't work - getMatchingCondition() in my condition didn't fire.
Any help would be greatly appreciated!
UPDATE
I read a little more, and it looks like RequestMappingHandlerMapping is a new class (since ver 3.1).
What happens in my app is that the #Configuration that tries to override and thereby redefine the requestMappingHandlerMapping bean actually works, but the url mappings (#RequestMapping methods in #Controllers) seem to get processed twice, once by the subclass ExtendedRequestMappingHandlerMapping and once by the original RequestMappingHandlerMapping --first with a custom condition, and then again without it.
Bottom line is my custom conditions are simply ignored.
This is supposed to be an advanced pattern, but IMO it should be quite common...
Comments anyone?
Spring MVC already provides a mechanism for distinguishing between json and html, the RequestMapping annotation takes a consumes attribute which looks at the content type of the request...
// REST version, Content-type is "application/json"
#RequestMapping(value = "/", consumes = "application/json")
public void myRestService() {
...
// HTML version, Content-type is not "application/json"
#RequestMapping(value = "/", consumes = "!application/json")
public void myHtmlService() {
...
Another way to use the same url but have distinct methods is with the param or headers attribute...
// the url is /?role=guest
#RequestMapping(value = "/", param = "role=guest")
public void guestService() {
// the url is / with header role=admin
#RequestMapping(value = "/", headers = "role=admin")
public void adminService() {
I would think you would want distinct urls for security. Typically, with something like Spring Security, you would put all of the admin functionality under /admin and let the framework manage it all...
<http auto-config="true">
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
...
Would this be sufficient for your use case(s)?
If you have extended RequestMappingHandlerMapping(say ExtendedRequestMappingHandlerMapping) you have to register this new mapping a little differently in application context xml.
You cannot use <mvc:annotation-driven/> to configure the Spring MVC as that defines it's own handlerMapping internally, you can instead do something along these lines(or follow the approach in the link with #Configuration that you have provided):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="..ExtendedRequestMappingHandlerMapping">
</bean>
This should ensure that your mapping takes effect and will ensure that the appropriate handler method is found by the handlerAdapter component.

Spring MVC: a better way to get localized messages from within controller method?

I have a Spring controller it needs to set a message in the request scope and sends the user back to a form because of errors. Here is the method signature:
public String update(HttpServletRequest request,
Model model,
#ModelAttribute("command") User user,
BindingResult result, SessionStatus status)
In the method the request object is available, here is my way of setting a message in the request scope, which I feel is convoluted.
.....
WebApplicationContext ctx = RequestContextUtils.getWebApplicationContext(reque st);
Locale locale = RequestContextUtils.getLocale(request);
request.setAttribute("formError", ctx.getMessage("errors.unique.value", new Object[]{new DefaultMessageSourceResolvable(new String[]{"label.userName"})}, locale));
.......
Here are my questions:
Is the above way correct for setting a message?
Is any better or simpler way?
Thanks for help!
Are you trying to give a feedback to the user? Then you should have a look at result.rejectValue(field, property)
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/validation/Errors.html#rejectValue%28java.lang.String,%20java.lang.String%29
Example:
if the field on which the validation didnt passed is called "new_password" and the language property is named "new_passwort_invalid" then you could handle it like this:
result.rejectValue("new_password", "new_passwort_invalid");
greets
(Even if the post is old, I hope this can be useful for others)
In servlet-context.xml configuration we declare the bean:
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="/WEB-INF/locale/messages" />
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
Then in controller we autowire the messageSource:
#Controller
public class SomeCtrl {
#Autowired
private MessageSource messageSource;
...
Finally in the action we can use the locale (by adding the locale formal parameter and using the message):
#RequestMapping(value="/doLogin", method=RequestMethod.POST)
#ResponseBody
public String tryLogin(
#ModelAttribute("loginBean") LoginBean loginBean,
BindingResult result, SessionStatus status,
Locale locale) {
....
....
String generalErrorMsg = messageSource.getMessage("login.generalError",new Object[] {}, locale);
....
....

Resources