最小成本的实现服务接口的rest支持,主要借助RequestMappingHandlerMapping来实现自定义的请求映射
1. 场景说明
如何最小成本的为一个非web服务提供rest接口?
什么场景会有上面这种需求场景呢,最近正好遇到了。我们有一个服务,本来是提供gprc的服务接口,结果因为某些原因,现在要提供http接口访问,难不成针对所有的Service都重新写一个对应的RestController,然后再做一层转发么
如果真这样,也着实有点蛋疼,那么有没有什么简单的方式来实现呢?
- 一个拦截器,解析url,通过反射的方式调用
- 借助
RequestMappingHandlerMapping来实现扩展
接下来将主要介绍第二种方案,自定义url映射规则
2. 项目环境
本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA + jdk1.8进行开发
pom核心依赖,web包即可
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
|
3. 设计方案
实现面向接口的REST服务扩展,通过自定义注解 RestService 来扫描哪些API需要生成对应的rest服务
url生成规则
- 只支持POST请求
- url:
service/method
- 参数: 表单方式提交
举例说明
1 2 3 4 5 6 7 8 9 10 11
| @RestService public interface UserApi {
String getName(int id); }
|
上面这个API生成的url访问姿势如下
1
| curl -X POST 'http://127.0.0.1:8080/UserApi/getName' -d 'id=111'
|
4. 实现
整体的实现过程相对简单,两个核心点
4.1 注解定义
自定义一个注解,用于表明哪些接口需要生成REST服务,特别注意,这个注解上还有@RestController,不能少
1 2 3 4 5 6
| @RestController @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface RestService { }
|
4.2 url映射
核心点,覆盖getMappingForMethod,找到当前类or父类上有RestService注解的目标类,然后生成RequestMappingInfo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class RestAdapterHandlerMapping extends RequestMappingHandlerMapping { @Override public Map<RequestMappingInfo, HandlerMethod> getHandlerMethods() { return super.getHandlerMethods(); }
@Override public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) { super.registerMapping(mapping, handler, method); }
@Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { System.out.println("current handlerType: " + handlerType.getName()); Class target = getMappingClass(handlerType); if (target == null) { return super.getMappingForMethod(method, handlerType); }
String prefix = target.getSimpleName(); RequestMappingInfo info = createRequestMappingInfo(method, prefix, false); if (info != null) { return RequestMappingInfo.paths(prefix).build().combine(info); } return super.getMappingForMethod(method, handlerType); }
private Class getMappingClass(Class<?> handlerType) { if (handlerType.isAnnotationPresent(RestService.class)) { return handlerType; }
for (Class clz : handlerType.getInterfaces()) { if (clz.isAnnotationPresent(RestService.class)) { return clz; } } return null; }
protected RequestMappingInfo createRequestMappingInfo(Method method, String prefix, boolean get) { RequestMappingInfo.Builder builder = RequestMappingInfo.paths(method.getName()).methods(get ? RequestMethod.GET : RequestMethod.POST); System.out.println("support url: " + prefix + "/" + method.getName() + (get ? "-X GET" : "-X POST")); return builder.build(); } }
|
4.3 注册
上面两个就是最核心的地方了,接下来注册一下就完全可用了
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration @SpringBootApplication public class Application implements WebMvcRegistrations {
@Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new RestAdapterHandlerMapping(); }
public static void main(String[] args) { SpringApplication.run(Application.class); } }
|
5. 测试
接下来测试一下是否能完成我们的目标
一个接口,一个实现Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @RestService public interface UserApi {
String getName(int id);
String updateName(String user, int age); }
@Service public class UserApiImpl implements UserApi { Map<String, Integer> cache = new ConcurrentHashMap<>();
@Override public String getName(int id) { return "一灰灰blog : " + id; }
@Override public String updateName(String user, int age) { if (cache.containsKey(user)) { Integer old = cache.put(user, age); return "update " + user + " old:" + old + " to:" + age; }
cache.put(user, age); return "add new: " + user + "| " + age; } }
|
启动之后测试一下
1 2 3
| $ curl -X POST 'http://127.0.0.1:8080/UserApi/updateName' -d 'user=一灰灰Blog&age=18'
add new: 一灰灰Blog| 18
|
II. 其他
0. 项目
1. 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