Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring通用REST控制器:解析请求主体_Java_Json_Spring_Rest_Generics - Fatal编程技术网

Java Spring通用REST控制器:解析请求主体

Java Spring通用REST控制器:解析请求主体,java,json,spring,rest,generics,Java,Json,Spring,Rest,Generics,我有以下控制器: @RestController @RequestMapping(value = "/{entity}", produces = MediaType.APPLICATION_JSON_VALUE) public class CrudController<T extends SomeSuperEntity> { @RequestMapping(method = GET) public Iterable<T> findAll(@PathVar

我有以下控制器:

@RestController
@RequestMapping(value = "/{entity}", produces = MediaType.APPLICATION_JSON_VALUE)
public class CrudController<T extends SomeSuperEntity> {

    @RequestMapping(method = GET)
    public Iterable<T> findAll(@PathVariable String entity) {
    }

    @RequestMapping(value = "{id}", method = GET)
    public T findOne(@PathVariable String entity, @PathVariable String id) {
    }

    @RequestMapping(method = POST)
    public void save(@PathVariable String entity, @RequestBody T body) {
    }
}
AbstractEntity
其带有某些字段的抽象类:

public abstract class AbstractEntity implements Comparable<AbstractEntity>, Serializable {

    private Timestamp firstField;
    private String secondField;

    public Timestamp getFirstField() {
        return firstField;
    }

    public void setFirstField(Timestamp firstField) {
        this.firstField = firstField;
    }

    public String getSecondField() {
        return secondField;
    }

    public void setSecondField(String secondField) {
        this.secondField = secondField;
    }
}
然后,在服务层上,我定义了接收到的实体(使用纯json)并将其转换为:

final EntityMetaData entityMetadata = getEntityMetadataByName(alias);
final T parsedEntity = getGlobalGson().fromJson(entity, entityMetadata.getEntityType());

其中,
EntityMetaData
enum
,在实体别名和类之间定义了关系。别名为
@PathVariable

Spring真正看到的是:

public class CrudController {

    @RequestMapping(method = GET)
    public Iterable<Object> findAll(@PathVariable String entity) {
    }

    @RequestMapping(value = "{id}", method = GET)
    public Object findOne(@PathVariable String entity, @PathVariable String id)     {
    }

    @RequestMapping(method = POST)
    public void save(@PathVariable String entity, @RequestBody Object body) {
    }
}
公共类CrudController{
@RequestMapping(方法=GET)
公共Iterable findAll(@PathVariable字符串实体){
}
@RequestMapping(value=“{id}”,method=GET)
公共对象findOne(@PathVariable字符串实体,@PathVariable字符串id){
}
@请求映射(方法=POST)
公共无效保存(@PathVariable字符串实体,@RequestBody对象体){
}
}
对于返回的对象,这并不重要,因为Jackson将生成正确的JSON输出,但Spring似乎无法以相同的方式处理传入的对象


你可以试着用一些超级实体来代替泛型,看看

经过长时间的研究,我发现它在Jackson中有效:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public interface ApiRequest {

}
和使用

REQUEST extends ApiRequest 
使用此选项时,不要更改MessageConverter。因此,在json请求中仍然需要额外的类信息。例如,您可以执行以下操作:

public abstract class ApiPost<REQUEST extends ApiRequest > {

    abstract protected Response post(REQUEST request) throws ErrorException;

    @ResponseBody
    @RequestMapping(method = RequestMethod.POST)
    public Response post(
            @RequestBody REQUEST request
    ) throws IOException {

        return  this.post(request);
    }

}
公共抽象类ApiPost{
抽象保护响应post(请求)抛出错误异常;
@应答器
@RequestMapping(method=RequestMethod.POST)
公众回应站(
@请求体请求请求
)抛出IOException{
返回此邮件(请求);
}
}
然后是控制器

 public class ExistApi {

        public final static String URL = "/user/exist";

        @Getter
        @Setter
        public static class Request implements ApiRequest{

            private String username;
        }

    }
@Controller
@RequestMapping(URL)
public class ExistApiController extends ApiPost<Request> {

    @Override
    protected Response post(Request request) implements ApiRequest  {
       //do something
       // and return response
    }

}
公共类ExistApi{
公共最终静态字符串URL=“/user/exist”;
@吸气剂
@塞特
公共静态类请求实现ApiRequest{
私有字符串用户名;
}
}
@控制器
@请求映射(URL)
公共类ExistaPicController扩展了ApiPost{
@凌驾
受保护的响应post(请求)实现APIRest{
//做点什么
//和返回响应
}
}
然后将请求作为{ “用户名”:xxxx, @class:“包…..请求” }

参考文献a

但对我来说,最好的解决方案不是使用spring来转换消息,而是让抽象类来做

public abstract class ApiPost<REQUEST> {

    @Autowired
    private ObjectMapper mapper;

    protected Class<REQUEST> getClazz() {
        return (Class<REQUEST>) GenericTypeResolver
                .resolveTypeArgument(getClass(), ApiPost.class);
    }

    abstract protected Response post(REQUEST request) throws ErrorException;

    @ResponseBody
    @RequestMapping(method = RequestMethod.POST)
    public Response post(
            @RequestBody REQUEST request
    ) throws IOException {

        //resolve spring generic problem
        REQUEST req = mapper.convertValue(request, getClazz());
        return  this.post(request);
    }

}
公共抽象类ApiPost{
@自动连线
私有对象映射器映射器;
受保护类getClazz(){
返回(类)GenericTypeResolver
.resolveTypeArgument(getClass(),ApiPost.class);
}
抽象保护响应post(请求)抛出错误异常;
@应答器
@RequestMapping(method=RequestMethod.POST)
公众回应站(
@请求体请求请求
)抛出IOException{
//解决spring通用问题
REQUEST req=mapper.convertValue(REQUEST,getClazz());
返回此邮件(请求);
}
}
这样,我们就不需要请求json中的ApiRequest接口和@class,而是将前端和后端解耦


请原谅我的英语不好。

Spring将为您的
CrudController
类型创建一个对象。没有其他类型信息,即
SomeSuperEntity
子类型。您希望Spring(然后是Jackson)从何处获得它?基本上,不要像使用
CrudController
那样。为您关心的每种类型创建一个专用的
@Controller
类,并使用适当的映射。这很有趣。为什么?提供常规
/animates
/animates/1
端点来获取所有动物或ID=1的任何动物,无论是狗还是猫,有什么不对?你建议强迫人们提前知道他们是要狗还是要猫,对吗?像
/动物/狗/1
。此外,如果您想获得所有动物,您需要迭代收集在一起的所有动物子类型
/animals/dogs
/animats/cats
等。我自己也在尝试解决类似的用例,并努力寻找最佳方法。@SotiriosDelimanolis目前我有约200个实体,我希望我不需要约200个控制器来执行CRUD操作。我知道存在类型擦除问题。因此,在我看来,作为一种解决方法,我可以将请求正文作为纯文本并手动解析(我可以在服务层中获取对象的类型),但我不确定这在性能和安全性方面是否是一个好主意。您解决过这个问题吗?您可能需要启用“defaultTyping”:在哪里使用T扩展APIRESQUEST?你能举个例子吗?
 public class ExistApi {

        public final static String URL = "/user/exist";

        @Getter
        @Setter
        public static class Request implements ApiRequest{

            private String username;
        }

    }
@Controller
@RequestMapping(URL)
public class ExistApiController extends ApiPost<Request> {

    @Override
    protected Response post(Request request) implements ApiRequest  {
       //do something
       // and return response
    }

}
public abstract class ApiPost<REQUEST> {

    @Autowired
    private ObjectMapper mapper;

    protected Class<REQUEST> getClazz() {
        return (Class<REQUEST>) GenericTypeResolver
                .resolveTypeArgument(getClass(), ApiPost.class);
    }

    abstract protected Response post(REQUEST request) throws ErrorException;

    @ResponseBody
    @RequestMapping(method = RequestMethod.POST)
    public Response post(
            @RequestBody REQUEST request
    ) throws IOException {

        //resolve spring generic problem
        REQUEST req = mapper.convertValue(request, getClazz());
        return  this.post(request);
    }

}