Intercept null ResponseBody before marshalling response - java

I've got multiple controllers for RESTful endpoints which currently return null if there's no resource at the endpoint. For instance,
#RequestMapping(method = ReqeustMethod.GET, value = "{id}")
#ResponseBody
public MyResource get(#PathVariable final Long id) {
return this.myService.get(id); // returns null if bad id
}
I want to return a specific, different resource to the client (ErrorResource) when there's no MyResource with the given id. I know I can do that with a separate method with #ExceptionHandler, such as:
#RequestMapping(method = RequestMethod.GET, value = "{id}")
#ResponseBody
public MyResource get(#PathVariable final Long id) {
final MyResource myResource = this.myService.get(id);
if (myResource == null) {
throw new NotFoundException();
}
return myResource;
}
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorResource notFoundException(
final HttpServletRequest request,
final NotFoundException exception) {
final ErrorResource errorResource = new ErrorResource();
errorResource.setStatus(HttpStatus.NOT_FOUND.value());
errorResource.setDeveloperMessage("No resource found at " + request.getRequestURL());
return errorResource;
}
And that's nice. But what I'd really like to be able to do is have some kind of interceptor that figures out for me that whenever an API method is returning a null #ResponseBody, it should instead run the logic in my notFoundException() method. That will make all my controller methods a little cleaner. Is there any way to do that?

It sounds like a job for Spring's HttpMessageConverter.
You can write your own converter by implementing HttpMessageConverter<T> interface.
In your case I would implement a converter of HttpMessageConverter<MyResource> with a null check on the MyResource instance in the write method. If the MyResource instance is null, then build and write your ErrorResource instance.
Here is an example:
import java.io.IOException;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
public class MyResourceConverter implements HttpMessageConverter<MyResource> {
// a real message converter that will respond to ancillary methods and do the actual work
private HttpMessageConverter<Object> delegateConverter;
public MyResourceConverter(HttpMessageConverter<Object> delegateConverter){
this.delegateConverter = delegateConverter;
}
#Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canRead(clazz, mediaType) && MyResource.class.equals(clazz);
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canWrite(clazz, mediaType) && MyResource.class.equals(clazz);
}
#Override
public MyResource read(Class<? extends MyResource> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
return (MyResource) delegateConverter.read(clazz, inputMessage);
}
#Override
public void write(MyResource t, MediaType contentType,
HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
Object result = null;
if(t == null){
result = // build your ErrorResource here
}else{
result = t;
}
delegateConverter.write(result, contentType, outputMessage);
}
}
Note that this converter needs to be registered in your Spring configuration.
The configuration class must extend WebMvcConfigurerAdapter and override the configureMessageConverters method, like:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// Here we add our custom-configured HttpMessageConverters.
// yourDelegateConverter may be a MappingJackson2HttpMessageConverter instance for example
converters.add(new EmployeeConverter(yourDelegateConverter));
super.configureMessageConverters(converters);
}
References (from official Spring documentation):
HTTP Message conversion
HttpMessageConverter
WebMvcConfigurerAdapter

Related

Parse RequestBody as two different objects in Spring boot

In my Spring Boot application (2.5.5) I get a large JSON body in the POST request to a specific endpoint. On that request I need to get both the parsed object and that whole object as a string to do some validation. The JSON object contains a lot of information that I don't need so that is not included in the Object so I can't convert it to a string.
#RestController
#RequestMapping("/example")
public class ExampleController {
#PostMapping("")
public void example(
#RequestBody String stringBody,
#RequestBody ExampleRequest exampleRequest
) {
// Validate request with 'stringBody'
// Do things with 'exampleRequest'
}
}
The best idea I had so far is to just use #RequestBody String stringBody and then convert that string to a JSON object but that is really not the ideal solution.
I know that you can't have two #RequestBody but I really need to somehow have both.
I believe that a custom HandlerMethodArgumentResolver is your best option.
For that I suggest you create a custom annotation as follows:
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidJsonSignature { }
Now you need to implement the custom HandlerMethodArgumentResolver:
public class JsonSignatureValidationArgumentResolver implements HandlerMethodArgumentResolver {
private final ObjectMapper objectMapper;
public JsonSignatureValidationArgumentResolver(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(ValidJsonSignature.class) != null;
}
#Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest httpServletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
String jsonPayload = StreamUtils.copyToString(httpServletRequest.getInputStream(), StandardCharsets.UTF_8);
// Do actual validation here
if (// Valid) {
// If valid, then convert String to method parameter type and return it
return objectMapper.treeToValue(objectMapper.readTree(jsonPayload), methodParameter.getParameterType());
} else {
// Throw exception if validation failed
}
}
}
Next, you need to register JsonSignatureValidationArgumentResolver as an argument resolver:
#Configuration
public class JsonSignatureValidationConfiguraion implements WebMvcConfigurer {
#Autowired
private ObjectMapper objectMapper;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new JsonSignatureValidationArgumentResolver(objectMapper));
}
}
Last but not the least, you need to annotate the Controller attribute with #ValidJsonSignature as follows:
#RestController
#RequestMapping("/example")
public class ExampleController {
#PostMapping("")
public void example(#ValidJsonSignature ExampleRequest exampleRequest) {
}
}

