RESTFul-service guideline

1. Spring MVC到 JAX-RS的迁移指导

 
1.1 WHAT
  1. 关于Spring REST 和Spring MVC 
    REST特性是Spring Framework的一部分,也是现有的Spring MVC编程模型的延续,因此,并没有所谓的“Spring REST framework”这种概念,有的只是Spring和Spring MVC。这意味着如果你有一个Spring应用的话,你既可以使用Spring MVC创建HTML Web层,也可以创建RESTful Web Services层。
  2. 关于JAX-RS和Jersey 
    JAX-RS:The Java API for RESTful Web Services,是Java的JSR339规范,详见:https://jcp.org/en/jsr/detail?id=339
    Jersey:是JAX-RS的一个实现。我们实际上在pom.xml中配置的依赖,使用的就是Jersey提供的各种Jar包。而且,Jersey不仅仅是JAX-RS的实现,还提供了很多方便开发者的特性和工具。 
    详见:https://jersey.java.net/index.html
  3. 关于RESTEasy
    RESTEasy是JAX-RS的另外一个实现,是JBoss提供的。跟Jersey相比性能上要更优越一些,详见:http://colobu.com/2015/11/17/Jax-RS-Performance-Comparison/ 。对于REST service的实现类来说,使用方法都是一样的,都是使用JAX-RS提供的注解。全局的配置有差别,下面将会详述。

1.2 WHY

从Spring迁移到JAX-RS 的目的有: 
1. JAX-RS : Java的JSR339规范,是业内通用的标准。是个标准化的Java RESTFul service的技术框架。 
2. JAX-RS比Spring MVC更加轻量级, Spring MVC比较重量级,其目标是Web应用开发。对REST的支持只是其中的一部分功能。 网上有一篇文章提到Spring实现的的REST的性能较差,详见:http://colobu.com/2015/11/17/Jax-RS-Performance-Comparison/ 
3. JAX-RS的使用很简便上手,跟Spring的使用方式基本类似,使用各种注解就能够实现一个RESTFul的webservice。
4. 对Swagger的支持相对于Spring要好。Swagger的标准用法和实例都是针对的JAX-RS来实现的。而spring若要支持Swagger,需要一些特殊的配置,和非标准的使用方式,很有可能会有一些坑,遇到问题也不太好解决。

1.3 HOW

1.3.1 配置Jersey

  • pom.xml中的配置(此处配置可能会优化修改) 
    增加如下dependency
<dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>servlet-api</artifactId>
 <version>${servlet-api-version}</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet-core</artifactId>
 <version>${jersey2-version}</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.media</groupId>
 <artifactId>jersey-media-multipart</artifactId>
 <version>${jersey2-version}</version>
 </dependency>
 <dependency>
 <groupId>org.glassfish.jersey.ext</groupId>
 <artifactId>jersey-spring3</artifactId>
 <version>2.23.2</version>
 </dependency>
 <dependency>
 <groupId>io.swagger</groupId>
 <artifactId>swagger-jersey2-jaxrs</artifactId>
 <scope>compile</scope>
 <version>${swagger-core-version}</version>
 </dependency>

对应版本如下

