Restful API设计(Java web Spring MVC 版)

来源:互联网 发布:ct重建算法 编辑:程序博客网 时间:2024/06/08 14:13

关于Restful API 设计的Java SpringMVC的介绍主要分为两部分,先介绍Restful API的基础知识,然后介绍如何在Java web项目中的Spring MVC中使用。

一:关于Restful API相关知识

Restful API 对于HTTP动作的约定

Restful API是目前比较成熟的一套互联网应用程序的API设计理论,它遵循统一接口原则,包含了一组预定义的操作,比如,常用的HTTP动词包括以下五个:

GET:查询操作(可以重复操作)对应MySQL的SELECT语句;

POST:添加操作,对应MySQL的CREATE语句;

PUT:更新操作(客户端提供改变后的完整资源),对应MySQL的UPDATE语句;

PATCH:更新操作(客户端提供改变的属性),对应MySQL的UPDATE语句;

DELETE:删除操作,对应MySQL的DELETE语句。

 

Restful API对接口的域名规定(两种方法)

方法一:对于部署的API应该在专用的域名之下,比如:https://api.example.com(推荐

方法二:如果确定API很简单,不会进一步扩展,可以考虑放在主域名下:https://example.om/api/

 

Restful API 对于版本号的约定(两种方法)

方法一:将API的版本号放入URL中:https://api.example.com/v1/

方法二:将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用此法。

 

Restful API 对于URL的约定

在Restful 架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表名相对应。一般来说,数据库中的表都是同种记录的“集合”(collection),所以API中的名词也可以使用复数。

URL的设计规则如下:

/模块/资源/{标识}/集合1/….

比如:

/user/{uid}/friends          -> 好友列表

/user/{uid}/followers      -> 关注者列表

 

再举个栗子:

与秒杀相关的操作的接口:

GET /seckill/list       秒杀列表

GET /seckill/{id}/detail    详情页

GET /seckill/time/now    系统时间

POST /seckill/{id}/exposer      暴露秒杀

POST /seckill/{id}/{md5}/execution       执行秒杀

 

不友好的设计如下:

POST /seckill/execute/{seckillId}    (不友好,execute是动词)

GET /seckill/delete/{id}   (不友好,应该用DELETE提交,可改为如下:)

DELETE /seckill/{id}/delete     (友好)


二、Restful API 在Spring MVC中的实现

Spring中的@RequestMapping注解,分析如下:

@RequestMapping注解的特点如下:

(1)支持标准的URL

(2)Ant风格URL(即?和**等字符)

(3)带{×××}占位符的URL

例如:

/user/*/creation

        匹配: /user/aaa/creation、/user/bbb/creation等URL。

/user/**/creation

        匹配: /user/creation、/user/aaa/bbb/creation等URL。

/user/{userId}

        匹配: /user/123、/user/abc等URL。

/company/{campanyId}/user/{userId}/detail

        匹配:/company/123/user/323/detail等的URL

SpringMVC中的具体配置如下:

(1)web.xml文件的内容

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1">  <!-- 修改Servlet 版本为3.1 -->  <!-- 配置DispatcherServlet-->  <servlet>    <servlet-name>seckill-dispatcher</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <!--  配置springMVC配置需要加载的配置文件        spring-dao.xml,spring-service.xml,spring-web.xml        Mybatis -> spring -> springMVC     -->    <init-param>      <param-name>contextConfigLocation</param-name>      <param-value>classpath:spring/spring-*.xml</param-value>    </init-param>  </servlet>  <servlet-mapping>    <servlet-name>seckill-dispatcher</servlet-name>    <!--  *.do 是非常丑陋的,默认匹配所有请求-->    <url-pattern>/</url-pattern>  </servlet-mapping></web-app>

注意:上述的DispatcherServlet对应的seckill-dispatcher将所有的请求(这里配置为”/”),都会进行handlemapping,即去控制器中去找对于的路径,因此需要配置一个对静态的资源默认的Servlet,因此有如下配置文件。

(2)spring-web.xml 中配置对静态的资源默认的Servlet

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:mvc="http://www.springframework.org/schema/mvc"       xmlns:context="http://www.springframework.org/schema/context"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context.xsd">    <!-- 配置springMVC -->    <!-- 1:开启SpringMVC注解模式 -->    <!--        (1)自动注册DefaultAnnotationHandlerMapping, AnnotationMethodHandlerAdapter        (2)提供一系列:数据绑定,数字和日期的format @NumberFormat, @DataTimeFormat,xml,json默认读写支持.     -->    <mvc:annotation-driven />    <!-- 2、servlet-mapping 映射路径:"/" -->    <!-- 静态资源默认servlet配置        1、加入对静态资源的处理:js,gif,png        2、允许使用“/”做整体映射     --> <!--   <mvc:default-servlet-handler />-->    <!-- 3:配置jsp 显示ViewResolver -->    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />        <property name="prefix" value="/WEB-INF/jsp/" />        <property name="suffix" value=".jsp" />    </bean>    <!-- 4:扫描web相关的bean -->    <context:component-scan base-package="org.seckill.web" /></beans>
(3)Java 秒杀 SeckillController控制器Restful 接口设计
@Controller@RequestMapping("/seckill") // url:/模块/{id}/细分  /seckill/listpublic class SeckillController {    private final Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    private SeckillService seckillService;    @RequestMapping(value="/list", method = RequestMethod.GET)    public String list(Model model){        //获取列表页        List<Seckill> list = seckillService.getSeckillList();        model.addAttribute("list", list);        // list.jsp + model = ModelAndView        return "list"; //WEB-INF/jsp/list.jsp    }    @RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET)    public String detail(@PathVariable("seckillId") Long seckillId, Model model){        if(seckillId == null){            return "redirect:/seckill/list";        }        Seckill seckill = seckillService.getById(seckillId);        if(seckill == null){            return "forward:/seckill/list";        }        model.addAttribute("seckill", seckill);        return "detail";    }    @RequestMapping(value="/{seckillId}/exposer",            method = RequestMethod.POST,            produces = {"application/json;charset=UTF-8"})    @ResponseBody    public SeckillResult<Exposer> exposer(Long seckillId){        SeckillResult<Exposer> result;        try{            Exposer exposer = seckillService.exposeSeckillUrl(seckillId);            result = new SeckillResult<Exposer>(true, exposer);        }catch (Exception e){            logger.error(e.getMessage(), e);            result = new SeckillResult<Exposer>(false, e.getMessage());        }        return result;    }    @RequestMapping(value = "/{seckillId}/{md5}/execution",            method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})    public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId,                                                   @PathVariable("md5") String md5,                                                   @CookieValue(value = "killPhone", required = false) Long phone){        //SpringMVC valid        if(phone == null){            return new SeckillResult<SeckillExecution>(false, "未注册");        }        SeckillResult<SeckillExecution> result;        try {            SeckillExecution execution = seckillService.executeSeckill(seckillId, phone, md5);            return  new SeckillResult<SeckillExecution>(true, execution);        }catch (RepeatKillException e){            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL);            return new SeckillResult<SeckillExecution>(false, execution);        }catch (SeckillCloseException e){            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.END);            return new SeckillResult<SeckillExecution>(false, execution);        }catch (Exception e){            logger.error(e.getMessage(), e);            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR);            return new SeckillResult<SeckillExecution>(false, execution);        }    }    @RequestMapping(value = "/time/now", method = RequestMethod.GET)    @ResponseBody    public SeckillResult<Long> time(){        Date now = new Date();        return new SeckillResult(true, now.getTime());    }}
原创粉丝点击