最小成本的实现服务接口的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或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
一灰灰blog
Be the first person to leave a comment!