`

Rop开发手册(2):最简单的服务开放平台框架

阅读更多
引用
Rop项目文档前后写了一个多月,在写的过程中发现很多功能不完善的,又回过头了改项目,此种反复多次,现在Rop开源项目的文档已经差不多了,将陆续在iteye此发出,希望对大家有帮助。在Rop项目的开发过程中得到了众多iteye网友的有力帮助,收到了很多很好的整改的意见,在此对他们的奉献和帮助表示诚挚的感谢,这些网友包括但不限于:
melin
kellokitty
風一樣的男子
lioliang
木木的爱情童话
devotionalist
ericliang
 


目录
1.快速了解Rop
2.请求服务模型
3.应用授权及验证
4.服务会话管理
5.错误处理模型
6.响应报文控制
7.文件上传
8.服务安全控制
9.拦截器及事件体系
10.性能调优
11.开发客户端SDK
12.参考资料

传统Web Service请求模型

请求模型设计的好坏将直接影响服务调用的难易程度,设计良好的请求模型可以让服务调用成为随时随地、信手拈来的事。此外,还能使服务接口清晰化,降低开发者理解服务的难度。我们先来了解一下传统Web Service的请求模型。

SOAP请求模型


Web Service基本上都是使用HTTP传输协议进行交互的,服务的响应报文一般支持XML和JSON两种格式,但Web Service服务请求模型却各有千秋。
传统的Web Service采用SOAP请求报文,任何服务都对应一组SOAP请求/响应报文,服务的调用及报文解析都比较麻烦。举例来说,即使是调用一个诸如查看当天天气的简单服务,该服务仅有一个city的参数,在SOAP的世界里,您也必须将其封装成一个复杂的SOAP请求报文才行。一般情况下,不借助CXF、Axis这类框架你很难访问SOAP。

REST请求模型


但是,很多情况下,开发者往往希望自由地随时随地访问服务,比如,通过一个形如
引用
http://www.xxx.com/weather/{city}

的URL就可以访问服务获取响应。把服务看成一个类似于文档、图片式的普通资源,通过一个唯一的URL进行定位和调用――这就是现在方兴未艾的REST Web Service的中心思想。
REST Web Service充分挖掘了HTTP通讯协议的内涵,借助HTTP方法(如GET、POST、PUT、DELETE等)及合理设计的服务URL,让Web Service达到不言自明的效果。

豆瓣网的API就是采用标准的REST Web Service开发的,来看一个获取图书信息的API:

引用
http://api.douban.com/book/subject/isbn/{isbnID}


该服务使用HTTP的GET方式调用,说白了就是您可以简单地在浏览器地址栏中敲入以下URL,就可以发起服务调用:

引用
http://api.douban.com/book/subject/isbn/9787508630069


以上请求将获得《史蒂夫•乔布斯传》这本书的服务响应报文,它是一个XML报文,您既可以在浏览器中预览,也可以写一个程序消费这个响应报文,完成您要干的事情。这种服务调用方式,对于服务调用者非常亲切,因为它和访问一个网页并无二致。如果要学习REST Web Service的设计,豆瓣网的API就是不错的学习案例,我们来欣赏一下豆瓣网其它几个API:

  • GET http://api.douban.com/movie/subject/{subjectID}:获取某个专题的信息,GET表示使用HTTP请求方法,下同;
  • GET http://api.douban.com/people/{userID}:获取某个用户的信息;
  • GET http://api.douban.com/people?q=douban&start-index=10&max-results=5:搜索用户,用户名通过q参数传递,其它两个参数是分页控制参数;
  • DELETE http://api.douban.com/review/{reviewID}:删除某篇评论。


采用REST请求模型发布的服务接口很清晰化、调用也很简单,REST服务已经模糊了服务和网页资源的界限。简单就是最好的,从这个意义上说REST确实优于SOAP,开发者也纷纷用脚做出了投票,弃SOAP之暗而投REST之明。
REST在扛起挑战SOAP大旗时,对SOAP的战斗檄文是:复杂,笨重,EJB死灰复燃。但是,当REST得天下后,我们发现REST本身也存在一些刻板的东西。

首先,经典的REST对HTTP请求方法的使用过于教条化:新增、更改、删除、获取资源的服务分别对应POST、PUT、DELETE和GET的HTTP请求方法。一般的Web服务器和浏览器都只支持GET和POST这两种HTTP请求方法,所以在实际应用中,REST希望充分挖掘HTTP请求方法能力的倡议遭遇了困难。

其次,REST提倡为每个服务设计一个“达意”的URL,让服务的URL望文生义。从可读性,清晰化的角度上看,REST的这个建议是非常值得称赞的。但是,服务的消费者主体是程序,让每个服务对应不同的URL,反而让客户端程序不好写。

综上所述,当前如日中天的REST Web Service自身也存在一些待改进的地方。淘宝的TOP的请求模型可以看成是REST的变体,首先,TOP提供的所有服务的URL都是一样的:即为http://gw.api.taobao.com/router/rest,使用method参数指定服务API名称,再通过其它参数指定服务的入参。由于平台所有服务的URL都相同,不同的服务方法通过method参数区分,反而让服务的调度变得简单了。

Rop请求模型

Rop请求模型的设计直接借鉴了TOP的思想,服务开放平台的所有服务URL是相同的,请求参数分为系统级参数和业务级参数两部分,系统级参数是所有服务API都拥有的参数,而业务级参数由具体服务API定义。

统一服务URL

采用Rop的服务开放平台,其所有的服务都使用统一的URL,Rop通过method系统级参数将请求路由到指定的服务方法中完成服务受理。如何设置这个统一的服务URL呢?答案很简单,即是通过RopServlet的<servlet-mapping>进行定义。

服务平台最终的URL为:<开放平台根URL>/<RopServlet的映射URI>。举例来说,服务器URL为api.xxx.com,而RopServlet的映射URI为/router,则服务统一URL为:

引用
http://api.xxx.com/router。


系统级参数

系统级参数是由开放平台定义的一组参数,每个服务都拥有这些参数,用以传送框架级的参数信息。如我们前面提到的method就是一个系统级参数,使用该参数指定服务的名称。Rop共有7个系统级参数,在下表中说明:

参数名称 是否必须 参数说明
appKey 应用键,开放平台用以确定客户端应用的身份,如000001,000002等。应用键对应一个密钥secret。要基于服务平台开发应用,必须事先通过申请获取appKey/secret后,才能进行应用的开发。
sessionId 会话ID,一般是一个36位的UUID,在登录服务平台后获取;
method 服务方法名,一般采用“名词+动词”的结构定义。如user.get、user.create等;
v 服务方法的版本号,如1.0、2.0等。一个具体的服务方法上method+v两者唯一确定。因此服务平台必须保证所有服务的method+v的唯一性。
format 通信报文格式,可选值为xml和json,默认为xml。
locale 本地化类型,默认为zh_CN。
sign 签名串,请求参数的签名,服务平台通过它验证请求数据的合法性。


locale、format这两个系统级参数的功用是不言自明的,而其它的系统级参数由于涉及到服务开放平台很多的领域性问题,需要一些背景知识的铺垫,因此我们将在后续内容中进行专门的介绍。

默认情况下,系统级参数名是固定的,一般情况下,并不需要对调整它。如果希望使用自行定义的参数名称,可以使用<rop:sysparams/>进行定义,如下所示:

sampleRopApplicationContext.xml:定义系统级参数名
<rop:sysparams 
            format-param-name="messageFormat"
            appkey-param-name="app_key"/>


业务级参数

业务级参数,顾名思义是由业务逻辑需要自行定义的,每个服务API都可以定义若干个自己的业务级参数。Rop根据参数名和RopRequest类属性名相等的契约,将业务级参数绑定到RopRequest中。

如LogonRequest定义了两个userName和password两个属性,Rop就会将HTTP请求参数值绑定到LogonRequest对象的同名属性中。

参数数据绑定与验证

参数数据绑定


当客户端调用服务平台某个服务时,其实质是向服务平台的URL发送若干个请求参数(包括系统级和业务级的参数)。Rop框架在接收到这些请求参数后,就会将其绑定到RopRequest请求对象中,服务方法可通过这个RopRequest对象获取请求参数信息,进而执行相应的服务API并返回响应结果。下图描述了请求参数的转换过程:


图1



首先,客户端的服务请求通过HTTP报文发送给服务端的Servlet服务器(即HTTP服务器),Servlet服务器将HTTP报文转换成一个HttpServletRequest对象。然后通过RopServlet转交给Rop框架,Rop框架将HttpServletRequest转换成一个RopRequestContext对象。接着,ServiceRouter将RopRequestContext传给ServiceMethodAdapter,ServiceMethodAdapter在内部将RopRequestContext转换成RopRequest对象,输送给最终的服务方法。
从上面的数据转换过程中,我们知道每当客户端发起一个服务调用时,Rop都会在内部创建一个RopRequestContext实例,它包含了所有的请求数据信息。

下面,我们来了解一下RopRequestContext接口的方法:
  • String getAppKey():获取appKey系统级参数的值。RopRequestContext为每个系统级参数都分配了一个对应的接口方法,如String getMethod()、String getSessionId()等;
  • HttpAction getHttpAction():获取HTTP请求方法,HttpAction是一个枚举,仅有两个枚举值,即GET和POST。这也说明,Rop仅支持GET和POST两个HTTP请求方法;
  • String getIp():获取请求来源的IP地址。由于在集群环境下,请求通过前端的负载均衡器再传给后端集群的某个具体服务节点。因此,直接使用ServletRequest#getRemoteAddr()返回的值将是前端负载均衡服务器的IP,在此Rop使用了一些技巧,以保证后端服务获取的IP是客户端的IP。具体实现可以参见com.rop.ServletRequestContextBuilder#getRemoteAddr(HttpServletRequest request)的实现;
  • Object getRawRequestObject():获取原请求对象,即服务请求对应的HttpServletRequest对象;
  • Map<String, String> getAllParams():获取服务请求所对应的所有请求参数。可以通过String getParamValue(String paramName)获取某个具体参数的值;
  • RopContext getRopContext():获取Rop框架上下文的信息。RopContext之于Rop框架相当于ServletContext之于Servlet容器,它包含了很多Rop框架的运行期信息,所有Rop的服务方法都注册在RopContext 中。


概括来说,RopRequestContext为每个系统级参数都提供了一个方法,如getAppKey()、getMethod()等。对于业务级参数,则可以使用RopRequestContext的getParamValue("<参数名>")获取。此外,RopRequestContext还提供了获取原始请求对象、客户端IP等方法。

所有服务方法的入参都是RopRequest接口或其实现类,RopRequest接口仅有一个方法:

RopRequestContext getRopRequestContext();

RopRequest的实现类负责定义业务级参数对应的属性,这样,在服务方法内部,就可以通过RopRequest#getRopRequestContext()获取RopRequestContext,再通过RopRequestContext访问到系统级参数了。而业务级参数是可通过RopRequest实现类的属性获取。

在下面的getSession()服务方法中,我们定义了一个LogonRequest的请求对象,它就是一个实现了RopRequest接口的对象,在服务方法内部,可以通过LogonRequest访问到系统级参数、业务级参数及其它相关的信息,如下所示:

UserService.java
@ServiceMethod(method = "user.getSession",needInSession = NeedInSessionType.NO) 
public RopResponse getSession(LogonRequest request) {

//①访问系统级参数
        String appKey = request.getRopRequestContext().getAppKey();

//②-1 访问业务级参数:通过类属性
        String userName1 = request.getUserName();
//②-2 访问业务级参数:通过RopRequestContext获取
        String userName2 = request.getRopRequestContext().getParamValue("userName");

//③获取其它信息
        String ip = request.getRopRequestContext().getIp();
}


通过上面的实例,我们可以知道通过RopRequest可以很方便地获取系统级参数、业务级参数及客户端的相关信息。

参数数据验证

由于应用客户端和服务平台都是服务报文进行通信的,所有的请求参数都以字符串的形式传送过来。为了保证服务得到正确执行,必须事先对请求参数进行数据合法性验证,只有在服务请求所有参数都符合约定的情况下,服务平台才执行具体的服务操作,否则直接驳回请求,返回错误的报文。

数据校验从责任主体上看,可分为客户端校验和服务端校验两种。对于一个封闭式的应用软件来说,由于服务端和客户端都是一体化开发的,为了减少开发工作量,有时仅需要进行客户端校验就可以了。但是,服务开放平台的潜在调用者是不受限的,应用开发者可基于服务平台开发出众多丰富多彩的应用,在这种场景下,服务端校验是必不可少的。

服务请求参数的校验是开放平台的一项重要的基础功能,Rop和Spring MVC一样使用JSR 303注解定义参数的校验规则,当请求数据违反校验规则时,直接返回对应的错误报文,只有所有请求参数都通过合法性验证后,才调用目标服务方法。

下面的CreateUserRequest使用了JSR 303注解的,来看一下具体的使用方法:

CreateUserRequest.java:使用JSR 303对业务级参数进行校验
public class CreateUserRequest extends AbstractRopRequest {

    @Pattern(regexp = "\\w{4,30}")
    private String userName;

    @IgnoreSign
    @Pattern(regexp = "\\w{6,30}")
    private String password;

    @DecimalMin("1000.00")
    @DecimalMax("100000.00")
    @NumberFormat(pattern = "#,###.##")
    private long salary;

…
}


对于系统级的参数,Rop本身会负责校验,开发者仅需关注业务级参数的校验即可。当请求的参数违反校验规则后,Rop将把这些错误“翻译成”对应的错误报文。假设salary格式不对,其对应的错误报文为:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error code="33">
    <message>非法的参数</message>
    <solution>请查看根据服务接口对参数格式的要求</solution>
    <subErrors>
        <subError code="isv.parameters-mismatch:salary-and-yyy">
            <message>传入的参数salary和aaa不匹配,两者有一定的对应关系</message>
        </subError>
    </subErrors>
</error

关于错误处理模型及报文格式,我们将后续内容中讲解。

XML和JSON参数绑定

如果某个请求参数的值是一个XML或JSON串,能否正确地进行绑定呢?Rop框架支持将XML或JSON格式的参数值透明地绑定到RopRequest的复合属性中。

我们通过rop-sample实例项目的UserService#addUser(CreateUserRequest request)讲解XML/JSON参数值绑定的内容。 CreateUserRequest拥有一个Address的业务级参数,如下所示:

CreateUserRequest.java:复合属性
package com.rop.sample.request;
import javax.validation.Valid;
…
public class CreateUserRequest extends AbstractRopRequest {

    @Pattern(regexp = "\\w{4,30}")
    private String userName;

…
    
    //① 可绑定XML或JSON的复合属性,必须打上@Valid注解进行数据校验  
    @Valid
    private Address address;
}

Address是一个复合对象属性,它的类结构对应XML的结构:

CreateUserRequest.java:复合属性
package com.rop.sample.request;

import javax.validation.constraints.Pattern;
import javax.xml.bind.annotation.*;
import java.util.List;

// ①使用JSR 222注解,定义了基于属性名进行数据绑定的规则。
@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "address")
public class Address {

    //②使用JSR 222定义数据绑定规则,使用JSR 303注解定义数据校验规则。
    @XmlAttribute                  
    @Pattern(regexp = "\\w{4,30}")
    private String zoneCode;

    @XmlAttribute
    private String doorCode;


    // ③使用JSR 222注解指定列表数据的绑定规则
    @XmlElementWrapper(name = "streets") 
    @XmlElement(name = "street")
    private List<Street> streets;
}


JSR 222标准规范(也即JAXB),已经作为XML数据绑定官方标准添加到JDK 6.0核心库中。因此,我们直接使用JSR 222注解定义XML数据的绑定规则。官方标准的JAXB库只支持XML数据的绑定,很多开源进行了扩展,支持JSON数据的绑定,Rop使用Jackson项目完成JSON数据的绑定。

请求数据绑定时一般都需要进行数据校验,因此您还需要使用JSR 303的注解定义数据校验规则。通过JSR 222和JSR 303注解两者珠联璧合,Rop很完美地解决了请求数据绑定和数据校验的问题。

开发者仅需要在CreateUserRequest中标注上注解,无需做任何其它的开发工作,就可以绑定客户端的XML和JSON数据了。rop-sample项目的UserServiceRawClient有一个testServiceXmlRequestAttr()测试方法,它演示了XML参数数据绑定的场景:

UserServiceRawClient.java:XML请求参数
@Test
public void testServiceXmlRequestAttr() {
    RestTemplate restTemplate = new RestTemplate();
    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.add("method", "user.add");
    form.add("messageFormat", "xml");//①定消息报文格式为XML格式
    …
    
    //②ML格式的参数数据
    form.add("address",
            "<address zoneCode=\"0001\" doorCode=\"002\">\n" + 
            "  <streets>\n" +
            "    <street no=\"001\" name=\"street1\"/>\n" +
            "    <street no=\"002\" name=\"street2\"/>\n" +
            "  </streets>\n" +
            "</address>");

    //手工对请求参数进行签名
    String sign = RopUtils.sign(form.toSingleValueMap(), "abcdeabcdeabcdeabcdeabcde");
    form.add("sign", sign);

     //调用服务获取响应报文
    String response = restTemplate.postForObject(SERVER_URL, form, String.class);
}


如果有某个请求参数的内容是XML,必须将报文格式设置成xml,如①所示。在②处,address的参数值即是一个XML格式的字符串,它将正确绑定到CreateUserRequest的address属性中。

相似的,下面的testServiceJsonRequestAttr()测试方法则使用JSON格式为address参数提供数据:

UserServiceRawClient.java:XML请求参数
public void testServiceJsonRequestAttr() {
	RestTemplate restTemplate = new RestTemplate();
	MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
	form.add("method", "user.add");
	form.add("messageFormat", "json");//①指定消息格式为JSON格式

        //②JSON格式的参数数据
	form.add("address",
			"{\"zoneCode\":\"0001\",\n" +                         
			" \"doorCode\":\"002\",\n" +
			" \"streets\":[{\"no\":\"001\",\"name\":\"street1\"},\n" +
			"            {\"no\":\"002\",\"name\":\"street2\"}]}");

	String sign = RopUtils.sign(form.toSingleValueMap(), "abcdeabcdeabcdeabcdeabcde");
	form.add("sign", sign);

	String response = restTemplate.postForObject(SERVER_URL, form, String.class);
}


将报文格式设置为json,即可支持JSON格式参数数据的绑定。在默认情况下,Rop不允许同时使用XML和JSON,仅能两者取一:请求和响应报文要么是XML,要么是JSON。


自定义数据转换器

对于复合结构的参数,我们推荐使用XML或JSON的格式指定参数内容。除此以外,Rop允许您通过注册自定义转换器支持自定义格式的参数。Spring 3.0新增了一个类型转换的核心框架,可以实现任意两个类型对象数据的转换,即org.springframework.core.convert.ConversionService,FormattingConversionService扩展于ConversionService,添加了格式化数据的功能。Spring的数据类型转换体系是高度可扩展的,Rop就是基于Spring的类型转换体系实施参数数据绑定的工作,因此,Rop允许开发者定义自己的类型转换器。

在Spring的类型转换服务体系中,转换器是由Converter<S, T>接口定义,它仅能实现单向转换,即从S到T的转换。但是Rop需要双向转换功能:在服务端将参数绑定到RopRequest时,将S转换成T,而在客户端将RopRequest流化成请求报文时,需要将T转换成S。因此,Rop对Converter<S, T>接口进行了扩展,定义了一个可以实现双向转换的接口,如下所示:

package com.rop.request;
import org.springframework.core.convert.converter.Converter;
public interface RopConverter<S, T> extends Converter<S, T> {

    S unconvert(T target);

    Class<S> getSourceClass();
    Class<T> getTargetClass();
}


Converter<S, T>接口定义了一个T convert(S source)的方法,RopConverter<S, T>新增了一个S unconvert(T target)的方法,这样就可以实现S和T两者的双向转换了。

开发一个类型转换器是件轻松的事情,仅需扩展RopConverter<S, T>接口并实现S和T相互转换的逻辑即可。rop-sample中定义了一个可实现格式化电话号码和Telephone对象的双向转换器:

TelephoneConverter.java:双向类型转换器
package com.rop.sample.request;

import com.rop.request.RopConverter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

public class TelephoneConverter implements RopConverter<String, Telephone> {

    @Override
    public Telephone convert(String source) {//①将格式化字符串转换为Telephone 
        if (StringUtils.hasText(source)) {
            String zoneCode = source.substring(0, source.indexOf("-"));
            String telephoneCode = source.substring(source.indexOf("-") + 1);
            Telephone telephone = new Telephone();
            telephone.setZoneCode(zoneCode);
            telephone.setTelephoneCode(telephoneCode);
            return telephone;
        } else {
            return null;
        }
    }

    @Override
    public String unconvert(Telephone target) {//②将Telephone转换为格式化字符串
        StringBuilder sb = new StringBuilder();
        sb.append(target.getZoneCode());
        sb.append("-");
        sb.append(target.getTelephoneCode());
        return null;
    }

    @Override
    public Class<String> getSourceClass() {
        return String.class;
    }

    @Override
    public Class<Telephone> getTargetClass() {
        return Telephone.class;
    }
}


接下来的工作是如何将TelephoneConverter注册到Rop中,以便Rop在进行参数数据绑定时利用这个转换器。
Rop的<rop:annotation-driven/>拥有一个formatting-conversion-service属性,可以通过该属性指定一个Spring的FormattingConversionService。在FormattingConversionService中即可注册自定义的Converter,如下所示:

sampleRopApplicationContext.xml:注册自定义类型转换器
<rop:annotation-driven formatting-conversion-service="conversionService"/>
<bean id="conversionService" 
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <!--将xxxx-yyy格式化串转换为Telephone对象-->
                <bean class="com.rop.sample.request.TelephoneConverter"/>
            </set>
        </property>
 </bean>


CreateUserRequest中拥有一个Telephone的属性:

CreateUserRequest.java
public class CreateUserRequest extends AbstractRopRequest {

    @Pattern(regexp = "\\w{4,30}")
    private String userName;

    @IgnoreSign
    @Pattern(regexp = "\\w{6,30}")
    private String password;

    private Telephone telephone;

…
}


Telephone类拥有zoneCode和telephoneCode两个属性,Rop在处理Telephone类型的数据绑定时,将自动调用TelephoneConverter进行数据转换。

UserServiceClient#testCustomConverter()演示了客户端使用TelephoneConverter的方法:

UserServiceClient.java:测试自定义类型转换器
@Test
public void testCustomConverter() {
    
ropClient.addRopConvertor(new TelephoneConverter());// ①
    
CreateUserRequest request = new CreateUserRequest();
    request.setUserName("tomson");
    request.setSalary(2500L);
    Telephone telephone = new Telephone();
    telephone.setZoneCode("0592");
    telephone.setTelephoneCode("12345678");

    CompositeResponse response = ropClient.buildClientRequest()
.post(request, CreateUserResponse.class, "user.add", "1.0");

    assertNotNull(response);
    assertTrue(response.isSuccessful());
    assertTrue(response.getSuccessResponse() instanceof CreateUserResponse);
}


在①处,RopClient注册了一个TelephoneConverter实现,当调用post()发送服务请求时,TelephoneConverter就会自动将Telephone对象转换成一个xxx-yyy的格式化串,并以请求报文的方式发送给服务端。而服务端则会利用注册在ConversionService中的TelephoneConverter,将xxx-yyy格式的电话号转为Telephone对象。

从上面的分析可知,RopConverter#unconvert()是服务于客户端,而RopConverter#convert()则服务于服务端。由于客户端和服务端位于不同的JVM中,因此必须各自独立注册RopConverter,关于RopClient的更多内容,将在后续内容中介绍。


请求服务映射

Spring MVC通过@RequestMapping注解实现HTTP请求到处理方法的映射。类似的,Rop使用@ServiceMethod注解实现HTTP请求到服务处理方法的映射。@ServiceMethod只能对Bean的方法进行标注,且该方法的签名是受限的:拥有一个RopRequest的入参和一个返回对象。

@ServiceMethod的method和version属性值是必须的,method代码服务方法名,而version表示版本号。如代码清单10-1的getSession()服务方法对应的注解是@ServiceMethod(method = "user.getSession", version = "1.0"),它对应如下的服务请求: 

引用
http://<serverUrl>/<ropServletUri>?method=user.getSession&v=1.0&...


来看一个具体的例子:

UserService.java
@Service //①服务类必须是一个Spring的Bean
public class UserService {

    //②服务方法对应如下的HTTP请求:?method=user.add&v=1.0&…
    @ServiceMethod(method = "user.add", version = "1.0") 
    public RopResponse addUser(CreateUserRequest request) {
       ...
    }
}


服务开放平台一旦将服务发布出去后,其内部实现可以不断优化和调整,但是服务接口必须保证不变,否则基于服务开发的第三方应用的运行稳定性就得不到保障。如果要调整服务接口定义,必须升级版本,这也是Rop为什么要求方法名一定要和版本同时提供的原因。

一个服务方法可以同时存在多个版本,客户端可以调用指定版本的服务。来看几个不同版本的服务及对应的客户端调用参数:

  • @ServiceMethod(method = "user.add", version = "2.0"):对应method=user.add&v=2.0;
  • @ServiceMethod(method = "user.add", version = "3.0"):对应method=user.add&v=3.0;
  • @ServiceMethod(method = "user.get", version = "1.5"):对应method=user.get&v=1.5;


@ServiceMethod除了method和version属性外,还拥有多个其它的属性,分别说明如下:
  • group:服务分组名。服务的分组没有特殊的意义,您可以为服务定义一个分组,以便在事件监听器、服务拦截器中利用分组信息进行特殊的控制。默认的分组为ServiceMethodDefinition.DEFAULT_GROUP;
  • groupTitle:服务分组标识;
  • tags:tags的类型是一个String[],您可以给服务打上一个或多个TAG,以便在事件处理监听器、服务拦截器利用该信息进行特殊的处理;
  • title:服务的标识;
  • httpAction:服务允许的HTTP请求方法,可选值在HttpAction枚举中定义,即GET或POST,如果不指定则不限制;
  • needInSession:表示该服务方法是否需要工作在会话环境中,默认所有的服务方法必须工作于会话环境中,也即请求的sessionId不能为空。如果某个方法不需要工作于会话环境中(如登录的服务方法、获取应用最新版本的服务方法),则必须显式设置:needInSession = NeedInSessionType.NO;
  • ignoreSign:表示该服务方法是否要进行请求数据签名验证,默认为需要。如果不需要,可以设置:ignoreSign=IgnoreSignType.NO。正式环境务必开启请求签名验证的功能,这样才能对客户端请求的合法性进行校验;
  • timeout:服务超时时间,单位为秒。如果服务方法执行时间超过timeout后,Rop将直接中断服务并返回错误的报文。



@ServiceMethod拥有众多的可设置属性,它们都和Rop具体的领域性问题相关联,因此,在这里只要知道method和version的属性就可以了,后面会对其它的属性进行深入的讲解。

如果一个服务类中拥有多个服务方法,而它们拥有一些共同的属性,如group、version等,能否在某个地方统一定义呢?答案是肯定的,Rop为复用服务方法元数据信息提供了一个类级别的@ServiceMethodBean。
@ServiceMethodBean拥有一套和@ServiceMethod类似的属性,其属性值会被同一服务类中所有的@ServiceMethod继承。

@ServiceMethodBean类本身已经标注了Spring的@Service,所以标注了@ServiceMethodBean的服务类就相当于打上的@Service,可以被Spring的Bean扫描器扫描到。

下面的例子拥有两个服务方法,它们的version都是1.0:

UserService.java:使用@ServiceMethodBean
@ServiceMethodBean(version = "1.0") ①
public class UserService {

    @ServiceMethod(method = "user.add") ②
    public RopResponse addUser(CreateUserRequest request) {
       ...
    }

   @ServiceMethod(method = "user.get", httpAction = HttpAction.GET)③
    public RopResponse getUser(CreateUserRequest request) {
...
}
}

②和③处的服务方法的version都自动设置为1.0,如果UserService 业务类方法显式指定了version属性,将会覆盖@ServiceMethodBean的设置。
Rop框架在启动时,将创建代表Rop框架上下文的RopContext实例,同时扫描Spring容器中所有的Bean,将标注了@ServiceMethod的Bean方法注册到RopContext的服务方法注册表中。这样,ServiceRouter就可根据RopContext中的服务方法注册表进行请求服务的路由了。
分享到:
评论

相关推荐

    Rop开发手册

    Rop框架适用于各种服务开放平台的开发,尤其适合那些需要处理复杂应用层面问题的场景,如电商平台、社交媒体平台等。通过利用Rop提供的成熟解决方案,开发者可以减少重复劳动,更加专注于业务逻辑的实现,提高开发...

    ROP(Rest Open Platform)用户开发手册

    Rop,即Rapid Open Platform,是一个参考淘宝开放平台(TOP Taobao Open Platform)的平台设计思路,充分借鉴Spring MVC的技术架构原理开发的一个快速服务开放平台开源框架项目,可以让您迅速构建成熟的SOA服务平台。...

    基于淘宝开放平台开源的rop

    总之,ROP框架为开发者提供了一套全面且易于使用的工具集,不仅降低了构建服务开放平台的门槛,还极大地提高了开发效率。通过借鉴淘宝开放平台的成功实践,ROP框架能够在各种复杂的业务场景下表现出色,为构建高效、...

    rop开放平台

    在描述中提到的“rop开放平台s”可能是这个平台的某种特定版本或服务,但信息过于简略,无法提供更详细的解释。通常,这样的平台可能会提供以下功能: 1. **漏洞分析**:用户可以上传目标二进制文件,平台会分析其...

    Rop framework

    与传统的 WebService 框架(例如 CXF 和 Jersey)不同,Rop 不仅提供了 WebService 的核心功能,还针对服务开放平台中的应用层问题提供了多种解决方案。 **特点**: 1. **易于上手**:Rop 的设计使得开发者能够...

    rop demo完整用例

    本示例是基于Maven构建的ROP框架,Maven是一个流行的Java项目管理工具,它帮助开发者管理和构建项目,依赖管理、构建过程自动化等功能使得开发更加便捷。在本案例中,Maven被用来组织和管理项目的依赖关系,确保所有...

    ROP(Rest Open Platform)开发指南

    ### ROP(Rest Open Platform)开发指南知识点解析 #### 一、REST服务框架的问题域 REST服务框架在解决Web服务的交互过程中面临多种挑战。主要包括: 1. **通讯报文协议**:REST服务通常需要支持多种数据交换格式,...

    ROP(Rest-Open-Platform)开发指南

    #### REST服务框架的问题域与解决方案 REST(Representational State Transfer)是一种轻量级的网络应用程序架构风格,被广泛应用于Web服务中。ROP(Rest-Open-Platform)作为一种面向RESTful服务的开发框架,旨在...

    rop-master.rar

    RopRop 开发类似于淘宝 开发类似于淘宝 开发类似于淘宝 开发类似于淘宝 服务开放平台 服务开放平台 服务开放平台 这样的服务平台 这样的服务平台 这样的服务平台 这样的服务平台 。RopRopRop充分借鉴了当前大型网站...

    ROP : Read origin protocol:ROP 是一种计算协议,用于发现所有读取的来源-开源

    ROP 是一种计算协议,旨在发现所有读数的来源,这些读数源自复杂的 RNA 分子、重组抗体和微生物群落。 ROP 占 poly(A) 和核糖耗竭协议所有读数的 98.8%,而传统的基于参考的协议为 83.8%。 ROP 分析重复、circRNA、...

    rop-sample.rar_.comrop_rop_rop源码下载_taobao netty

    ROP在淘宝的网络通信框架中扮演了重要角色,它允许开发者构建安全且高性能的网络服务。而Netty,作为一个异步事件驱动的网络应用框架,为各种协议(包括TCP、UDP等)的服务器和客户端提供了高度可定制化和高性能的...

    Rop-Fast:快速跳动

    Rop-Fast在Rop的基础上进行了扩展和优化,解决了原始框架中的一些问题,并提供了更友好的API和更快的性能,使得开发人员能够更高效地构建Web服务。 **RESTful服务介绍** REST(Representational State Transfer)是...

    hackme.inndy.tw的rop2题目

    这是https://hackme.inndy.tw/scoreboard/的rop2题目如果网站被墙或者关闭请从这里下载

    rop-plus:rop框架修改加强版

    2. **漏洞利用开发**:黑客可能会使用`rop-plus`来开发针对Java应用程序的攻击。 3. **防御机制设计**:了解ROP的工作原理可以帮助开发者设计更有效的防御策略,如改进的代码混淆、动态代码验证等。 总的来说,`...

    apple-rop:Apple里面集成ROP框架

    在iOS开发中,为了提高代码的安全性和执行效率,开发者们经常使用各种技术手段,其中一种是Return-Oriented Programming(ROP)框架。本文将深入探讨Apple内部集成的陈雄华的ROP框架,以及其在Java环境下的应用。 *...

    rop轻松谈.pdf

    本文將對ROP技術進行詳細的介紹,涵蓋ROP的基本概念、Buffer Overflow、ret2libc/ret2text、Return Oriented Programming等知识点。 ROP技术的基本概念 ROP技術是基於Buffer Overflow的攻擊技術,通過覆蓋函數返回...

    Rapid Open Platform

    Rop,即Rapid Open Platform,是一个参考淘宝开放平台(TOP Taobao Open Platform)的平台设计思路,充分借鉴Spring MVC的技术架构原理开发的一个快速服务开放平台开源框架项目,可以让您迅速构建成熟的SOA服务平台。...

    开放平台(Open Platform)

    开放平台是指软件系统或网络服务通过公开其应用程序编程接口(API)或特定函数的方式,允许外部开发者开发的应用程序能够访问和利用该系统的功能或资源,而无需直接修改系统源代码。这种设计方式促进了软件生态系统...

    弓形虫ROP2基因的克隆及原核表达

    弓形虫ROP2基因的克隆及原核表达,江涛,周艳琴,应用PCR技术从刚地弓形虫( Toxoplasma gondii )RH株的基因组DNA中扩增编码ROP2(rhpotry protein 2)的部分基因,构建pGEX-KG-ROP2重组表达质粒,经酶切�

Global site tag (gtag.js) - Google Analytics