Spring Server Error on unknown Accept header - spring

I have a Spring REST project that uses spring boot. I have declared jackson-dataformat-xml as a dependency in maven to support xml when the accept header is application/xml (and it natively accepts application/json).
The problem I have is when i set the accept header to anything other than those two (ex application/dsfas or text/html, I get a server error with the following exception:
23:36:04.368 [http-nio-8082-exec-5] WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Failure in #ExceptionHandler protected org.springframework.http.ResponseEntity<java.lang.Object> com.mergg.common.web.RestResponseEntityExceptionHandler.handleNotFound(java.lang.RuntimeException,org.springframework.web.context.request.WebRequest)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Any idea on how to fix so that I can either ignore the accept header and return json or tell the client it was a bad request?

public class JsonContentNegotiation implements ContentNegotiationStrategy {
#Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
System.out.println("This is your negotation Strategy");
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String headers = request.getHeader(HttpHeaders.ACCEPT);
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headers);
if(headers.indexOf(MediaType.APPLICATION_JSON_VALUE)==-1){
mediaTypes.add(new MediaType("application","json"));
}
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
}
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true"/>
<property name="defaultContentTypeStrategy">
<bean class="demo2.ContentNegotation.JsonContentNegotiation"/>
</property>
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
otherwise use this in the controller to tell the client this is wrong header
#ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
#ResponseBody
public String fix(Exception e){
System.out.println("do This");
return "Accept Header may be wrong";
}

Related

JasperReportsViewResolver and Spring MVC - Set filename

I need help with JasperReports and Spring MVC. I can export everything, but I can't set the filename in the output PDF/Excel that my software exports.
In my dispatcher-servlet I have this bean :
<!-- ViewResolver JasperReports -->
<bean id="jasperViewResolver" class="org.springframework.web.servlet.view.jasperreports.JasperReportsViewResolver">
<property name="prefix" value="classpath:/jasper/" />
<property name="reportDataKey" value="dataSource" />
<property name="suffix" value=".jrxml" />
<property name="viewNames" value="Report_*" />
<property name="viewClass">
<value>org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView</value>
</property>
<property name="order" value="1" />
</bean>
That is the ViewResolver provided by Spring MVC.
I have a function in my BaseController ( abstract controller extended by all the #Controller ) :
protected String exportReport(String reportName, String formatoReport, Model model, JRDataSource dataSource) {
model.addAttribute("dataSource", dataSource);
model.addAttribute("format", formatoReport);
return reportName;
}
So, what I do is simply returning this view name from all my #RequestMapping :
#RequestMapping(..something)
public String functionName(...something else) {
.. do some stuff
return exportReport("Report_docIngresso", EFormatoReport.XLS, model, jrDataSource);
}
This works. The export is perfect, but I didn't find the way to set the filename of the exported pdf/excel, that comes out like the latest part of the URL I called before exporting the report.
I already tried to set in the HttpServletResponse the content-disposition with the filename, but it didn't work.
Thanks a lot,
Marco
Try setting the Content-Disposition HTTP response header:
#RequestMapping(..something)
public String functionName(HttpServletResponse response, ...something else) {
.. do some stuff
String header = "inline; filename=myfile.xls";
response.setHeader("Content-Disposition", header);
return exportReport("Report_docIngresso", EFormatoReport.XLS, model, jrDataSource);
}
(Note I saw just now your concluding comment that you tried without success to set the content disposition header. Well.. I can only say it worked for me in a similar setup.)

Camel #Header annotation not working when using spring ProxyFactory

When I use the camel #Header annotation in an interface, and call the bean (that implements it) directly, the Header value is filled. But when I proxy it with the spring ProxyFactoryBean then the #Header annotated parameter is empty. Probably I'm doing something wrong or am missing part of the configuration.
public interface Foo {
public void execute(#Header("FooHeader") String headerValue);
}
public class FooImpl implements Foo {
public void execute(String headerValue) {
System.out.println(headerValue);
}
}
public class FooInterceptor implements org.aopalliance.intercept.MethodInterceptor {
public Object invoke(final MethodInvocation invocation) throws Throwable {
return invocation.proceed();
}
}
spring context:
<bean id="foo" class="FooImpl"/>
<bean id="fooInterceptor" class="FooInterceptor"/>
<bean id="fooProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="foo"/>
<property name="interfaces">
<list>
<value>Foo</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>fooInterceptor</value>
</list>
</property>
</bean>
The camel route DSL:
from("foo-queue").to("bean:foo?method=execute");
prints the value of the header, but the route:
from("foo-queue").to("bean:fooProxy?method=execute");
prints null. Seems that in my setup, camel does not "see" the header annotation that's on the interface, that the proxy implements.
So, how can I make camel see the annotation and inject the value of the header as an argument to the method?
ProxyFactoryBean is a spring and not Camel, and as such it does not know about those Camel annotations.
Instead see here how to use Camel Proxy in Spring XML files: http://camel.apache.org/using-camelproxy.html

Handling error 500 by Interceptor in Spring MVC

I have created my Interceptor class and have overridden only the afterCompletion method.
When ever the system throws an error ( 500, 404 ), the system breaks and does not reach afterCompletion method of my interceptor. Is there any way so that the request can reach the afterCompletion method with the exception occured.
Interceptor class
public class ClassName extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
//do somthing here
}
}
}
interceptor declaration
<mvc:interceptors>
<bean class="x.y.z.ClassName" />
</mvc:interceptors>
Can anyone guide me one this.
I am using SWF + MVC and realized different ways of registering interceptors ,
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="flowRegistry" ref="DataCollectorFlowRegistry" />
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor"/>
</list>
</property>
</bean>
This works for me.

