0

I am developing API to front end, but many APIs are similar, e.g:

GET foos
GET foos/{id}
POST foos
PUT foos
DELETE foos/{id}

GET bars
GET bars/{id}
POST bars
PUT bars
DELETE bars/{id}

I want to put any common logic in BaseController to reduce developing work, e.g:

public abstract class BaseController<V> {
    @GetMapping("/{id}")
    @ApiOperation(value = "Get detail info", response = FooVO.class)
    protected ApiResult detail(@PathVariable String id) {
        V v = getService().getById(id);
        return ApiResult.success(v);
    }
}

but I also want to support many concrete Controllers:

public class FooController extends BaseController<FooVO> 
public class BarController extends BaseController<BarVO> 
...

So response class should be dynamically map to generic V

    @ApiOperation(value = "Get detail info", response = FooVO.class)
==>
    @ApiOperation(value = "Get detail info", response = V.class)

but it does not compile.

I also tried below way, still failed to compile

protected abstract Class<V> getClazz();

@ApiOperation(value = "Get detail info", response = getClazz())

So any other manner could solve this problem?

pirho
  • 11,565
  • 12
  • 43
  • 70
zhuguowei
  • 8,401
  • 16
  • 70
  • 106

2 Answers2

1

Annotation values must be constants so the response values can not be generic because the actual is not known at compile time and not constant. Maybe there are some options to get around this restriction, like another answer here but not something that makes things more easy.

Not a perfect solution but maybe the best option you can do might be to separate the logic from base controller EP and let inheriting class to populate the annotation. So something like in your base class:

public abstract class BaseController<V> {
    protected ApiResult detail(String id) {
        V v = getService().getById(id);
        return ApiResult.success(v);
    }
}

and in inheriting controller:

public class FooController<FooVO> {
    @GetMapping("/{id}")
    @ApiOperation(value = "Get detail info", response = FooVO.class)
    @Override
    protected ApiResult detail(@PathVariable String id) {
        return super(id);
    }
}

NOTE: this example is of course bad because there is no actual need for generics or base class with this single method. The service could be injected and called straight without invoking super. But of course if there are some other things you need handle with generics - in the controller level in your methods - this is the next best alternative.

pirho
  • 11,565
  • 12
  • 43
  • 70
0

If you real concern is having similar API endpoints, you can try out Spring Data Rest API. This solves the repetitive work we make in Controllers.

Can refer below link:

https://www.baeldung.com/spring-data-rest-intro

karthick M
  • 192
  • 1
  • 13