<properties>
<swagger-core-version>1.5.9</swagger-core-version>
<jetty-version>9.2.9.v20150224</jetty-version>
<jersey2-version>2.22.2</jersey2-version>
<junit-version>4.12</junit-version>
<servlet-api-version>2.5</servlet-api-version>
</properties>
  • web.xml中的配置 (此处配置将来可能会优化修改)
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.facishare.paas.metadata.rest</param-value>// REST service的Java代码所在的路径要注册在这里
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>//这里是所有REST api的baseUrl
</servlet-mapping>
  • Java代码中的配置
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
import org.springframework.beans.factory.annotation.Autowired;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import org.springframework.stereotype.Component;
@Component //此注解保证此REST service可以使用Spring的IOC
@Path("/{apiVersion}/tenant")//指定此service的根path。相当于Spring的@RequestMapping("/{apiVersion}/tenant")
@Consumes({"application/json"})//用于此处来说明此service所有的method传入参数的传输数据格式都是基于json。相当于spring的consumes = "application/json"
@Produces({"application/json"})//用于此处来说明此service所有的method返回值的传输数据格式都是基于json。相当于spring的produces = "application/json"
public class TenantCtrl {
 @Autowired//依赖注入其他后台service
 ITenantInitializeService tenantInitializeService;
 @GET //相当于Spring中的@RequestMapping中的method = RequestMethod.GET, 描述此方法是个GET请求。
 @Path("/init/{tenantId}/{type}")//方法级别的path定义。相当于Spring中的@RequestMapping中的value = "/init/{tenantId}/{type}"
 public Response initializeTenant(@PathParam("tenantId") String tenantId, @PathParam("type") String type,@QueryParam("level") String level) {
 //上面的@PathParam相当于Spring的@PathVariable, @QueryParam相当于Spring的@RequestParam
 APIResult result = new APIResult();
 try {
result.setMessage(APIResult.OK);
tenantInitializeService.initialTenant(tenantId, type, level);
 return Response.ok().entity(result).build();//后台操作成功,返回200状态码,ok()代表成功。entity()方法可以把某个Java对象作为返回值传入到Response中。前面配置@Produces是application/json,将会把Java Object转化为Json格式返回给前端。
 catch (DBException e) {
log.error("Init tenant failed with tenantId:"+tenantId+" , exception:",e);
result.setMessage(APIResult.ERROR);
result.setErrorDetail(e.toString());
 return Response.serverError().entity(result).build();
 //如果遇到异常或者报错,返回500状态码。直接使用serverError()方法,可以将包装好的APIResult对象,带着异常栈信息返回给客户端。
 //此处也可以抛出WebApplicationException,详见: https://jersey.java.net/apidocs/latest/jersey/javax/ws/rs/WebApplicationException.html
 }
 }
  • 其他设置 
    需要一个工具类的支持,来包装Java Object在Respons.entity()方法中转化为json格式时候能够顺利成功。 
    JacksonJsonProvider.java
package com.facishare.paas.metadata.rest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import io.swagger.util.Json;
@Provider
@Produces({MediaType.APPLICATION_JSON})
public class JacksonJsonProvider extends JacksonJaxbJsonProvider {
 private static ObjectMapper commonMapper = Json.mapper();
 public JacksonJsonProvider() {
 super.setMapper(commonMapper);
 }
}

1.3.2 配置RESTEasy

pom.xml 中的配置
<properties>
    <resteasy-version>3.0.19.Final</resteasy-version>
    <servlet-api-version>3.1.0</servlet-api-version>
</properties>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servlet-api-version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>${resteasy-version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-client</artifactId>
    <version>${resteasy-version}</version>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-multipart-provider</artifactId>
    <version>${resteasy-version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson2-provider</artifactId>
    <version>${resteasy-version}</version>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-servlet-initializer</artifactId>
    <version>${resteasy-version}</version>
</dependency>
web.xml

因为使用了Servlet 3 ,所以web.xml中无需再做设置

增加一个javax.ws.rs.core.Application的子类

此类的作用是注册REST service,并可以通过此类的配置来跟swagger集成,也可以注册其他的provider,例如

JacksonJsonProvider等。
如例:
package com.facishare.paas.metadata.rest;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
 
import io.swagger.jaxrs.config.BeanConfig;
 
/**
 * Created by Jacky on 8/30/16.
 */
 
@ApplicationPath("/rest")
public class RestApplication extends Application {
    private Set<Object> singletons = new HashSet<Object>();
    private Set<Class<?>> classes = new HashSet<Class<?>>();
 
    public RestApplication()
    {
        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0.2");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8080");
        beanConfig.setBasePath("/rest");
        beanConfig.setResourcePackage("io.swagger.resources");
        beanConfig.setScan(true);
        singletons.add(new PingJAXRS());
        singletons.add(new TenantCtrl());
        singletons.add(new ObjectDescribeCtrl());
        singletons.add(new ObjectDataCtrl());
        singletons.add(new GlobalDataSourceConfigCtrl());
        singletons.add(new GlobalDataStoreConfigCtrl());
        singletons.add(new DataStoreCtrl());
        classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
        classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
        classes.add(JacksonJsonProvider.class);
    }
 
    @Override
    public Set<Class<?>> getClasses()
    {
        return classes;
    }
 
    @Override
    public Set<Object> getSingletons()
    {
        return singletons;
    }
}

其中在singletongs中增加每一个Rest的服务的一个实例。此处非常重要。如果不添加到singletons中的话,这个REST服务是无法被swagger所探测到,就无法生成对应的swagger.json中的内容。

        singletons.add(new PingJAXRS());
        singletons.add(new TenantCtrl());
        singletons.add(new ObjectDescribeCtrl());
        singletons.add(new ObjectDataCtrl());
        singletons.add(new GlobalDataSourceConfigCtrl());
        singletons.add(new GlobalDataStoreConfigCtrl());
        singletons.add(new DataStoreCtrl());

所以,每当新增加一个新的REST服务的时候,一定要修改此处代码,把新的服务注册进来。否则无法生成swagger

关于和Spring bean的集成,需要一些特殊的配置,否则无法注入spring的bean。

根据 RESTEasy的官方文档: http://docs.jboss.org/resteasy/docs/3.0.19.Final/userguide/html_single/index.html#RESTEasy_Spring_Integration

需要进行两个配置,

      1,在pom.xml中增加:

<dependency>

    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-spring</artifactId>
    <version>${resteasy-version}</version>
</dependency>

     2, 在web.xml中配置:

<listener>
      <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
   </listener>

   <listener>
      <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
   </listener>

但是在我们的项目中遇到了和FcpServerContextListener冲突的问题。 解决方案是继承FcpServerContextListener写一个新类,然后把

org.jboss.resteasy.plugins.spring.SpringContextLoaderListener中的内容拷贝到此类中。然后在web.xml配置这个类。代码如下:
package com.facishare.paas.metadata.rest;
 
import com.facishare.fcp.server.FcpServerContextListener;
 
import org.jboss.resteasy.plugins.spring.SpringContextLoader;
import org.jboss.resteasy.plugins.spring.SpringContextLoaderSupport;
import org.jboss.resteasy.plugins.spring.i18n.Messages;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
 
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
 
/**
 * Created by Jacky on 9/6/16.
 */
public class RestEasyServerContextListener extends FcpServerContextListener {
    private SpringContextLoaderSupport springContextLoaderSupport = new SpringContextLoaderSupport();
 
    @Override
    public void contextInitialized(ServletContextEvent event)
    {
        boolean scanProviders = false;
        boolean scanResources = false;
 
        String sProviders = event.getServletContext().getInitParameter("resteasy.scan.providers");
        if (sProviders != null)
        {
            scanProviders = Boolean.valueOf(sProviders.trim());
        }
        String scanAll = event.getServletContext().getInitParameter("resteasy.scan");
        if (scanAll != null)
        {
            boolean tmp = Boolean.valueOf(scanAll.trim());
            scanProviders = tmp || scanProviders;
            scanResources = tmp || scanResources;
        }
        String sResources = event.getServletContext().getInitParameter("resteasy.scan.resources");
        if (sResources != null)
        {
            scanResources = Boolean.valueOf(sResources.trim());
        }
 
        if (scanProviders || scanResources)
        {
            throw new RuntimeException(Messages.MESSAGES.cannotUseScanParameters());
        }
 
 
        super.contextInitialized(event);
    }
 
    protected ContextLoader createContextLoader()
    {
        return new SpringContextLoader();
    }
 
    @Override
    protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext configurableWebApplicationContext) {
        super.customizeContext(servletContext, configurableWebApplicationContext);
        this.springContextLoaderSupport.customizeContext(servletContext, configurableWebApplicationContext);
    }
 
}
 
 
  
---如下是web.xml中的配置
<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
 
<listener>
    <listener-class>com.facishare.paas.metadata.rest.RestEasyServerContextListener</listener-class>
</listener>
关于Swagger配置,

详见:https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-RESTEasy-2.X-Project-Setup-1.5

根据这个文档,我们在上面的Application实现类中的增加了如下代码,支持了Swagger:

BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/rest");
beanConfig.setResourcePackage("io.swagger.resources");
beanConfig.setScan(true);
classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);

关于RESTEasy,更多详情请参考官方链接:

