2015-03-07 2 views
3

Я пытаюсь реализовать универсальный контроллер REST для Spring MVC:ошибка «аргумент типа несоответствие» при использовании универсального типа для @RequestBody с Spring MVC

public abstract class GenericRestController<T extends GenericEntity> { 

    protected final GenericService<T> service; 

    public GenericRestController(GenericService<T> service) { 
    this.service = service; 
    } 

    @RequestMapping(method = RequestMethod.GET) 
    @ResponseStatus(HttpStatus.OK) 
    public List<T> list() { 
    return service.list(); 
    } 

    @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseStatus(HttpStatus.CREATED) 
    public T create(@RequestBody T entity) { 
    return service.create(entity); 
    } 

    // other basic REST methods such as get(), delete(), etc. 

} 

Тогда, например ArticleRestController:

@RestController 
@RequestMapping(value = "/rest/articles", produces = MediaType.APPLICATION_JSON_VALUE) 
public class ArticleRestController extends GenericRestController<Article> { 

    @Autowired 
    public ArticleRestController(ArticleService articleService) { 
    super(articleService); 
    } 

} 

Скажем, статья содержит только поле nameString.

При вызове GET /rest/articles я получаю список сохраненных статей, как ожидалось. Но при вызове POST /rest/articles {name: "Any Article Name"}, я ловлю следующее исключение:

java.lang.IllegalStateException: argument type mismatch 

HandlerMethod details: 
Controller [com.basepackage.web.controllers.rest.ArticleRestController] 
Method [public T com.basepackage.web.controllers.rest.support.GenericRestController.create(T)] 
Resolved arguments: 
[0] [type=com.google.gson.internal.LinkedTreeMap] [value={name=Any Article Name}] 

При переопределении метода create в ArticleRestController, исключение не брошено больше:

@Override 
    @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseStatus(HttpStatus.CREATED) 
    public Article create(@RequestBody Article article) { 
    return articleService.create(article); 
    } 

Любая идея, почему? Может быть, Spring не может решить общий тип T при использовании в качестве @RequestBody?

+0

Какую версию весны вы используете? (В качестве предложения не пытайтесь использовать обобщение.) –

+0

@SotiriosDelimanolis 4.1.5.RELEASE – sp00m

+0

Вы вручную зарегистрировали конвертер 'Gson'? –

ответ

5

С весны 4.1 в список экземпляров HttpMessageConverter, которые Spring использует для сериализации объекта, возвращаемого методом аннотированного обработчика @ResponseBody, добавляется GsonHttpMessageConverter. Этот ток GsonHttpMessageConverter недостаточно изощрен для десериализации типичных типов. Поэтому, когда он видит переменную типа T, он не пытается связать точки с аргументом типа Article в предложении extends и вместо этого использует значение по умолчанию LinkedTreeMap.

Одним из вариантов является добавление Jackson 2 в ваш путь к классам. Весна будет регистрировать свой MappingJackson2HttpMessageConverter до GsonHttpMessageConverter. То есть, он попытается использовать его в первую очередь. Конвертер Джексона достаточно умен, чтобы выполнить общую десерилизацию.

Смежные вопросы