#Valid JSON request with BindingResult causes IllegalStateException - java

I have a REST service which takes a JSON request. I want to validate the JSON request values that are coming in. How can I do that?
In Spring 3.1.0 RELEASE, I know one wants to make sure they are using the latest support classes listed at 3.1.13 New HandlerMethod-based Support Classes For Annotated Controller Processing
The old ones are items like: AnnotationMethodHandlerAdapter. I want to make sure I am using the latest such as RequestMappingHandlerAdapter.
This is because I hope it fixes an issue where I see this:
java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
My #Controller handler method and associated code is this:
#Autowired FooValidator fooValidator;
#RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public #ResponseBody Map<String, String> fooBar(
#Valid #RequestBody Map<String, String> specificRequest,
BindingResult results) {
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
}
#InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder){
binder.setValidator(fooValidator);
}
FooValidator looks like this:
#Component
public class FooValidator implements Validator {
public boolean supports(Class<?> clazz) {
out("supports called ");
return Map.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
out("validate called ");
}
private void out(String msg) {
System.out.println("****** " + getClass().getName() + ": " + msg);
}
}
If I remove the BindingResult, everything works fine except I won't be able to tell if the JSON validated.
I am not strongly attached to the concept of using a Map<String, String> for the JSON request or using a separate validator as opposed to a Custom Bean with validation annotation (How do you do that for a JSON request?). Whatever can validate the JSON request.

3.1.17 #Valid On #RequestBody Controller Method Arguments says that:
An #RequestBody method argument can be annotated with #Valid to invoke automatic validation similar to the support for #ModelAttribute method arguments. A resulting MethodArgumentNotValidException is handled in the DefaultHandlerExceptionResolver and results in a 400 response code.
In other words, if you use #Valid #RequestBody then Spring will reject an invalid request before it gets as far as calling your method. if you method is invoked, then you can assume the request body is valid.
BindingResult is used for validation of form/command objects, rather than #RequestBody.

I had to do something similar once. I just ended up making my life simpler by creating a Java object that the JSON could be convert into and used GSON to do the conversion.
It was honestly as simple as:
#Autowired
private Gson gson;
#RequestMapping(value = "/path/info", method = RequestMethod.POST)
public String myMethod(#RequestParam(value = "data") String data,
Model model,
#Valid MyCustomObject myObj,
BindingResult result) {
//myObj does not contain any validation information.
//we are just using it as as bean to take advantage of the spring mvc framework.
//data contains the json string.
myObj = gson.fromJson(data, MyCustomObject.class);
//validate the object any way you want.
//Simplest approach would be to create your own custom validator
//to do this in Spring or even simpler would be just to do it manually here.
new MyCustomObjValidator().validate(myObj, result);
if (result.hasErrors()) {
return myErrorView;
}
return mySuccessView;
}
Do all your validation in your custom Validator class:
public class MyCustomObjValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return MyCustomObj.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MyCustomObj c = (MyCustomObj) target;
Date startDate = c.getStartDate();
Date endDate = c.getEndDate();
if (startDate == null) {
errors.rejectValue("startDate", "validation.required");
}
if (endDate == null) {
errors.rejectValue("endDate", "validation.required");
}
if(startDate != null && endDate != null && endDate.before(startDate)){
errors.rejectValue("endDate", "validation.notbefore.startdate");
}
}
}
MyCustomObject does not contain any annotation for validation, this is because otherwise Spring will try to validate this fields in this object which are currently empty because all the data is in the JSON String, it could for example be:
public class MyCustomObject implements Serializable {
private Date startDate;
private Date endDate;
public Date getStartDate() {
return startDate;
}
public Date getEndDate() {
return endDate;
}
public void setStartDate(Date theDate) {
this.startDate = theDate;
}
public void setEndDate(Date theDate) {
this.endDate = theDate;
}
}

Try using the following:
#Autowired
private FooValidator fooValidator;
#InitBinder("specificRequest") // possible to leave off for global behavior
protected void initBinder(WebDataBinder binder){
binder.setValidator(fooValidator);
}
#ModelAttribute("specificRequest")
public Map<String, String> getModel() {
return new HashMap<String, String>();
}
This will make your controller serialize the request into the type you specify it to be.
I have to say i normally dont make a service (autowired) of the validator, but it might be better.
Your handler looks like this now:
#RequestMapping(value="/somepath/foo", method=RequestMethod.POST)
public #ResponseBody Map<String, String> fooBar(
#Valid #ModelAttribute("specificRequest")
Map<String, String> specificRequest, BindingResult results) {
out("fooBar called");
// get vin from JSON (reportRequest)
return null;
}
To my knowledge this works perfectly and addresses the error you are receiving.

Related

Validate Json Object before casting it to POJO - using #requestBody

