membersound
membersound

Reputation: 86757

How to handle invalid parameters in REST service?

I offer a REST webservice using spring @RestController.

How should in general invalid parameter content be handled? I tried throwing a custom exception, but that will lead to a HTTP 500 error on the client side and expose the stacktrace thereby.

Probably that is not the right way. But how should just simple error messages be returned? (the webservice will not be accessed manually by users. Just by other services connecting to the rest controller).

Upvotes: 3

Views: 10762

Answers (4)

Chris Hinshaw
Chris Hinshaw

Reputation: 7255

Im using jersey and this is a simple example that will use the hibernate bean validation framework to validate your beans. This is a work in progress but you should can see how it will work very simply.

@Path("customers")
public class CustomerResource {
    @PUT
    public Response createCustomer(Customer customer) {
        BeanValidator.validate(customer);
        final String rialtoId = customerProvider.createCustomer(customer);

        return Response.ok(rialtoId).build();
    }
}

Here is a generic class that I created that handles the bean validation.

public class BeanValidator {

    /**
     * Used to validate an order request and all the attached objects that
     * support validation.
     * 
     * @param request
     * @throws ConstraintViolationException
     */
    public static <T> void validate(T request) throws ConstraintViolationException {
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            Validator validator = factory.getValidator();
            Set<ConstraintViolation<T>> constraintViolations = validator.validate(request);
            if (constraintViolations.size() > 0) {
                    throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(constraintViolations));
            }
    }
}


@XmlRootElement
public class Customer {

    @NotNull(message = "spCustomerID1 is a required field")
    @Size(max = 60, message = "spCustomerID1 has a max length of 60 characters")
    private String spCustomerID1;

    @Size(max = 60, message = "spCustomerID2 has a max length of 60 characters")
    private String spCustomerID2;

    @Size(max = 60, message = "spCustomerID3 has a max length of 60 characters")
    private String spCustomerID3;

    @NotNull(message = "customerName is a required field")
    @Size(max = 60)
    private String customerName;

    @Valid
    @NotNull(message = "customerAddress is a required field")
    private PostalAddress customerAddress;

    @Valid
    @NotNull(message = "customerContact is a required field")
    private ContactInfo customerContact;

    @Valid
    @NotNull(message = "technicalContact is a required field")
    private ContactInfo technicalContact;
    ... / Getters and Setters
}

Then here is a simple ExceptionMapper that will support constructing a simple response to be sent back to the client. Notice that it will set the Response type to a 400 BAD_REQUEST instead of a 500+ Server Side error.

public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {

    public Response toResponse(ConstraintViolationException exception) {

        final StringBuilder strBuilder = new StringBuilder();

        for (ConstraintViolation<?> cv : exception.getConstraintViolations()) {
            strBuilder.append(cv.getPropertyPath().toString() + " " + cv.getMessage());
        }

        RestResponse responseEntity = RestResponse.responseCode(ResponseCode.CONSTRAINT_VIOLATION).setResponseMessage(strBuilder.toString()).build();

        return Response.status(Response.Status.BAD_REQUEST).entity(responseEntity).build();
    }
}

This code hasn't been tested yet but it might help to get some ideas of how to do the validation. This is a pretty straight forward way to do rest service validation in my opinion and allows you to report exact variable paths along with customized error messages for each field.

Upvotes: 5

tddmonkey
tddmonkey

Reputation: 21184

You should validate your parameters at the very outmost layer of your application before it gets handed off inside your domain. At this point you're still in the HTTP layer so can take the appropriate action which is to return a 400 BAD REQUEST status.

Within that you have complete control over how to relay this information to your users (or other services). Plain text is fine if you're just logging it, or design your own Json/Xml payload describing the error.

Upvotes: 1

sebge2
sebge2

Reputation: 403

I you manually validate your arguments, you could throw a specific exception. Then you can map your exception to a specific HTTP status, for instance BAD REQUEST.

You can map your exception to a response status with Spring Controller Advice: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc.

Upvotes: 0

Yazan
Yazan

Reputation: 6082

if i understand you well,

then , generally i think it's good to have a key in each json response (or even if your response is XML), that indecates the status of the process. this field could be called status.

so each and every response you send back should have this status field, and it's value should indicate what happens while processing, and what should the caller expect in the response.

the value could be a number, or a text message, some constant-like message also you can add another field, message that contains some text-desc of the status code.

now you have to make a list of possible statues that your service may send back.

ex:

status: 0000 
message: success

status: 0001 
message: invalid_params

status: 0002 
message: invalid_param_value

status: 0003 
message: missing_param,
:
:
etc

so your json response will contain those fields always. among the other data supposed to be returned.

now it's the clients duty to handle those responses.

JSON example:

{
"status":"0000",
"message":"success",
"images":[ ... ]
}

{
"status":"0003",
"message":"missing_param"
}

as you notice in case of non 0000 status, no other data is sent back. just telling the client we have "this issue".

or you can make it more informative, by adding : to the error message constant, telling more info about the error:

ex,

{
"status":"0003",
"message":"missing_param:album_id"
}

telling the user, there is a missing parameter, and it's album_id

now you can write all possible status responses, and there message this will be part of your service documentation.

Upvotes: 0

Related Questions