Spring MVC #PathVariable with dot (.) is getting truncated

This is continuation of question
Spring MVC #PathVariable getting truncated
Spring forum states that it has fixed(3.2 version) as part of ContentNegotiationManager. see the below link.
https://jira.springsource.org/browse/SPR-6164
https://jira.springsource.org/browse/SPR-7632
In my application requestParameter with .com is truncated.
Could anyone explain me how to use this new feature? how is it configurable at xml?
Note: spring forum- #1
Spring MVC #PathVariable with dot (.) is getting truncated
As far as i know this issue appears only for the pathvariable at the end of the requestmapping.
We were able to solve that by defining the regex addon in the requestmapping.
/somepath/{variable:.+}
Spring considers that anything behind the last dot is a file extension such as .jsonor .xml and trucate it to retrieve your parameter.
So if you have /somepath/{variable} :
/somepath/param, /somepath/param.json, /somepath/param.xml or /somepath/param.anything will result in a param with value param
/somepath/param.value.json, /somepath/param.value.xml or /somepath/param.value.anything will result in a param with value param.value
if you change your mapping to /somepath/{variable:.+} as suggested, any dot, including the last one will be consider as part of your parameter :
/somepath/param will result in a param with value param
/somepath/param.json will result in a param with value param.json
/somepath/param.xml will result in a param with value param.xml
/somepath/param.anything will result in a param with value param.anything
/somepath/param.value.json will result in a param with value param.value.json
...
If you don't care of extension recognition, you can disable it by overriding mvc:annotation-driven automagic :
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="useSuffixPatternMatch" value="false"/>
</bean>
So, again, if you have /somepath/{variable} :
/somepath/param, /somepath/param.json, /somepath/param.xml or /somepath/param.anything will result in a param with value param
/somepath/param.value.json, /somepath/param.value.xml or /somepath/param.value.anything will result in a param with value param.value
note : the difference from the default config is visible only if you have a mapping like somepath/something.{variable}. see Resthub project issue
if you want to keep extension management, since Spring 3.2 you can also set the useRegisteredSuffixPatternMatch property of RequestMappingHandlerMapping bean in order to keep suffixPattern recognition activated but limited to registered extension.
Here you define only json and xml extensions :
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="useRegisteredSuffixPatternMatch" value="true"/>
</bean>
<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>
Note that mvc:annotation-driven accepts now a contentNegotiation option to provide a custom bean but the property of RequestMappingHandlerMapping has to be changed to true (default false) (cf. https://jira.springsource.org/browse/SPR-7632).
For that reason, you still have to override the all mvc:annotation-driven configuration. I opened a ticket to Spring to ask for a custom RequestMappingHandlerMapping : https://jira.springsource.org/browse/SPR-11253. Please vote if you are intereted in.
While overriding, be carreful to consider also custom Execution management overriding. Otherwise, all your custom Exception mappings will fail. You will have to reuse messageCoverters with a list bean :
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<util:list id="messageConverters">
<bean class="your.custom.message.converter.IfAny"></bean>
<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>
</util:list>
<bean name="exceptionHandlerExceptionResolver"
class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
<property name="order" value="0"/>
<property name="messageConverters" ref="messageConverters"/>
</bean>
<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 name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters" ref="messageConverters"/>
</bean>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
I implemented, in the open source project Resthub that I am part of, a set of tests on these subjects : see https://github.com/resthub/resthub-spring-stack/pull/219/files & https://github.com/resthub/resthub-spring-stack/issues/217
Update for Spring 4: since 4.0.1 you can use PathMatchConfigurer (via your WebMvcConfigurer), e.g.
#Configuration
protected static class AllResources extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseRegisteredSuffixPatternMatch(true);
}
}
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}
In xml, it would be (https://jira.spring.io/browse/SPR-10163):
<mvc:annotation-driven>
[...]
<mvc:path-matching registered-suffixes-only="true"/>
</mvc:annotation-driven>
In addition to Martin Frey's answer, this can also be fixed by adding a trailing slash in the RequestMapping value:
/path/{variable}/
Keep in mind that this fix does not support maintainability. It now requires all URI's to have a trailing slash - something that may not be apparent to API users / new developers. Because it's likely not all parameters may have a . in them, it may also create intermittent bugs
In Spring Boot Rest Controller, I have resolved these by following Steps:
RestController :
#GetMapping("/statusByEmail/{email:.+}/")
public String statusByEmail(#PathVariable(value = "email") String email){
//code
}
And From Rest Client:
Get http://mywebhook.com/statusByEmail/abc.test#gmail.com/
adding the ":.+" worked for me, but not until I removed outer curly brackets.
value = {"/username/{id:.+}"} didn't work
value = "/username/{id:.+}" works
Hope I helped someone :)
/somepath/{variable:.+} works in Java requestMapping tag.
Here's an approach that relies purely on java configuration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
#Configuration
public class MvcConfig extends WebMvcConfigurationSupport{
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
handlerMapping.setUseSuffixPatternMatch(false);
handlerMapping.setUseTrailingSlashMatch(false);
return handlerMapping;
}
}
One pretty easy way to work around this issue is to append a trailing slash ...
e.g.:
use :
/somepath/filename.jpg/
instead of:
/somepath/filename.jpg
In Spring Boot, The Regular expression solve the problem like
#GetMapping("/path/{param1:.+}")
The complete solution including email addresses in path names for spring 4.2 is
<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>
<mvc:annotation-driven
content-negotiation-manager="contentNegotiationManager">
<mvc:path-matching suffix-pattern="false" registered-suffixes-only="true" />
</mvc:annotation-driven>
Add this to the application-xml
If you are using Spring 3.2.x and <mvc:annotation-driven />, create this little BeanPostProcessor:
package spring;
public final class DoNotTruncateMyUrls implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping)bean).setUseSuffixPatternMatch(false);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
Then put this in your MVC config xml:
<bean class="spring.DoNotTruncateMyUrls" />
Finally I found solution in Spring Docs:
To completely disable the use of file extensions, you must set both of the following:
useSuffixPatternMatching(false), see PathMatchConfigurer
favorPathExtension(false), see ContentNegotiationConfigurer
Adding this to my WebMvcConfigurerAdapter implementation solved the problem:
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
#Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseSuffixPatternMatch(false);
}
For me the
#GetMapping(path = "/a/{variableName:.+}")
does work but only if you also encode the "dot" in your request url as "%2E" then it works. But requires URL's to all be that...which is not a "standard" encoding, though valid. Feels like something of a bug :|
The other work around, similar to the "trailing slash" way is to move the variable that will have the dot "inline" ex:
#GetMapping(path = "/{variableName}/a")
now all dots will be preserved, no modifications needed.
If you write both back and frontend, another simple solution is to attach a "/" at the end of the URL at front. If so, you don't need to change your backend...
somepath/myemail#gmail.com/
Be happy!
As of Spring 5.2.4 (Spring Boot v2.2.6.RELEASE)
PathMatchConfigurer.setUseSuffixPatternMatch and ContentNegotiationConfigurer.favorPathExtension have been deprecated ( https://spring.io/blog/2020/03/24/spring-framework-5-2-5-available-now and https://github.com/spring-projects/spring-framework/issues/24179).
The real problem is that the client requests a specific media type (like .com) and Spring added all those media types by default. In most cases your REST controller will only produce JSON so it will not support the requested output format (.com).
To overcome this issue you should be all good by updating your rest controller (or specific method) to support the 'ouput' format (#RequestMapping(produces = MediaType.ALL_VALUE)) and of course allow characters like a dot ({username:.+}).
Example:
#RequestMapping(value = USERNAME, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class UsernameAPI {
private final UsernameService service;
#GetMapping(value = "/{username:.+}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.ALL_VALUE)
public ResponseEntity isUsernameAlreadyInUse(#PathVariable(value = "username") #Valid #Size(max = 255) String username) {
log.debug("Check if username already exists");
if (service.doesUsernameExist(username)) {
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
return ResponseEntity.notFound().build();
}
}
Spring 5.3 and above will only match registered suffixes (media types).
If you are using Spring 3.2+ then below solution will help. This will handle all urls so definitely better than applying regex pattern in the request URI mapping to allow . like /somepath/{variable:.+}
Define a bean in the xml file
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="useSuffixPatternMatch" value="false"/>
<property name="useRegisteredSuffixPatternMatch" value="true"/>
</bean>
The flags usage can be found on the documentation. I am putting snipped to explain
exlanation of useRegisteredSuffixPatternMatch is said to be resolving the issue. From the java doc in the class
If enabled, a controller method mapped to "/users" also matches to
"/users.json" assuming ".json" is a file extension registered with the
provided {#link #setContentNegotiationManager(ContentNegotiationManager)
contentNegotiationManager}. This can be useful for allowing only specific
URL extensions to be used as well as in cases where a "." in the URL path
can lead to ambiguous interpretation of path variable content, (e.g. given
"/users/{user}" and incoming URLs such as "/users/john.j.joe" and
"/users/john.j.joe.json").
Simple Solution Fix: adding a regex {q:.+} in the #RequestMapping
#RequestMapping("medici/james/Site")
public class WebSiteController {
#RequestMapping(value = "/{site:.+}", method = RequestMethod.GET)
public ModelAndView display(#PathVariable("site") String site) {
return getModelAndView(site, "web site");
}
}
Now, for input /site/jamesmedice.com, “site” will display the correct james'site

Spring MVC multipart response

I'm trying to write full multipart flow, from the client side sending multipart request using Spring restTemplate and from the server side auto resolve the different parts to objects (I'm using JAXB for objects marshaling) and send a response back in multipart.
I'm was able to implement almost all the flow but I'm unable to send a multipart response with jaxb objects from spring controller.
Here is the contorller code:
#RequestMapping(value="/putuser",method=RequestMethod.POST)
#ResponseBody
public MultiValueMap<String, Object> getUser(#RequestBody User user) throws IOException, JAXBException {
}
user.setName("new");
MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
form.add("user", user);
form.add("file", new FileSystemResource("/tmp/1.1"));
return form;
}
This is the exception that I'm getting in the server side:
java.lang.ClassCastException: org.springframework.core.io.FileSystemResource cannot be cast to java.lang.String
at org.springframework.http.converter.FormHttpMessageConverter.writeForm(FormHttpMessageConverter.java:233)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:197)
at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:73)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:148)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:90)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:189)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:69)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
It looks like Spring is trying to convert each part in the response to String instead of the correct content type (file/xml etc/)
I tried to update my spring.xml file like this:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
<property name="partConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
To try making he FormHttpMessageConverter to write the parts correctly but this did not helped
Is there any way making spring to send multipart responses correctly with different types of parts?
You will need to set Content-Type header of your response to appropriate value. The FormHttpMessageConverter relies on Content-Type to identify if it needs to cast the response to String or some other type.
#RequestMapping(value="/putuser",method=RequestMethod.POST)
#ResponseBody
public MultiValueMap<String, Object> getUser(#RequestBody User user, HttpServletResponse httpResponse) throws IOException, JAXBException {
}
user.setName("new");
MultiValueMap<String, Object> form = new LinkedMultiValueMap<String, Object>();
form.add("user", user);
form.add("file", new FileSystemResource("/tmp/1.1"));
httpResponse.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE); // <-- IMPORTANT
return form;
}

Resources