I would like to validate the incoming json object in controller before casting it to POJO using spring jackson.
My Controller:
#RequestMapping( value = "/createContact" , method = RequestMethod.POST , consumes = MediaType.APPLICATION_JSON_VALUE , produces = MediaType.APPLICATION_JSON_VALUE )
public Contact createContact( #RequestBody Contact contact ) throws Exception
{
return ContactService.createContact( contact );
}
My Contact.java
public class Contact
{
private String ID = UUID.randomUUID().toString();
private String type = "contact";
private String category;
private String name;
}
What I am trying to achieve is that 'type' field should not be passed in the request json. I need to throw an exception if the consumer passes that value.
I can get the json as a Map or string and validate it and then cast it to POJO. But is it possible to validate it before direct casting?
This can be done with an interceptor which will extend HandlerInterceptor. For example, you can create a ContactRequestValidator class like below.
#Component("contactRequestInterceptor")
public class ContactRequestValidator implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
// get the request payload using reader from httpServletRequest and do the validation
// and throw an exception if not valid and may handle it using an Spring MVC exception handler
}
// other two methods omitted..
}
Then register the validator interceptor with
#Configuration
public class MVCConfigurerAdapter extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("contactRequestInterceptor")
private HandlerInterceptor contactRequestValidator;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(contactRequestValidator).addPathPatterns("/api/**"); // Also have the option to use Ant matchers
}
}

Spring MVC (RESTful API): Validating payload dependent on a path variable

Use Case:
let's design a RESTful create operation using POST HTTP verb - creating tickets where creator (assigner) specifies a ticket assignee
we're creating a new "ticket" on following location: /companyId/userId/ticket
we're providing ticket body containing assigneeId:
{
"assigneeId": 10
}
we need to validate that assigneeId belongs to company in URL - companyId path variable
So far:
#RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(#Valid #RequestBody Ticket newTicket, #PathVariable Long companyId, #PathVariable Long userId) {
...
}
we can easily specify a custom Validator (TicketValidator) (even with dependencies) and validate Ticket instance
we can't easily pass companyId to this validator though! We need to verify that ticket.assigneeId belongs to company with companyId.
Desired output:
ability to access path variables in custom Validators
Any ideas how do I achieve the desired output here?
If we assume that our custom validator knows desired property name, then we can do something like this:
Approach one:
1) We can move this getting path variables logic to some kind of a base validator:
public abstract class BaseValidator implements Validator {
#Override
public boolean supports(Class<?> clazz)
{
// supports logic
}
#Override
public void validate(Object target, Errors errors)
{
// some base validation logic or empty if there isn't any
}
protected String getPathVariable(String name) {
// Getting current request (Can be autowired - depends on your implementation)
HttpServletRequest req = HttpServletRequest((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (req != null) {
// getting variables map from current request
Map<String, String> variables = req.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return variables.get(name);
}
return null;
}
}
2) Extend it with your TicketValidator implementation:
public class TicketValidator extends BaseValidator {
#Override
public void validate(Object target, Errors errors)
{
// Getting our companyId var
String companyId = getPathVariable("companyId");
...
// proceed with your validation logic. Note, that all path variables
// is `String`, so you're going to have to cast them (you can do
// this in `BaseValidator` though, by passing `Class` to which you
// want to cast it as a method param). You can also get `null` from
// `getPathVariable` method - you might want to handle it too somehow
}
}
Approach two:
I think it worth to mention that you can use #PreAuthorize annotation with SpEL to do this kind of validation (You can pass path variables and request body to it). You'll be getting HTTP 403 code though if validation woudnt pass, so I guess it's not exaclty what you want.
You could always do this:
#Controller
public class MyController {
#Autowired
private TicketValidator ticketValidator;
#RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(#RequestBody Ticket newTicket,
#PathVariable Long companyId, #PathVariable Long userId) {
ticketValidator.validate(newTicket, companyId, userId);
// do whatever
}
}
Edit in response to the comment:
It doesn't make sense to validate Ticket independently of companyId when the validity of Ticket depends on companyId.
If you cannot use the solution above, consider grouping Ticket with companyId in a DTO, and changing the mapping like this:
#Controller
public class MyController {
#RequestMapping(value="/{userId}/ticket", method=POST)
public void createTicket(#Valid #RequestBody TicketDTO ticketDto,
#PathVariable Long userId) {
// do whatever
}
}
public class TicketDTO {
private Ticket ticket;
private Long companyId;
// setters & getters
}

Spring4 MVC serialize enum as string with quotes

i have a rest controller in a spring boot mvc container
#RestController
public class LoginController {
#RequestMapping("rest/login")
public Response login() {
return Response.GRANTED;
}
public static enum Response {
GRANTED, DENIED;
}
}
I have to use double quotes for checking the return type after request a rest resource. how to avoid the double quotes?
$http.post("rest/login", $scope.data).success(function(data) {
if (data === "\"GRANTED\"") {
alert("GRANTED")
} else if (data === "DENIED") {
alert("DENIED")
};
#RestController
public class LoginController {
#RequestMapping("rest/login")
public String login() {
return Response.GRANTED.name();
}
public static enum Response {
GRANTED, DENIED;
}
}
bring the result I want but I want the type safe return type Response and not String.
Thanks for help.
A #RestController is like a #Controller annotated with #ResponseBody. That is, each handler is implicitly annotated with #ResponseBody. With any reference type other than String (and a few others), the default target content-type is JSON.
The 6 data types in JSON are Object, Array, Number, String, true, false, and null. How would you map an enum constant? The default that Jackson (which backs the default JSON HttpMessageConverter) serializes an enum constant to a JSON String. That's arguably the best matching JSON data type.
You could force it to write the value without quotes by providing your own JsonSerializer
#JsonSerialize(using = ResponseSerializer.class)
public static enum Response {
...
class ResponseSerializer extends JsonSerializer<Response> {
#Override
public void serialize(Response value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeRaw(value.name());
}
}
but I don't recommend it since you wouldn't be producing valid JSON.
You should really consider what others have suggested and use the various HTTP status codes.

Spring Validation - Programmatically bind to a Validator

I am trying to use Spring validation to validate my model populated by Jackson converter.
So I have a java class,
class MyClass(){
private String myString;
}
This class is populated by Jackson and I have the instance in my Java code.
Have also defined a validator class like,
class MyValidator implements Validator {
public boolean supports(Class<?> clazz) {
return MyClass.class.equals(clazz);
}
public void validate(Object object, Errors errors) {
//Validation logic here
}
}
Now what I wanted to do was to send the object to the validator and get the errors object, examine and proceed further. But, for calling
validate(Object object,Errors errors)
I need an errors instance which I dont have. I cant use BindingResult or Errors. Please advise on how to proceed further.
You can do this manually in code with a DataBinder:
MyClass toValidate = new MyClass();
DataBinder binder = new DataBinder(toValidate);
binder.setValidator(new MyValidator());
binder.validate();
if (binder.getBindingResult().hasErrors()) {
// oh noes!
}
Although if you've got a #ModelAttribute defined in a #Controller in spring-mvc, something like this should work (placed inside the relevant #Controller):
#ModelAttribute("myclass")
public MyClass myClass() {
return new MyClass();
}
#InitBinder("myclass")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new MyValidator());
}
#RequestMapping(value = "/do/something", method = POST)
public ModelAndView validatedRequest(#Valid #ModelAttribute("myclass") MyClass profile,
BindingResult result) {
if (result.hasErrors()) {
// oh noes!
}
}

Spring MVC returning JSONS and exception Handling

I am using Spring MVC with Controllers, my question is how do I return a JSON response which is different from the #ResponseBody object which is returned and convereted to a JSON to be returned.
To elaborate further, I have the object called "UserDetails" which has two fields called "name", "emailAddress"
#ResponseBody UserDetails
now the json returned will look like
{ name : "TheUsersName",
emailAddress:"abc#abc123.com" }
Is there any way I can modify the json before returning (ALL jsons in all methods across all controllers) where a "status" field will be added and the other json data will be under the "data" key in the json.
Also how do I return a json to the frontend when the java server from somewhere throws an exception, the json should have "status : false" and the exception name (atleast the status part though)
Create a response class:
public class Response<T> {
T data;
boolean status = true;
public Response(T d) { data = d; }
}
Then return that from your controllers:
#ResponseBody public Response getUserDetails) {
//...
return new Response(userDetails);
}
For the exception you'll want to return an object like:
public class BadStatus {
String errorMessage;
boolean status = false;
public BadStatus(String msg) { errorMessage = msg; }
}
#ExceptionHandler(Exception.class)
public BadStatus handleException(Exception ex, HttpServletRequest request) {
return new BadStatus(ex.getMessage());
}
Yes. Return a model and a view instead.
public ModelMap getUserDetails() {
UserDetails userDetails; // get this object from somewhere
ModelMap map = new ModelMap()(;
map.addAttribute("data", userDetails);
map.addAttribute("success", true);
return map;
}
To add the exception you'd do it the same way with a key and success = false.
An alternate solution (works with spring 3.1), which is less invasive
in your spring config :
<bean id="jacksonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="mypackage.MyMessageConverter"
p:delegate-ref="jacksonConverter">
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
The idea is to provide your own HttpMessageConverter that delegates to the provided jackson converter.
public class MyMessageConverter implements HttpMessageConverter<Object> {
// setters and delegating overrides ommitted for brevity
#Override
public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
// t is whatever your #ResponseBody annotated methods return
MyPojoWrapper response = new MyPojoWrapper(t);
delegate.write(response, contentType, outputMessage);
}
}
This way all your pojos are wrapped with some other json that you provide there.
For exceptions, the solution proposed by ericacm is the simplest way to go (remember to annotate the 'BadStatus' return type with #ResponseBody).
A caveat : your json-serialized BadStatus goes through MyMessageConverter too, so you will want to test for the object type in the overriden 'write' method, or have MyPojoWrapper handle that.

Categories