In your controller method, you can include an argument of type @RequestParam Map<String, String> to access all the request parameters passed to. The generic ArgsChecker class can be used to check if the user passed in an invalid argument. If so, you can throw an exception that can be handled by the @ControllerAdvice class.
@RestController @ExposesResourceFor(Widget.class) @RequestMapping("/widgets") public class WidgetController { @Autowired ArgsChecker<Widget> widgetArgsChecker; @RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/hal+json"}) public HttpEntity<PagedResources<WidgetResource>> findAll(@RequestParam @ApiIgnore Map<String, String> allRequestParams, Pageable pageable, PagedResourcesAssembler pageAssembler) { Set<String> invalidArgs = widgetArgsChecker.getInvalidArgs(allRequestParams.keySet()); if (invalidArgs.size() > 0) { throw new QueryParameterNotSupportedException("The user supplied query parameter(s) that are not supported: " + invalidArgs + " . See below for a list of query paramters that are supported by the widget endpoint.", invalidArgs, widgetArgsChecker.getValidArgs()); }
ArgsChecker can be defined as follows:
import com.widgetstore.api.annotation.Queryable; import lombok.Getter; import org.apache.commons.lang3.reflect.FieldUtils; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; public class ArgsChecker<T> { @Getter private Set<String> validArgs; private ArgsChecker(){}; public ArgsChecker(Class<T> someEntityClass){ validArgs= FieldUtils.getFieldsListWithAnnotation(someEntityClass,Queryable.class) .stream() .map(Field::getName) .collect(Collectors.toSet()); validArgs.add("page"); validArgs.add("size"); } public Set<String> getInvalidArgs(final Set<String> args){ Set<String> invalidArgs=new HashSet<>(args); invalidArgs.removeAll(validArgs); return invalidArgs; } }
which uses reflection to search for fields that are annotated with @Queryable annotation:
package com.widgetstore.api.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Queryable { }
Now mark the fields of your domain class that you want to request using this annotation:
@Getter @Setter public class Widget { @Queryable private String productGuid; @Queryable private String serialNumber; private String manufacturer;
Now make sure that the ArgsChecker bean is created when the application starts:
@SpringBootApplication public class StartWidgetApi{ public static void main(String[] args){ SpringApplication.run(StartWidgetApi.class); } @Bean(name="widgetArgsChecker") public ArgsChecker<Widget> widgetArgsChecker(){ return new ArgsChecker<Widget>(Widget.class); }
Finally,
Define the @ControllerAdvice class that will listen for exceptions thrown by your application:
package com.widgetstore.api.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @ControllerAdvice @RequestMapping(produces = "application/json") @ResponseBody public class RestControllerAdvice { @ExceptionHandler(QueryParameterNotSupportedException.class) public ResponseEntity<Map<String,Object>> unrecogonizedParameter(final QueryParameterNotSupportedException e){ Map<String,Object> errorInfo = new LinkedHashMap<>(); errorInfo.put("timestamp",new Date()); errorInfo.put("errorMessage",e.getMessage()); errorInfo.put("allowableParameters",e.getValidArgs()); return new ResponseEntity<Map<String, Object>>(errorInfo,HttpStatus.BAD_REQUEST); } }
and define a QueryParameterNotSupportedException:
import lombok.Getter; import java.util.Set; @Getter public class QueryParameterNotSupportedException extends RuntimeException{ private Set<String> invalidArgs; private Set<String> validArgs; public QueryParameterNotSupportedException(String msg, Set<String> invalidArgs, Set<String> validArgs){ super(msg); this.invalidArgs=invalidArgs; this.validArgs=validArgs; } }
Now, when does the user hit / widgets? someUnknownField = abc & someOtherField = xyz, it will get a json response along the lines
{"timestamp": 2017-01-10'T'blahblah, "errorMessage": "The user supplied query parameter(s) that are not supported: ["someUnknownField","someOtherField"]. See below for a list of allowed query parameters." , "allowableParameters": ["productGuid","serialNumber"] }