Как эффективно поддерживать валидацию JSR-303 и сопоставление JSON Джексона? - PullRequest
13 голосов
/ 11 декабря 2011

При создании веб-службы RESTful с использованием Spring MVC мне кажется, что я столкнулся с непростой ситуацией, когда пытался объединить десериализацию JSON Джексона и проверку bean-компонента JSR-303.

Одна из интересных особенностей JSR-Проверка 303 заключается в том, что можно возвращать все ошибки проверки, связанные с целевым объектом, а не просто потерпеть неудачу при первом нарушении ограничения (хотя существует режим быстрого отказа, если вы хотите его использовать).

Представьте себе сценарий, в котором у вас есть полезная нагрузка JSON, такая как:

{
   "firstname": "Joe",
   "surname": "Bloggs",
   "age": 25
}

И затем у вас есть объект, который вы хотите сопоставить:

public class Person {

   @NotNull
   private String firstname;

   @NotNull
   private String surname;

   @Min(value = 0)
   private int age;

   ...
}

Моя проблемас этой настройкой так: если клиент отправляет String в качестве значения для "age", весь процесс десериализации Джексона завершится неудачно, не имея возможности конкретно указать причину, по которой он завершился неудачей (т.е. мы никогда не доберемся до проверки).Представьте себе такую ​​полезную нагрузку:

{
   "age": "invalid"
}

В этом случае я хотел бы получить ответ "ошибки", такой как:

{ 
   "errors" : [
      "error": {
         "field": "firstname",
         "message": "Must not be null",
      },
      "error": {
         "field": "surname",
         "message": "Must not be null",
      },
      "error": {
         "field": "age",
         "message": "Must be numeric value greater than or equal 0",
      }
   ]
}

Простой способ обойтиэта проблема будет заключаться в том, чтобы просто указать поле age в качестве типа «String», а затем преобразовать значение при его передаче, скажем, на уровень постоянства.Тем не менее, мне не очень нравится идея прибегать к объектам передачи данных, которые используют строки единообразно - просто неправильно делать это с точки зрения сохранения чистого дизайна.

Еще один вариант, о котором я подумалбыло бы создать класс, такой как:

public abstract class BindableValue<T> {

   private String stringValue;

   private T value;

   public T getValue() {
      if(value == null) {
         value = fromString(stringValue);
      }
      return value;
   }

   protected abstract T fromString(String stringValue);
}

Затем я мог бы создать реализации, такие как IntegerValue, LongValue, DateTimeValue и т. д., конечно, также писать адаптеры пользовательских типов для Джексона.И тогда мой класс Person может быть:

public class Person {

   @NotNull
   private StringValue firstname;

   @NotNull
   private StringValue surname;

   @Min(value = 0)
   private IntegerValue age;

   ...
}

Это почти то, что я хочу, но, к сожалению, аннотации JSR-303, такие как @NotNull и @Min, не будут распознавать мои реализации BindableValue.

Итак, кто-нибудь может объяснить, как я мог бы решить эту проблему более чистым способом?

Ответы [ 3 ]

2 голосов
/ 11 декабря 2011

Теперь я понимаю, что наилучшим способом сделать это может быть простое использование строк для всех полей, но инкапсулирование этого факта и предоставление установщиков и получателей методов конкретного типа. например,

public class Person {

    @NotNull
    private String name;

    @NotNull
    @Min(value = 0)
    private String age;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age != null ? new Integer(age) : null;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age == null ? null : String.valueOf(age);
    }

    @JsonSetter
    private void setAge(String age) {
        this.age = age;
    }
 }

Таким образом, я получаю поведение, за которым следую, в то же время гарантируя, что интерфейс публичного объекта все еще "чист".

1 голос
/ 15 декабря 2011

Другая возможность - просто отключить жалобы Джексона на нераспознанные свойства (см. эту вики-страницу), «отбросить» нераспознанные вещи и проверить вещи, которые действительно были связаны. Вы также можете определить слушателя для нераспознанных свойств. И тогда вы можете использовать Bean Validation для проверки данных, которые действительно связаны.

0 голосов
/ 20 октября 2012

Хорошо, поскольку это еще не было упомянуто .... DropWizard - это путь, который я думаю - он сочетает в себе JAX-RS (Джерси) на Джексоне с Bean Validation (я думаю, что это hibernate)и все это "просто работает".

...