Restful API设计(Java web Spring MVC 版)

本文介绍了Restful API设计的基本原则,包括HTTP动词的使用、域名和版本号约定、URL设计,以及在Java Web项目中使用Spring MVC实现Restful API的方法,详细解析了@RequestMapping注解的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于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/list
public 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());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值