 http://jboss.org/resteasy 

 https://github.com/resteasy/Resteasy   

 http://resteasy.jboss.org/docs

2. Swagger的使用方法

2.1 WHAT

Swagger的定义:Swagger offers a specification and complete framework implementation for describing, producing, consuming, and visualizing RESTful web services. The Swagger framework works with many of the popular programming languages, such as Java, Scala, Clojure, Groovy, JavaScript, and .Net.”

“The Swagger framework has the following three major components:

  • Server: This component hosts the RESTful web API descriptions for the services that clients want to use
  • Client: This component uses the RESTful web API descriptions from the server to provide an automated interfacing mechanism to invoke the REST APIs
  • User interface: This part of the framework reads a description of the APIs from the server and renders it as a web page and provides an interactive sandbox to test the APIs”

2.2 WHY

为什么要使用Swagger呢?简单的说Swagger可以帮助我们通过Swagger的注解,生成json文件,来描述我们所有的REST API,以便于把REST API文档化,规范化。

个人总结了一下Swagger的几大优点: 
1,功能强大,可以从下倒上,也可以从上到下两个方向的设计REST API 
2,语言无关性,提供针对各种语言和框架的支持。 
3,json或者yaml的定义格式简单易读。针对我们的REST外部数据源的动态接入方式,重点是解析json或者yaml,变可以能够分析出相应REST Api的接口方法,参数和返回值的定义。 
4,Swagger非常流行,也是Open API的主要发起者,非常的成熟,用户广泛,学习资料多。 
5,相关工具非常强大和易用,包括swagger editor, swagger ui, swager code generator, maven plugin等等。可以帮助开发者很方便的在UI上定义一个REST API,并生成客户端或者服务端代码,还可以在SwaggerUI上测试,非常方便开发者调试程序。

2.3 HOW