Can I make it mandatory to include at least one of two headers with Spring?

One of my headers is misspelled, and I want to change it while being backwards compatible.
#RequestHeader(value = "Custmer-Key") String customerKey
I want to add a header with the correct spelling Customer-Key, and make at least one of them mandatory. Any ideas?
I'll make a few assumptions here. Each one may or may not be correct in your specific case, but the purpose is to give better context on when such solution is viable and makes sense to use.
You have a need to keep backward compatibility (this one is easy... you wrote it)
You have a pretty large codebase possibly based on microservices and maintained by several developers and you want to avoid large commits spanning across several teams, centralising the fix in a common shared library that all services are meant to use
Your headers are fetched using not just Spring but occasionally also by accessing the request directly
You are working in a production application where you want to change as little code as possible as some of its inner workings are difficult to understand
The solution consists into wiring a custom filter, along with its configuration. The filter will swap the HttpServletRequest instance with a different one that allows to manipulate the headers.
First, create your own filter, as follows:
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public class HeadersFilter implements Filter {
private static final String WRONG_HEADER = "Custmer-Key";
private static final String RIGHT_HEADER = "Customer-Key";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String newHeaderValue = request.getHeader(RIGHT_HEADER);
String headerValue;
if(newHeaderValue != null) {
headerValue = newHeaderValue;
}
else {
headerValue = request.getHeader(WRONG_HEADER);
}
HeadersRewriteHttpServletRequestWrapper requestWrapper = new HeadersRewriteHttpServletRequestWrapper(request);
requestWrapper.setCustomHeader(WRONG_HEADER, headerValue);
filterChain.doFilter(requestWrapper, response);
}
public static class HeadersRewriteHttpServletRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String> customHeaders;
HeadersRewriteHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
customHeaders = new HashMap<>();
}
void setCustomHeader(String name, String value) {
customHeaders.put(name, value);
}
private String getCustomHeader(String name) {
return customHeaders.get(name);
}
#Override
public String getHeader(String name) { // not needed by spring but useful if someone uses this method directly
String header = super.getHeader(name);
if(header != null) {
return header;
}
return getCustomHeader(name);
}
#Override
public Enumeration<String> getHeaderNames() {
Set<String> names = new HashSet<>(Collections.list(super.getHeaderNames()));
names.addAll(customHeaders.keySet());
return Collections.enumeration(names);
}
#Override
public Enumeration<String> getHeaders(String name) {
List<String> headers = Collections.list(super.getHeaders(name));
String customHeader = getCustomHeader(name);
if(headers.isEmpty() && customHeader != null) {
headers.add(customHeader);
}
return Collections.enumeration(headers);
}
}
}
Second, wire in the Spring configuration to create an instance of this filter and inject it as necessary.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class FilterConfiguration {
#Bean
public HeadersFilter headersFilterBean() {
return new HeadersFilter();
}
}
That's it. Assuming your application doesn't have quirks preventing this from working (in which case good luck with your debugging), this code will take the contents of both Customer-Key and Custmer-Key, giving precedence to Customer-Key and write them in a fake Custmer-Key header. This way you won't have to touch any of the controllers, which should continue to work transparently.
Next Approach is to create a annotation OneOf or something. I have used a simpler approach than using Aspect. Using this approach you can validate request param , Requestbody and RequestHeader
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = OneOfValidator.class)
#Documented
public #interface OneOf {
String message() default "";
String[] value();
}
Create a validator class like below.
public class OneOfValidator implements ConstraintValidator<OneOf, Object> {
private String[] fields;
private String fieldList;
public void initialize(OneOf annotation) {
this.fields = annotation.value();
fieldList = Arrays.toString(fields);
}
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);
int matches = countNumberOfMatches(wrapper);
if (matches > 1) {
setErrorMessage(context, <your message>);
return false;
} else if (matches == 0) {
setErrorMessage(context, <your message>);
return false;
}
return true;
}
private int countNumberOfMatches(BeanWrapper wrapper) {
int matches = 0;
for (String field : fields) {
Object value = wrapper.getPropertyValue(field);
boolean isPresent = detectOptionalValue(value);
if (value != null && isPresent) {
matches++;
}
}
return matches;
}
private boolean detectOptionalValue(Object value) {
if (value instanceof Optional) {
return ((Optional)value).isPresent();
}
if (value instanceof String) {
return StringUtils.hasText((String)value);
}
return true;
}
private void setErrorMessage(ConstraintValidatorContext context, String template) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(template)
.addNode(fieldList)
.addConstraintViolation();
}
In the controller you can create something like below.
#GetMapping(value = "your path")
public ResponseEntity<HeaderDataDTO> getBuildDetails(#RequestHeader(value = "Custmer-Key") String custmerKey,#RequestHeader(value = "Customer-Key") String customerKey
) {
HeaderDataDTO data = new HeaderDataDTO();
data.setCustomerKey(customerKey);
data.setCustmerKey(custmerKey);
data.validate();
return new ResponseEntity<>(data,
HttpStatus.OK);
}
You can define your DTO as below.
#Valid
#OneOf(value = {"customerKey", "custmerKey"})
public class HeaderDataDTO extends HeaderValidator {
private String customerKey;
private String custmerKey;
//getter and setter
HeaderValidator should be like below. Validate method will validate the object.
import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
public abstract class HeaderValidator {
public boolean validate() {
Validator validator = Validation
.buildDefaultValidatorFactory()
.getValidator();
Set<ConstraintViolation<HeaderValidator>> violations = validator.validate(this);
if (!CollectionUtils.isEmpty(violations)) {
throw <your exception>
}
return true;
}
You can create a interceptor like below.
#Component
#Primary
public class HeadersInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpInputMessage inputMessage=new ServletServerHttpRequest(request);
HttpHeaders httpHeaders = inputMessage.getHeaders();
//validation code for header goes here.
//return true if validation is successful
return true;
}
}
and add the interceptor to your configuration.
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Autowired
HeadersInterceptor headersInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(headersInterceptor);
}
}`
Now you can customize your validation in any manner.

how to get request session before controller method

I have a server and simple auth using controller. How to get request session and modify response body before running the controller method?
I want to check(for only methods marked by my own annotation) if session have an attribute (login) or not, and to send my error response body or go to the controller.
I tried to use aspectj and interceptor but as a result I can manage session after the controller method has been completed. Because interceptor preHandle method runs before aspectJ #Before.
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
public #interface CheckSession {
}
-
#Aspect
#Component
public class CheckSessionAspect {
public CheckSessionAspect() {
super();
}
#Before("#annotation(path.to.CheckSession)")
public void check(JoinPoint joinPoint) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
requestAttributes.setAttribute(SESSION_MARKER, "1", RequestAttributes.SCOPE_REQUEST);
}
}
-
#ControllerAdvice
public class ResponseModifierAdvice implements ResponseBodyAdvice<Object> {
#Autowired
ErrorService errorService;
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
if (servletRequest.getAttribute(SESSION_MARKER) != null)
if (servletRequest.getSession().getAttribute(LOGIN) == null || StringUtils.isBlank(servletRequest.getSession().getAttribute(LOGIN).toString())) {
ResponseEntity<Object> newResponseBody = errorService.sessionError(request, returnType.getMethod().getName(), NO_SESSION, getClass().toString());
body = newResponseBody.getBody();
}
return body;
}
}
I did it. You can use #Around and return ResponseEntity object if the user has no session.
#Aspect
#Component
public class CheckSessionAspect {
public CheckSessionAspect() {
super();
}
#Around("#annotation(path.to.CheckSession)")
public Object check(ProceedingJoinPoint pjp) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if ((request.getSession().getAttribute(LOGIN) == null || StringUtils.isBlank(request.getSession().getAttribute(LOGIN).toString())) ||
(request.getSession().getAttribute(IP) == null || StringUtils.isBlank(request.getSession().getAttribute(IP).toString()) ||
request.getSession().getAttribute(IP) != null && !request.getSession().getAttribute(IP).toString().equals(request.getRemoteAddr())))
{
requestAttributes.setAttribute(WRONG_SESSION_MARKER, "1", RequestAttributes.SCOPE_REQUEST);
return new ResponseEntity<Object>( new YourResponse(ERROR, "Invalid session"), HttpStatus.BAD_REQUEST);
}
Object proceed = pjp.proceed();
return proceed;
}
}

Spring Boot JPA - paging and sorting

I am trying to implement pagination to my Spring Data JPA repository in Spring Boot but I am stuck with the following exception when running uni tests:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
...
Could someone point out to me what am I missing here? This is my repository:
#Repository
public interface VenueRepository extends PagingAndSortingRepository<Venue, Long> {
public Page<Venue> findAll(Pageable pageable);
}
and controller:
#RestController
#RequestMapping("/venues")
public class VenueController {
#Autowired
private VenueRepository venueRepo;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Page<Venue>> getVenues(Pageable pageable) {
return new ResponseEntity<>(venueRepo.findAll(pageable), HttpStatus.OK);
}
}
and finally my test:
#Test
public void responseOkVenuesTest() throws Exception {
mvc.perform(get("/venues").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk());
}
I spent couple of hours trying to make this work and am running out of ideas. Thank you for any tips!
Change your method getVenues in the way that you can pass the parameters to instantiate a PageRequest instead of passing Pageable :
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Venue>> getVenues(int from,int to) {
return new ResponseEntity<>(
venueRepo.findAll((new PageRequest(from, to)), HttpStatus.OK).getContent();
}
In addition to #SEY_91's answer you might also like to use the following solution inspired with How to remove redundant Spring MVC method by providing POST-only #Valid? and used in my Spring Boot-driven application for long time.
In short, here is an annotation to annotate controller method parameters:
#Target(PARAMETER)
#Retention(RUNTIME)
public #interface PlainModelAttribute {
}
Now, just a method processor that would scan for parameters annotated with #PlainModelAttribute:
public final class PlainModelAttributeMethodProcessor
extends ModelAttributeMethodProcessor {
private final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index;
private PlainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
super(true);
this.index = index;
}
public static HandlerMethodArgumentResolver plainModelAttributeMethodProcessor(final Map<TypeToken<?>, Converter<? super NativeWebRequest, ?>> index) {
return new PlainModelAttributeMethodProcessor(index);
}
#Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.hasParameterAnnotation(PlainModelAttribute.class) || super.supportsParameter(parameter);
}
#Override
protected Object createAttribute(final String attributeName, final MethodParameter parameter, final WebDataBinderFactory binderFactory,
final NativeWebRequest request) {
final TypeToken<?> typeToken = TypeToken.of(parameter.getGenericParameterType());
final Converter<? super NativeWebRequest, ?> converter = index.get(typeToken);
if ( converter == null ) {
throw new IllegalArgumentException("Cannot find a converter for " + typeToken.getType());
}
return converter.convert(request);
}
#Override
protected void bindRequestParameters(final WebDataBinder binder, final NativeWebRequest request) {
final HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if ( !isSafe(resolve(servletRequest.getMethod())) ) {
((ServletRequestDataBinder) binder).bind(servletRequest);
}
}
private static HttpMethod resolve(final String name) {
return HttpMethod.valueOf(name.toUpperCase());
}
private static boolean isSafe(final HttpMethod method)
throws UnsupportedOperationException {
switch ( method ) {
case GET:
case HEAD:
case OPTIONS:
return true;
case POST:
case PUT:
case PATCH:
case DELETE:
return false;
case TRACE:
throw new UnsupportedOperationException();
default:
throw new AssertionError(method);
}
}
}
I don't really remember, but a resolve() method equivalent should be present in Spring Framework somewhere. Note that I use Google Guava TypeToken in order to let the processor be compatible with generic types (since I use models like IQuery<Foo> and IQuery<Bar> in controllers). Now just register the processor:
#Configuration
#EnableWebMvc
public class MvcConfiguration
extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(createModelAttributeMethodProcessor());
}
private static HandlerMethodArgumentResolver createModelAttributeMethodProcessor() {
return plainModelAttributeMethodProcessor(ImmutableMap.of(pageableTypeToken, MvcConfiguration::toPageable));
}
private static final TypeToken<Pageable> pageableTypeToken = new TypeToken<Pageable>() {
};
private static Pageable toPageable(final WebRequest request) {
return new PageRequest(
ofNullable(request.getParameter("page")).map(Integer::parseInt).orElse(0),
ofNullable(request.getParameter("size")).map(Integer::parseInt).orElse(1)
);
}
}
Here is a web request to a Pageable DTO conversion, and the converter must be registered as an argument resolver. So now it's ready to use:
#RestController
#RequestMapping("/")
public class Controller {
#RequestMapping(method = GET)
public String get(#PlainModelAttribute final Pageable pageable) {
return toStringHelper(pageable)
.add("offset", pageable.getOffset())
.add("pageNumber", pageable.getPageNumber())
.add("pageSize", pageable.getPageSize())
.add("sort", pageable.getSort())
.toString();
}
}
A few examples:
/ ⇒ PageRequest{offset=0, pageNumber=0, pageSize=1, sort=null}
/?page=43 ⇒ PageRequest{offset=43, pageNumber=43, pageSize=1, sort=null}
/?size=32 ⇒ PageRequest{offset=0, pageNumber=0, pageSize=32, sort=null}
/?page=22&size=32 ⇒ PageRequest{offset=704, pageNumber=22, pageSize=32, sort=null}

How can I use Spring REST with MappingJackson2XmlHttpMessageConverter and XSD validation

I'm creating a Spring 4 REST application, using MappingJackson2XmlHttpMessageConverter to convert incoming XML requests to domain objects. Is there any way to apply XSD validation in that process? If not, I think my fallback is to just make the #RequestBody a String, parse and validate it, and then convert it to the domain object. Is there a better approach?
One approach to this may be to write a custom HttpMessageConverter<T> that checks XSD validation (look here for a way to validate XML with XSD) before returning the object.
Suppose that you have the following method in your Controller class:
#RequestMapping(value = "/")
public CustomObject getCustomObject(#RequestParam(value = "id") String id){
return new CustomObject();
}
Then your converter may look like this:
public class CustomObjectConverter implements HttpMessageConverter<CustomObject> {
// a real message converter that will respond to ancillary methods and do the actual work
protected HttpMessageConverter<Object> delegateConverter;
public CustomObjectConverter (HttpMessageConverter<Object> delegate) {
super(delegate, personService);
super.delegateConverter = delegate;
this.employeePhotoBaseUrl = employeePhotoBaseUrl;
}
#Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canRead(clazz, mediaType) && CustomObject.class.equals(clazz);
}
#Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return delegateConverter.canWrite(clazz, mediaType) && CustomObject.class.equals(clazz);
}
#Override
public List<MediaType> getSupportedMediaTypes() {
return delegateConverter.getSupportedMediaTypes();
}
#Override
public CustomObject read(Class<? extends CustomObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return (CustomObject) delegateConverter.read(clazz, inputMessage);
}
#Override
public void write(CustomObject t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
if(validationOK)
delegateConverter.write(t, contentType, outputMessage);
else
// You may implement a custom exception handler to return a proper HTTP error code
throw new YourCustomException();
}
}
Remember to configure your new converter. I do this in my configuration class:
#Configuration
#EnableWebMvc
public class RestConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// initialize your MappingJackson2XmlHttpMessageConverter
MappingJackson2XmlHttpMessageConverter xmlMessageConverter = xmlMessageConverter();
//Here we add our custom-configured HttpMessageConverters
converters.add(new CustomObjectConverter(xmlMessageConverter));
super.configureMessageConverters(converters);
}
}

Categories