spring mvc #pathvariable not working with url file extension - java

i am facing an issue while invoking spring mvc method which has pathvariable
#RequestMapping(value="/retrieveData/{userId}", method=RequestMethod.POST)
public #ResponseBody
ModelAndView retrieveData(#PathVariable Long userId , HttpSession session) {
i have web.xml servlet mapping as below
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
in my jsp , i have my javascript
document.getElementById("form1").action = "retrieveData.html/2";
document.getElementById("form1").submit();
so my question is, will path variable works with url file extension in my case i have *.html , which i eventually want my servlet to map /retrieveData/{userId}
while submitting the page i am getting HTTP Status 400 error. help needed
EDIT:- i have ContentNegotiationManagerFactoryBean configure in my spring context
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="true" />
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>

Related

Spring: servlet-mapping -> url-pattern : /* working but can't display

web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
/WEB-INF/spring/webmvc-config.xml
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml" />
<entry key="html" value="text/html" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Controller
#Controller
#RequestMapping ( "/" )
public class IndexController extends BaseController
{
#RequestMapping ( "/" )
public String index ( Model model ){
System.out.println("AA");
return index2(model);
}
#RequestMapping ( "/index" )
public String index2 ( Model model ){
System.out.println("BB");
return "index";
}
}
And exist index.jsp File
I guess that is very good working
BBBBBBBBBBBUUUUUUUUUTTTTTTTTT, BUT!
WHY????
WHY????
WHY????
WHY????
And More strange
??????????????????????????????????????????????????????????????????
Controller work it!! but don't display browser
What's going on?
Please help me.
And Log
DispatcherServlet with name 'dispatcher' processing GET request for [/WEB-INF/views/index.jsp]
No mapping found for HTTP request with URI [/WEB-INF/views/index.jsp] in DispatcherServlet with name 'dispatcher'
Servlet containers have rules for how they map and handle URI requests. These can be found in the Servlet Specification. It's also important to note that most Servlet containers have a Servlet to handle JSPs, mapped to *.jsp, which is an extension mapping. Tomcat has a JspServlet to do this.
You've mapped your DispatcherServlet to
<url-pattern>/*</url-pattern>
which is a path mapping. Path mappings take precedence over extension mappings. So when you submit your view name
return "index";
Spring will use the ViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
to resolve a path to use with a RequestDispatcher's forward method. That path will be /WEB-INF/views/index.jsp. Now the Servlet container will receive that path and attempt to find a Servlet to handle it. Since you have a Servlet mapped to /* it will use it, but your DispatcherServlet doesn't have a mapping for that path and therefore responds with a 404.
The simple solution is to change your mapping to /, which is the default handler if no other matches are found. In this case, when you submit your view and the container must find a mapped Servlet, it will find the JspServlet and use it.

Spring MVC - Multipart file upload with Ajax (Could not parse multipart servlet request)

I'm trying to upload a multipart file using Ajax, Spring MVC 3.2.0, Tomcat 8.0.9, but can't get it work. I read a lot of blogs and similar posting here on stackoverflow (Spring upload file problems, MultipartConfig with Servlet 3.0 on Spring MVC, …) which seem to have similar causes but couldn't figure out how to solve it. The weird thing is that the upload works when the file is smaller than 1MB, but when ever the recorded video exceeds that size, the following error is raised:
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. null
org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:163)
org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:110)
In the following you can see all the configurations I made:
The AJAX POST-Request:
var videoBlob = e.data;
var pathArray = window.location.pathname.split( '/' );
var userID;
for (i = 0; i < pathArray.length; i++) {
if (pathArray[i].toString() == "edit"){
userID = pathArray[i+1];
}
}
var fd = new FormData();
fd.append('fname', 'video');
fd.append('data', videoBlob);
$.ajax({
url: '/user/edit/uploadVideo/' + userID,
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data)
{
$('#result').html(data + "uploaded by FormData!");
}
});
The web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:root-context.xml</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>common</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<display-name>springMultipartFilter</display-name>
<filter-name>springMultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springMultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The servlet-context.xml
<mvc:annotation-driven />
<mvc:resources mapping="/**" location="/resources/" />
<context:component-scan base-package="de.talentwuerfel"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/schema"/>
<property name="username" value="root"/>
<property name="password" value=""/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="packagesToScan">
<array>
<value>de.talentwuerfel</value>
</array>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
The root-context.xml where I defined the MultipartResolver
<bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000"/>
<property name="maxInMemorySize" value="4096"/>
</bean>
The Java-Controller
#RequestMapping(value = "/edit/uploadVideo/{id}", method = RequestMethod.POST)
public #ResponseBody String uploadVideo(#PathVariable long id, MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
//.... file handling
}
How can I solve this problem?
EDIT:
I tried the suggested approach and used the Servlet implementation to manage my video-file upload. The following adjustments have been made, but it's still resulting in a similar error:
Adjusted #Controller:
#RequestMapping(value = "/edit/uploadVideo/{id}", method = RequestMethod.POST)
public String uploadVideo(#PathVariable long id, #RequestParam("data") Part file) {
//...
}
The root-controller has been deleted and I added the multipartResolver to the servlet-context.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>
The tag was in the web.xml has been extended by the following Multipart-Configuration:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>/tmp</location>
<max-file-size>20848820</max-file-size>
<max-request-size>418018841</max-request-size>
<file-size-threshold>1048576</file-size-threshold>
</multipart-config>
</servlet>
However, I'm still getting an exception and can't upload a blob file larger than 1MB:
Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. null
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:927)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
I implemented a similar file upload where a single file was simply picked and it totally worked to send large files while using the same configuration. So I believe it has rather something to do with the Ajax POST or the attached blob file?!
you can add this in you application to active Servlet3.0 MultiParsing:
#Bean
public MultipartConfigElement multipartConfigElement() {
MultiPartConfigFactory factory = new MultiPartConfigFactory();
factory.setMaxFileSize("128KB");
factory.setMaxRequestSize("128KB");
return factory.createMultipartConfig();
}
or do it in XML.
Not really an answer to your exact question, just my 2 cents. There are basically 2 ways of uploading files with the help of Spring MVC :
using Jakarta Commons FileUpload, the only way (aside from implementing one yourself) before the appearance of the servlet 3.0 API
using the Servlet implementation of your server (only if servlet impl version >= 3.0)
Since you are using Tomcat 8.0.9, the Servet 3.0 option is available to you which I definitely recommend since it doesn't introduce yet another external dependency in your project. Also, since it follows the Servlet 3.0 spec, the configuration of such upload mechanism is now java standard which is nice in case you decide to move from Spring MVC to another MVC framework (your configuration values would remain the same).
In case you can't figure out your IOFileUploadException, I think you should give it a try.

Spring invokes wrong controller mapping

I'm building very basic mvc application with Spring. It has one controller that should invoke validation on request body. The problem is that if I define the mapping in web.xml it stops finding the right controller, but when I modify the servlet application context Spring star making some new bindings on the fly but this time annotation based validation is ignored. How can I controll mappings in web.xml while still invoking annotation based validation?
Here are the details:
The controller:
#Controller
#RequestMapping("/api")
public class UserActionsController {
#RequestMapping(value="/choice", method = RequestMethod.POST)
public #ResponseBody NameValue addUserChoice(#Valid #RequestBody NameValue action)
{
return action;
}
}
This is the servlet application context:
<mvc:annotation-driven/>
<context:component-scan base-package="com.my.package" />
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultContentType" value="application/json" />
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Web xml:
<servlet>
<servlet-name>action-api</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action-api</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
The configuration above is working. The problem starts when I try to change web.xml so the controller will only be responsible for "/api/*". I change it to <url-pattern>/api/*</url-pattern>. In that case Spring cannot find the right controller.
(DispatcherServlet:819) - DispatcherServlet with name 'action-api' processing POST request for [/api/choice]
(RequestMappingHandlerMapping:209) - Looking up handler method for path /choice
(RequestMappingHandlerMapping:219) - Did not find handler method for [/choice]
(PageNotFound:1080) - No mapping found for HTTP request with URI [/api/choice] in DispatcherServlet with name 'action-api'
(DispatcherServlet:913) - Successfully completed request
Modifying the servlet application context helps, Spring now able to find the controller, but validation is not invoked anymore.
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="alwaysUseFullPath" value="true" />
<property name="messageConverters">
<util:list id="beanList">
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</util:list>
</property>
</bean>
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
</bean>
Here is the log:
(DispatcherServlet:819) - DispatcherServlet with name 'action-api' processing POST request for [/api/choice]
(RequestMappingHandlerMapping:209) - Looking up handler method for path /choice
(RequestMappingHandlerMapping:219) - Did not find handler method for [/choice]
(DefaultAnnotationHandlerMapping:124) - Mapping [/api/choice] to HandlerExecutionChain with handler [com.my.package.controller.UserActionsController#1f86dbd] and 1 interceptor
(HandlerMethodInvoker:638) - Reading [com.my.package.model.NameValue] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter#2059ef]
(HandlerMethodInvoker:173) - Invoking request handler method: public com.my.package.model.NameValue com.citypath.dima.controller.UserActionsController.addUserChoice(com.my.package.model.NameValue)
(AnnotationMethodHandlerAdapter:1037) - Written [com.my.package.model.NameValue#166685b] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter#2059ef]
(DispatcherServlet:957) - Null ModelAndView returned to DispatcherServlet with name 'action-api': assuming HandlerAdapter completed request handling
(DispatcherServlet:913) - Successfully completed request
Looks like Spring making some binding on the fly, but this time validators are ignored.
I need to have 2 controllers for, say '/api' and '/something'. How can I define it in web.xml so Spring will be able to locate them and to invoke validation?
Thanks.
Spring #Controllers URLs are always interpreted relative the the Spring Dispatcher Servlet that handles them. So if you map the dispatcher servlet to /api/* in web.xml then the URL to your controller above is /api/api/choice
So you can configure two spring dispatcher servelts in web.xml one mapped to /api in the web.xml and one mapped to /somethingelse in web.xml then you can just remove /api from the #RequestMappings
In my app I am using a single Dispatcher Servlet for api and UI and I use public static final String called URL in my various API controllers to build up the paths to the various resources exposed by the API. Below is an example from my API.
#Controller
#RequestMapping(CompanyPermissionsResourceController.PATH)
public class CompanyPermissionsResourceController extends BaseApiController
{
public static final String PATH = CompanyResourceController.PATH + "/permissions";
Are you sure you are making post request to /api/choice?
#RequestMapping(value="/choice", method = RequestMethod.POST)
Try Changing to
#RequestMapping(value="/choice", method = RequestMethod.GET)
public #ResponseBody NameValue addUserChoice(#Valid #RequestBody NameValue action)
{
return action;
}
I don't think it works like this. Put all the path /api/choice at method level.

Spring message bundle issue

I've got a spring-security setup in my web-app. I want to replace some messages of spring security with my custom messages i.e.
Instead of Bad Credentials I want AbstractUserDetailsAuthenticationProvider.badCredentials to have value invalid username or password, please try again
This is my spring setup :
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages/messages"/>
<!-- Tried this as well <property name="basename" value="/WEB-INF/messages/messages.properties"/> -->
<property name="cacheSeconds" value="0" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
And I have created folder in the WEB-INF called messages. Inside this folder there is file called messages.properties. Inside this file is one line :
AbstractUserDetailsAuthenticationProvider.badCredentials=invalid username or password, please try again
What am I doing wrong here?
Update :
This servlet-context is defined in the web xml :
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Problem solved, moved the messageSource bean definition to another context.
Everything looks right. Try to get the message using spring message tag in order to check if the problem is with your configuration or with the security message:
<spring:message code="AbstractUserDetailsAuthenticationProvider.badCredentials" />
If this does work, problem is not with your configuration (that looks fine for me).
Anyway, try to put your messages files in the classpath (source folder) and this:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages/messages"/>
<property name="cacheSeconds" value="0" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
Also, check that the bean is being declared in a application context file that Spring is aware of.

How to browse htm file in Spring MVC

I am using default configuration of Spring MVC 2. So, my dispatcher will look like,
<prop key="index.htm">indexController</prop>
I have a simple htm file on root. When I browse this file I get 404:Not Found Error. How to browse (which exist physically on disk) htm file in Spring MVC.
In annotation based Spring 3.x, you can just write it in your controller as,
#RequestMaping(value="/index.htm")
public void doSomeJob(){
//some code here
}
and if you make a request as "/pathToIt/index.htm", then it will be caught by doSomeJob() method..
P.S No need for request mappings in configuration files in Spring MVC 3.x
Note: And also 404 can be caused if spring can't find your physical file..
Try and add a bean id in your springmvc-servlet.xml file :
<bean id="indexController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
then :
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
...
<prop key="/index.htm">indexController</prop>
</props>
</property>
</bean>
Ok this sounds familiar , if you are using MVC 2 then the best way to achieve this is to give a specific mapping for dispatcher servlet instead of /
<servlet>
<servlet-name>myDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myDispatcherServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
This will make sure the only requests to *.jsp will go dispatcher servet and rest will be handled by the container itself.
You need to map URL index.htm with your controller. You cannot view your page until it will have correct spring mvc configuration.
<bean name="/index.htm" class="com.indexController">
....
</bean>
In controller you will pass your jsp page name as view in modelAndView.
Check your view resolver has correct setting like this
<!-- View Resolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix">
<value>/WEB-INF/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

Categories