  • 如果是想要先定义REST API,然后通过yaml或者json生成Java代码,这种从上到下的方式来使用Swagger的话。可以使用SwaggerEditor。 
    http://editor.swagger.io/#/ 
    可以open example,选择一个例子,在其基础上来修改。

  • 如果先有了Java代码,需要反向生成Swagger.json定义文件的话。需要在Java代码中使用Swagger的注解。 
    主要的常用注解如下: 
    @io.swagger.annotations.Api 
    @io.swagger.annotations.ApiOperation 
    @io.swagger.annotations.ApiResponses 
    @io.swagger.annotations.ApiParam 
    @io.swagger.annotations.ApiModel; 
    @io.swagger.annotations.ApiModelProperty;

详见文档:https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X 
也可以通过swagger Editor生成的Java code中去看每个注解都是怎样使用的。

  • 一些其他重要的基础配置:

    • pom.xml

     

      <build>
     <plugins>
     <plugin>
     <groupId>com.github.kongchen</groupId>
     <artifactId>swagger-maven-plugin</artifactId>
     <version>3.1.0</version>
     <configuration>
     <apiSources>
     <apiSource>
     <springmvc>false</springmvc>
     <locations>com.facishare.paas</locations>
     <schemes>http,https</schemes>
     <host>localhost:8080</host>
     <basePath>/service/rest</basePath>
     <info>
     <title>Facishare PAAS MDS</title>
     <version>v1</version>
     <description>This is PAAS metadata service project</description>
     </info>
     <swaggerDirectory>generated/swagger-ui</swaggerDirectory>
     </apiSource>
     </apiSources>
     </configuration>
     <executions>
     <execution>
     <phase>compile</phase>
     <goals>
     <goal>generate</goal>
     </goals>
     </execution>
     </executions>
     </plugin>
     </plugins>
    </build>

     

