简体   繁体   English

接受两个具有相同@RequestMapping 的不同子类

[英]Accept two different subclasses with same @RequestMapping

I have WebDeviceInfo and IOSDeviceInfo classes that are subclasses of DeviceInfo .我有WebDeviceInfoIOSDeviceInfo类,它们是DeviceInfo子类。 How can I create a single endpoint in a Spring @RestController that will accept either IOSDeviceInfo or WebDeviceInfo ?如何在接受IOSDeviceInfoWebDeviceInfo的 Spring @RestController中创建单个端点?

Attempt #1尝试 #1

I tried to map the same RequestMapping to two different methods, one that would get called if the RequestBody could be mapped to a WebDeviceInfo and the other that would get called if the RequestBody could be mapped to a IOSDeviceInfo .我尝试将相同的RequestMapping映射到两种不同的方法,一种在RequestBody可以映射到WebDeviceInfo被调用,另一种在RequestBody可以映射到IOSDeviceInfoIOSDeviceInfo

@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerWebDevice(@RequestBody final WebDeviceInfo webDeviceInfo) {
    //register web device
}

@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerIOSDevice(@RequestBody final IOSDeviceInfo iosDeviceInfo) {
    //register ios device
}

But this does not work, the second RequestMapping does not get registered and the application fails to start up because Spring sees that /register-device with the same RequestMethod and MediaType is already mapped to another method.但这不起作用,第二个RequestMapping没有注册并且应用程序无法启动,因为 Spring 看到具有相同RequestMethodMediaType /register-device已经映射到另一个方法。

Attempt #2尝试#2

Next, I tried accepting the superclass as the RequestBody and then casting it to the appropriate subclass.接下来,我尝试接受超类作为RequestBody ,然后将其转换为适当的子类。

@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
    if (deviceInfo instanceof WebDeviceInfo) {
        final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo;
        //register web device
    } else if (deviceInfo instanceof IOSDeviceInfo) {
        final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo;
        //register ios device
    } else {
        logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
    }
}

This does not work either.这也不起作用。 I always get:我总是得到:

Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo无法将 deviceInfo 转换为WebDeviceInfoIOSDeviceInfo

Attempt #3尝试 #3

Finally, I tried just casting to the correct subclass inside a try / catch .最后,我尝试在try / catch强制转换为正确的子类。

@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
    try {
        final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo);
        //register web device
    } catch (final ClassCastException ex) {
        try {
            final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo);
            //register ios device
        } catch (final ClassCastException ex2) {
            logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
        }
    }
}

Again I get error:我再次收到错误:

Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo无法将 deviceInfo 转换为WebDeviceInfoIOSDeviceInfo

Is there any way to accomplish this, or am I going to have to create two separate methods with two different RequestMapping s?有什么办法可以做到这一点,还是我必须用两个不同的RequestMapping创建两个单独的方法?

Attempts #2 and #3 should work when you annotate the base class DeviceInfo with the correct Jackson annotations:当您使用正确的 Jackson 注释对基类DeviceInfo进行注释时,尝试 #2 和 #3 应该可以工作:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = IOSDeviceInfo.class, name = "ios"),
        @JsonSubTypes.Type(value = WebDeviceInfo.class, name = "web")
})
public abstract class DeviceInfo {
    [...]
}

class IOSDeviceInfo extends DeviceInfo {
    [...]
}

class WebDeviceInfo extends DeviceInfo {
    [...]
}

Then when you receive a request, the body will be deserialized into the correct subclass, either a IOSDeviceInfo or a WebDeviceInfo, depending on the 'type' parameter in the JSON body:然后,当您收到请求时,主体将被反序列化为正确的子类,即 IOSDeviceInfo 或 WebDeviceInfo,具体取决于 JSON 主体中的“type”参数:

{
  type : "ios",
  [...]
}

Now you only need a single @RequestMapping method:现在你只需要一个 @RequestMapping 方法:

@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
    if (deviceInfo instanceof WebDeviceInfo) {
        final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo;
        //register web device
    } else if (deviceInfo instanceof IOSDeviceInfo) {
        final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo;
        //register ios device
    } else {
        logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
    }
}

You can use @PathVariable to the parameter to define which type you need to deserialize 您可以对参数使用@PathVariable来定义需要反序列化的类型

public void registerDevice(@PathVariable("deviceType") String deviceType, @RequestBody final DeviceInfo deviceInfo) {

switch (deviceType){
...
}

You can use the same @RequestMapping , however, you still need something to differentiate them. 您可以使用相同的@RequestMapping ,但是,仍然需要一些区别它们。 By same, I mean the same name, path, and RequestMethod. 同样,我的意思是相同的名称,路径和RequestMethod。

You can use the params argument in request mapping to differentiate them. 您可以在请求映射中使用params参数来区分它们。

For example, 例如,

@RequestMapping(value = "/register-device", 
    method = RequestMethod.POST, 
    consumes = MediaType.APPLICATION_JSON_VALUE, 
    params="device=android")
public void registerDevice(@RequestBody final WebDeviceInfo deviceInfo){

}


@RequestMapping(value = "/register-device", 
    method = RequestMethod.POST, 
    consumes = MediaType.APPLICATION_JSON_VALUE,    
    params="device=ios")
public void registerDevice(@RequestBody final IOSDeviceInfo deviceInfo){

}

Then just add an extra parameter to the request with the key "device", and Spring will resolve the correct method. 然后只需使用键“ device”向请求添加一个额外的参数,Spring就会解析正确的方法。

eg, if this were your original payload: 例如,如果这是您的原始有效载荷:

{
   deviceInfo : {..}
}

it would now be this: 现在是这样的:

{
    device : "ios",
    deviceInfo : {..}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM