文档章节

RESTful风格的支持实践

悠悠然然
 悠悠然然
发布于 2015/03/05 12:12
字数 1903
阅读 2801
收藏 21

RESTful方式的访问确实即方便又易用,确实是非常不错的一种架构模式。前面一直嘴馋了好长时间,但是由于时间及人员关系,一直没有启动,刚开年,时间也有了,人员也到位了,没啥说的,开工!


本人的习惯,在开工之前,先去学习、研究、了解别人是怎么做的,在看了大量的文档及Roy Thomas Fielding博士的论文还有大量的实践之后,对于它是个啥,解决了啥等等有了比较充分的认识,接现来就是实现了。


程序员同学的第一想法是采用Spring中的RESTful方案来进行解决,这个当然马上被我否掉,原因是这个会导致对SpringMVC的依赖,第二会导致所有的实现都和RESTful进行硬绑定,如果有一天,出来另外一种XXXful的支持,程序员们必须要通过修改代码才能实现,这个与本人提倡的一次开发到处使用有冲突,而且会让程序员知道RESTful相关的实现细节,这明显会增加程序员的学习与开发成本。如果要修改Restful的访问方式,还要去修改相关源码,这个也太恶了,难以忍受。


那程序员同学的问题就来了,你说怎么做?


佛家对看事物有三个层次,第一层是看山是山 看水是水,第二层是看山不是山,看水不是水,第三层次是看山还是那山,看水还是那水。


所以对于RESTful这个东东,也是同样的问题,如果套用上面的3个层次来分析的话,大概是这样的:


第一层次:看山是山 看水是水,看到RESTful就是RESTful,于是就直接针对这个做了。当然这个层次比较低,我前面说的程序员同学就是这个思路,缺点上面我都已经讲过了。


第二个层次:看山不是山,看水不是水,看到RESTful的时候,隐隐约约看着,它根本就不是啥新东西,它就是我们常用的WEB访问的一种新的约定和规范,再说得直接一点,它就是一种门面,对原来的访问方式进行了一种新的组织而已。


第三个层次:看山还是那山,看水还是那水,这个时候再看RESTful,就知道它的实质了,它根本就不是个啥,它就是定义了一套新的访问规范而已。


它通过HTTP方法来声明访问方式,它通过ACCEPT来声明返回数据格式,它通过多层的URL路径来传一些参数。


它和具体的代码与控制层实现逻辑有1毛钱关系么??从本来来说是没有的。因此,通过上面的抽象,我们可以认为,RESTful架构实际上可以认为就是一系列的URL地址的进行转换规则,它通过预定义好的规则,把RESTful格式的请求转换成我们原来的已经开发好的一些访问地址就可以了,其它的就该咋咋地好了。


通过上面的讲解,程序员同学似乎懂了我说的意思,于是去噼里啪啦敲代码了,半天以后,代码出来了,大致如下:

规则配置

转换规则定义:


<rules>
    <rule pattern="/users/{id}">
        <mapping method="get" accept="text/html"
                     url="queryUsersTiny.servicepage"></mapping>
        <mapping method="post" accept="text/html"
                     url="addUserTiny.servicepage"></mapping>
        <mapping method="put" accept="text/html"
                     url="updateUserTiny.servicepage"></mapping>
        <mapping method="delete" accept="text/html"
                     url="deleteUserTiny.servicepage"></mapping>
        <mapping method="get" accept="text/json"
                     url="queryUsersTiny.servicejson"></mapping>
    </rule>
    <rule pattern="/users/new/">
        <mapping method="get" accept="text/html"
                     url="crud/restful/operate.page"></mapping>
    </rule>
    <rule pattern="/users/edit/{id}">
        <mapping method="get" accept="text/html"
                     url="queryUserByIdTiny.servicepage"></mapping>
    </rule>
    <rule pattern="/users/{id}/classes/{name}">
        <mapping method="post" accept="text/html"
                     url="queryclasses.servicepage"></mapping>
    </rule>
</rules>


  
  
说明:


rules下面放置一系列的url转换规则rule。

rule表示一条转换规则
pattern:属性是一条匹配表达式,表示这种类型的URL地址是归我处理的,它下面可以有占位符,来表示要提取一些值
在它下面呢,有多条的mapping 子规则,mapping 中有如下属性:
method:响应的请求方法
accept:响应的返回类型
url:真正执行的URL

管理器

管理器就是个规则仓库,里面放置了所有的规则。


public interface UrlRestfulManager {
	
	String URL_RESTFUL_XSTREAM ="urlrestful";
	
	/**
	 * 增加restful配置信息
	 * @param Rules
	 */
	public void addRules(Rules Rules);
	/**
	 * 移除restful配置信息
	 * @param Rules
	 */
	public void removeRules(Rules Rules);
	
	/**
	 * 根据请求路径、请求的方法以及请求头的accept 组装此次请求的上下文对象
	 * @param requestPath
	 * @param httpMethod
	 * @param accept
	 * @return
	 */
	public Context getContext(String requestPath, String httpMethod, String accept);
}



public class UrlRestfulFileProcessor extends AbstractFileProcessor {

	private static final String RESTFUL_EXT_FILENAME = ".restful.xml";

	private UrlRestfulManager urlRestfulManager;

	public boolean isMatch(FileObject fileObject) {
		return fileObject.getFileName().toLowerCase()
				.endsWith(RESTFUL_EXT_FILENAME);
	}

	public void setUrlRestfulManager(UrlRestfulManager urlRestfulManager) {
		this.urlRestfulManager = urlRestfulManager;
	}

	public void process() {
		XStream stream = XStreamFactory
				.getXStream(UrlRestfulManager.URL_RESTFUL_XSTREAM);
		for (FileObject fileObject : deleteList) {
			logger.logMessage(LogLevel.INFO, "正在移除restful文件[{0}]",
					fileObject.getAbsolutePath());
			Rules Rules = (Rules) caches.get(fileObject
					.getAbsolutePath());
			if (Rules != null) {
				urlRestfulManager.removeRules(Rules);
				caches.remove(fileObject.getAbsolutePath());
			}
			logger.logMessage(LogLevel.INFO, "移除restful文件[{0}]结束",
					fileObject.getAbsolutePath());
		}
		for (FileObject fileObject : changeList) {
			logger.logMessage(LogLevel.INFO, "正在加载restful文件[{0}]",
					fileObject.getAbsolutePath());
			Rules Rules = (Rules) stream.fromXML(fileObject
					.getInputStream());
			Rules oldRules = (Rules) caches.get(fileObject
					.getAbsolutePath());
			if (oldRules != null) {
				urlRestfulManager.removeRules(oldRules);
			}
			urlRestfulManager.addRules(Rules);
			caches.put(fileObject.getAbsolutePath(), Rules);
			logger.logMessage(LogLevel.INFO, "加载restful文件[{0}]结束",
					fileObject.getAbsolutePath());
		}
	}
}

这个类用于匹配所有的RESTful规则文件并添加到管理器。

这样就可以进行良好的模块化,把各模块中的配置文件统一加载起来,统一使用。易于开发,自动集成。

URL重写处理

public class RestfulStyleSubstitutionHandler implements
        RewriteSubstitutionHandler {

    private static final String Accept = "Accept";
    private static final String HTTP_METHOD_KEY = "X-HTTP-METHOD-OVERRIDE";

    private UrlRestfulManager urlRestfulManager;

    public void setUrlRestfulManager(UrlRestfulManager urlRestfulManager) {
        this.urlRestfulManager = urlRestfulManager;
    }

    /**
     * 先获取原来的请求路径与UrlRestful的配置进行匹配,把匹配的值放到上下文中,最后重新设置请求的路径。
     */
    public void postSubstitution(RewriteSubstitutionContext context) {
        String originalPath = context.getPath();
        String httpMethod = getHttpMethod(context);
        String requestAccept = context.getParserWebContext().get(Accept);
        Context restfulContext = urlRestfulManager
                .getContext(originalPath, httpMethod, requestAccept);
        if (restfulContext != null) {
            ParameterParser parameterParser = context.getParameters();
            setParameter(parameterParser, restfulContext.getVariableMap());
            context.setPath(restfulContext.getMappingUrl());
        }

    }

    private String getHttpMethod(RewriteSubstitutionContext context) {
        WebContext webContext = context.getParserWebContext();
        String httpMethod = webContext.get(HTTP_METHOD_KEY);
        if (StringUtil.isBlank(httpMethod)) {
            httpMethod = webContext.getRequest().getMethod();
        }
        return httpMethod;
    }

    private void setParameter(ParameterParser parameterParser,
                              Map<String, String> variableMap) {
        if (!CollectionUtil.isEmpty(variableMap)) {
            for (String key : variableMap.keySet()) {
                String value = variableMap.get(key);
                if (!StringUtil.isBlank(value)) {
                    String[] parameterValues = value.split(",");
                    if (parameterValues.length > 1) {
                        parameterParser.setObjects(key, parameterValues);
                    } else {
                        parameterParser.setObject(key, value);
                    }
                }
            }
        }
    }
}
这里的逻辑很简单,得到访问路径然后到管理器中查找看是否有对应的,如果有则进行rewrite处理。

这里有个处理逻辑是:X-HTTP-METHOD-OVERRIDE参数,这个参数可以通过放在HEADER、REQUEST等地方来声明请求类型,如果这个值有,则采用声明的方法,否则用HTTP请求的方法来进行处理,这就允许通过一个POST方法来处理所有的请求方式。推荐采用放在HEADER来进行声明的。

代码行统计

连接口带配置带实现,总结代码行数480行(ValueParser现在还未使用)。

总结

自此,已经完成了对Tiny框架的扩展,支持了RESTful访问方式完美支持,至于如何定义访问格式及路径层次结构规划,那是技术经理和开发人员的事情。对于已经使用Tiny框架的项目,只要增加新的依赖,然后增加配置就可以对外提供RESTful的访问支持了。

对于非Tiny项目,也可以采用上面的开发思路,来做自己的RESTful风格访问支持。

功能特点:对原有实现无任何侵入性(重定向地址要转向RESTful格式的新地址)。

上面的分析及解决方案如有不对或不妥之处,欢迎批评指正。

© 著作权归作者所有

悠悠然然

悠悠然然

粉丝 2471
博文 185
码字总数 363071
作品 14
杭州
架构师
私信 提问
加载中

评论(15)

悠悠然然
悠悠然然 博主

引用来自“JPomichael”的评论

对于RESTful风格 建议在增删改查上 尽量避免使用动词命名api路径 比如看到你这里的 new ,edit ,delete这些不要在路径上暴露出来
使用HTTP 方法 get post put delete 分别对应 获取,新增,修改 ,删除
正解,谢谢
JPomichael
JPomichael
对于RESTful风格 建议在增删改查上 尽量避免使用动词命名api路径 比如看到你这里的 new ,edit ,delete这些不要在路径上暴露出来
使用HTTP 方法 get post put delete 分别对应 获取,新增,修改 ,删除
悠悠然然
悠悠然然 博主

引用来自“哈库纳”的评论

维护配置文件头疼哦,虽然一次开发到处运行。
但是开发需要读懂逻辑的成本搞了。
这玩意儿,需要的话多少都不多,不需要的话,一份就多了。
哈库纳
哈库纳
维护配置文件头疼哦,虽然一次开发到处运行。
但是开发需要读懂逻辑的成本搞了。
xtgss007
xtgss007

引用来自“xtgss007”的评论

我刚看了下源码,这样的解析会随着url的增加,解析url的时间也会增加的,更不用说逻辑的处理时间了。最近也在研究restful,就是苦于解析url的机制。

引用来自“悠悠然然”的评论

你觉得我们在web.xml当中配的一些规则是怎么匹配的?或者你觉得Spring当中RESTful当中是如何匹配的?
我看到了rules.xml中的规则配置,spring-restful的源码还没有看, 愿闻你们在web.mxl中的配置规则
悠悠然然
悠悠然然 博主

引用来自“xtgss007”的评论

我刚看了下源码,这样的解析会随着url的增加,解析url的时间也会增加的,更不用说逻辑的处理时间了。最近也在研究restful,就是苦于解析url的机制。
你觉得我们在web.xml当中配的一些规则是怎么匹配的?或者你觉得Spring当中RESTful当中是如何匹配的?
xtgss007
xtgss007
我刚看了下源码,这样的解析会随着url的增加,解析url的时间也会增加的,更不用说逻辑的处理时间了。最近也在研究restful,就是苦于解析url的机制。
悠悠然然
悠悠然然 博主

引用来自“何顺wq”的评论

大神的思路非常棒!让我去写的话肯定也是程序员的方式来做。
谢谢,能理解的都是大神。
何顺wq
何顺wq
大神的思路非常棒!让我去写的话肯定也是程序员的方式来做。
悠悠然然
悠悠然然 博主

引用来自“山哥”的评论

我靠,一个 RESTful 支持要这么复杂,还有规则配置文件,我是汗了啊
呵呵,有一种抱怨叫不懂
json-server的实践与自定义配置化

背景 之前做项目都没有用到服务,都是等后端给接口字段或者前端留空位;但新公司的项目需要搭建服务,本想把旧项目的搬过来就好了,不过发现添加一个 mock api 步骤过于繁复,服务本应简单为...

天驱丶
2018/05/25
0
0
RESTful APIs设计指南

接口设计的时候最好都使用RESTful风格,下面是一些资料: RESTful API 设计指南 RESTful API 设计最佳实践 RESTful_APIs.pdf Laravel的RESTfulAPI最佳实践 REST实践指南.pdf...

极_度
2015/03/04
10
0
RESTful API 设计参考文献--restful-api-design-references

restful-api-design-references是RESTful API 设计参考文献列表,可帮助你更加彻底的了解REST风格的接口设计。 RESTful 介绍及设计思路 Principles of good RESTful API Design(译:好 REST...

匿名
2016/09/12
1K
2
PHP实现Restful风格的API

Restful是一种设计风格而不是标准,比如一个接口原本是这样的: http://www.test.com/user/view/id/1 表示获取id为1的用户信息,如果使用Restful风格,可以变成这样: http://www.test.com/...

crazymus
2015/10/24
1K
0
Spring Boot 中使用 Java API 调用 Elasticsearch

ElasticSearch 是一个高可用开源全文检索和分析组件。提供存储服务,搜索服务,大数据准实时分析等。一般用于提供一些提供复杂搜索的应用。 ElasticSearch 提供了一套基于restful风格的全文检...

PengLei
2017/11/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Qt程序打包发布方法(使用官方提供的windeployqt工具)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/toTheUnknown/article/details/81748179 如果使用到了Qt ...

shzwork
41分钟前
7
0
MainThreadSupport

MainThreadSupport EventBus 3.0 中的代码片段. org.greenrobot.eventbus.MainThreadSupport 定义一个接口,并给出默认实现类. 调用者可以在EventBus的构建者中替换该实现. public interface ...

马湖村第九后羿
今天
3
0
指定要使用的形状来代替文字的显示

控制手机键盘弹出的功能只能在ios上实现,安卓是实现不了的,所以安卓只能使用type类型来控制键盘类型,例如你要弹出数字键盘就使用type="number",如果要弹出电话键盘就使用type="tel",但这...

前端老手
今天
6
0
总结:Raft协议

一、Raft协议是什么? 分布式一致性算法。即解决分布式系统中各个副本数据一致性问题。 二、Raft的日志广播过程 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。 Followers收...

浮躁的码农
今天
7
0
Flask-admin Model View字段介绍

Model View字段介绍 can_create = True 是否可以创建can_edit = True 是否可以编辑can_delete = True 是否可以删除list_template = 'admin/model/list.html' 修改显......

dillonxiao
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部