    • web.xml

<servlet>
 <servlet-name>Jersey REST Service</servlet-name>
 <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
 <!-- Register resources and providers under com.vogella.jersey.first package. -->
 <init-param>
 <param-name>jersey.config.server.provider.packages</param-name>
 <param-value>com.facishare.paas.metadata.rest,io.swagger.jaxrs.listing</param-value>
 </init-param>
 <init-param>
 <param-name>jersey.config.server.provider.classnames</param-name>
 <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
 </init-param>
 <init-param>
 <param-name>jersey.config.server.wadl.disableWadl</param-name>
 <param-value>true</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 </servlet>
 
  

2.4 重要的网址链接:

Swagger UI

Live Demo:http://petstore.swagger.io/#/ 
doc: https://github.com/swagger-api/swagger-ui 
纷享销客内部的SwaggerUI的相关链接和使用指导: 
http://oss.firstshare.cn/swagger-ui/ 
如果已有写好的swagger api的json,但是没有部署到Java工程中。可以提交json到这个git的项目里面 
http://git.firstshare.cn/paas/fs-swagger-api 
例如, 
http://oss.firstshare.cn/swagger-api/paas/metadata/crm_swagger_sample.json 
然后把这个url复制粘贴到http://oss.firstshare.cn/swagger-ui/中间的输入框中,就可以用UI来展现API的定义。

http://git.firstshare.cn/paas/fs-swagger-api 
每次push会自动推动到http://oss.firstshare.cn/fs-swagger-api/

Swagger editor

Live Demo: http://editor.swagger.io/#/ 
doc: https://github.com/swagger-api/swagger-editor 
将来也考虑在内部搭建一个SwaggerEditor的server

Swagger codegen: 
https://github.com/swagger-api/swagger-codegen 
也可以在http://editor.swagger.io/#/ 生成代码下载:

Swagger maven plugin: 
https://github.com/kongchen/swagger-maven-plugin

OpenAPI 规范

http://swagger.io/specification/ 
https://github.com/OAI/OpenAPI-Specification

3. REST API设计指导方针

以下内容基本翻译自RESTful Java Web Services Second Edition ,

严格意义上说,REST是一种构建可扩展的webservices的软件架构风格,不是规范。REST并没有设计或者构建APIs的严格规范。 
这里要讨论一些标准,最佳实践,惯例,技巧和窍门。 
希望可以借此在内部形成统一的风格,乃至规范。

3.1 从问题领域中识别和定义Resource

1,识别出所有的问题领域中的对象,也就是所有的名词。例如:department , employee 
2,识别出所有跟上述对象相关的CRUD操作。

3.2 把操作转化为HTTP methods

最常用的HTTP methods有:POST ,GET , PUT ,DELETE 
idempotent[aɪ'dɛmpətənt]在这里是个比较重要的概念,翻译过来叫做幂等性。简单的解释:一个幂等性的REST API无论通过同样的参数调用多少次都返回相同的结果。 
HTTP方法中:GET, HEAD, PUT , DELETE都是幂等的, POST则不是。

GET

GET操作只是读取服务端Resource的represetation。只会读取数据,不应该作出任何状态变更。是幂等性的操作。

如果GET成功,应该返回200 OK的HTTP状态码。 
如果有错误,可以返回404 NOT FOUND或者400 BAD REQUEST

DELETE

用DELETE做删除操作。 
如果成功,返回200 OK 
如果已经删除过,返回404 NOT FOUND

PUT

PUT method也是个幂等的。一般用于把Resource所有的可用的字段都PUT到Server上,不仅仅是更改过的部分数据。

POST

POST是非幂等性的。当你不知道这个Resource的所有字段的值的时候需要创建或者更新资源时,使用POST。例如ID是在server端生成的。

如果成功创建资源,建议返回201 Created 状态,并把新创建的Resource的location返回。 
例如: 
201 Created 
Location: /hrapp/api/employees/1001”

PUT和POST的区别 
You can use PUT for creating or updating a resource when the client has the full resource content available. In this case, all values are with the client, and the server does not generate a value for any of the fields.

You will use POST for creating or updating a resource if the client has only partial resource content available. Note that you are losing the idempotency support with POST. An idempotent method means that you can call the same API multiple times without changing the state. This is not true for the POST method; each POST method call may result in a server state change. PUT is idempotent, and POST is not. If you have strong customer demands, you can support both methods and let the client choose the suitable one on the basis of the use case.

3.3 HTTP状态码使用规范

200 OK

201 Created 
This status is generated in response to a POST operation that creates a new resource.”

204 No Content 
This code tells that the server has processed the request but not returned any content. For example, this can be used with the PUT, POST, GET, or DELETE operations if they do not result in any result body.

304 Not Modified 
This code tells the client that it can use the cached resource that was retrieved in the previous call. This code is typically used with the GET operation.

400 Bad Request 
This code indicates that the request sent by the client was invalid (client error) and could not be served. The exact error should be explained in the response entity body payload.

401 Unauthorized 
This code indicates that the REST API call requires user authentication. The client can take the necessary steps for authentication based on this response status.

403 Forbidden 
This code indicates that the caller (even after authentication) does not have access to the requested resource.

404 Not Found 
This code indicates that the requested resource is not found at this moment but may be available again in the future.

405 Method Not Allowed 
This code indicates that the HTTP method requested by the caller is not supported by the resource.”

406 Not Acceptable 
This code indicates that the server does not support the required representation. For example, this can be used in response to the GET, PUT, or POST operations if the underlying REST API does not support the representation of the resource that the client is asking for.

409 Conflict 
“This code indicates that the request could not be completed due to some general conflict with the current state of the resource. This response code makes sense where the caller is capable of resolving the conflict by looking at the error details present in the response body and resubmitting the request. For example, this code can be used in response to PUT, POST, and DELETE if the client sends an outdated resource object to the server or if the operation results in the duplication of resources

410 Gone

This code indicates that the resource identified by this URI is no longer available. Upon receiving a 410 status code, the caller should not request the resource again in the future.

415 Unsupported Media Type

This code indicates that the entity media type present in the request (PUT or POST) is not supported by the server.

422 Unprocessable Entity

This code indicates that the server cannot process the entity present in the request body. Typically, this happens due to semantic errors such as validation errors.”

429 Too Many Requests

This code indicates that the client has sent too many requests in a given time, which results in the rejection of requests due to rate limiting. Note that rate limiting is used for controlling the rate of traffic sent or received by a network interface controller.

500 Internal Server Error

This code indicates that the server encountered an unexpected error scenario while processing the request.

使用JAX-RS实现RESTful Service的话: 

 return Response.status(Response.Status.OK).entity(result).build();


Response.Status中有所有HTTP 状态码的枚举值。 
常用的状态码,例如200 OK,可使用Response.ok()来返回,更加便捷。 
包括: 
ok() 200 
serverError() 500 
created() 201 
accepted() 202 
noContent() 204 
notModified() 304

3.4 命名

1,避免在resource path中出现动词。尽量都用名词。 
2,应该一直使用复数的名词作为Resource的命名。例如: 
/departments 是get所有部门的uri. 
/departments/{id} 是获取id为10的单个department对象 
3, 尽量用更加具体的名词。例如:/programmers 比/employees更具体 
4,resource中尽量不要包含特殊字符,下划线等。 
5,驼峰式命名,首字母小写,首字符不要用特殊符号。

3.5 版本

可以有很多种实现方式:例如把version放在path中。

/rest/v1/customers

或者把版本放在request header中 
GET /api/departments HTTP/1.1 
api-version: 1.0”

混合方式:主版本放置在URI中。小版本放在request header中。

3.8 Header规范

名称
说明
值(范例)
accept 接收的数据格式,约定都用Json application/json
content-type 传输的数据格式,约定都用Json application/json
x-fs-ea 企业账号(一般来说用EI,不用EA,特殊情况下可以用EA来标识一个企业)  
x-fs-ei 企业ID。如果企业ID已经作为PATH参数的话,header中的这个企业ID可以不传。 7
x-fs-userInfo 用户信息,当前操作人ID 10000
x-fs-peer-host 调用方Host地址 例:VSWR104002
x-fs-peer-name 调用方Name 例:FBS, OpenAPI等
 

3.7 其他

1,建议在RESTful service中不写任何业务逻辑代码。只是作为一个controller,所有的业务逻辑都代理给另外一个注入进来的Service。 


2,  什么东西可以放在header里面,什么不可以放在header里面的相关规范。

https://www.soapui.org/testing-dojo/best-practices/understanding-rest-headers-and-parameters.html

3,EasyREST和Jersey比较。用法,性能等方面。

http://resteasy.jboss.org/docs

http://colobu.com/2015/11/17/Jax-RS-Performance-Comparison/

4,dubboX 的REST和JAX-RS怎么取舍? 
https://github.com/dangdangdotcom/dubbox 
5, update是否暴露ID 
何时暴露ID到path中。 
批量操作,批量更新和删除,如何定义API? 
6, 内部的安全校验 

参考:
http://172.31.160.165:8500/ui/#/firstshare/acls/2f8a1b46-9f71-af0b-3a58-35fd36261f3c 
7, swagger 能否支持mockup 
https://github.com/dzdrazil/swagger-mock-api 
https://www.npmjs.com/package/swagger-mock

8, Java对象和json之间的转换,fastjson or jackson

https://github.com/alibaba/fastjson

http://colobu.com/2014/09/30/using-fastjson-as-JAX-RS-data-binding/

原文地址:https://www.cnblogs.com/ljsong/p/8692792.html