REST风格

本文介绍了REST风格的原理,包括资源、表现层和状态转化,并探讨了RESTFUL设计模式。接着,详细讲解了SpringMVC中实现RESTful的特性,如@PathVariable、静态文件访问、数据格式化、异常处理以及JSON数据交互。内容涵盖HTTP方法的应用、自定义异常处理及响应JSON数据的配置和处理。

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

什么是REST?

REST:即 Representational State Transfer。(资源)表现层状态转化。

是目前 最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用

  • 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它 可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。 可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要 获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识 别符。

  • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层 (Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格 式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。

  • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一 次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器 端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“ 状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动 词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:GET 用来获 取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。

是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用。

什么是REST FUL?

一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

  • 需求示例: /某路径/1 HTTP GET :得到 id = 1 的 user /某路径/1 HTTP DELETE:删除 id = 1的 user /某路径 HTTP PUT :更新查询id = 1的 user,进行PUT请求更新 /某路径 HTTP POST :新增 user

http://localhost:8080/pro/user/1 GET

http://localhost:8080/pro/user/1 DELETE

http://localhost:8080/pro/user/1 PUT

http://localhost:8080/pro/user/ POST

@PathVariable

注解:映射 URL 绑定的占位符

带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的 意义

通过 @PathVariable 可以将 URL 中占位符参数绑定到控 制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过 @PathVariable(“xxx”) 绑定到操作方法的入参中。

    /**
     * REST风格
     */
   @RequestMapping("/rest/{name}/{age}")
   @ResponseBody
    public String rest(@PathVariable("name") String name, @PathVariable Integer age, Model model){
        return "hello:"+name+",age="+age;
   }
  • @PathVariable:不能省略

  • @PathVariable("name"):中“name"如果有{name}名称相同,可以省略"name"

  • 优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀,需将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获 WEB 容器的所有请求,包括静态资源的请求, SpringMVC 会将他 们当成一个普通请求处理,因找不到对应处理器将导致错误

SpringMVC访问静态文件

    <!--静态资源-->
    <mvc:resources mapping="/statics/**" location="/statics/"/>

mapping:将静态资源映射到指定的路径下 location:本地静态资源文件所在的目录

数据格式化

  1. 实现方式

    • 接口方式:org.springframework.format.Formatter

    • 注解方式:org.springframework.format.AnnotationFormatterFactory

  2. 内置API

    • @DateTimeFormat

    • @NumberFormat

  3. 全局注册

    • Java configuration

    • XML-based configuration

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="registerDefaultFormatters" value="false" />
        <property name="formatters">
            <set>
                <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
                <bean class="org.springframework.format.datetime.DateFormatter" p:pattern="yyyy-MM-dd" />
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
                    <property name="dateFormatter">
                        <bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
                            <property name="pattern" value="yyyyMMdd"/>
                        </bean>
                    </property>
                </bean>
            </set>
        </property>
    </bean>
  1. <mvc:annotation-driven conversion-service="conversionService"/>

  2. 日期默认情况下转换失败

HTTP Status 400 – Bad Request

异常处理器

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。

异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

自定义异常类

为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

public class CustomException extends RuntimeException {
​
    /** serialVersionUID*/
    private static final long serialVersionUID = -5212079010855161498L;
    
    public CustomException(String message){
        super(message);
        this.message = message;
    }
​
    //异常信息
    private String message;
​
    public String getMessage() {
        return message;
    }
​
    public void setMessage(String message) {
        this.message = message;
    }
}

根据项目需求 创建多种异常,但一般继承RuntimeException

自定义异常处理器

public class CustomExceptionResolver implements HandlerExceptionResolver {
​
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
​
        ex.printStackTrace();
​
        CustomException customException = null;
        
        //如果抛出的是系统自定义异常则直接转换
        if(ex instanceof CustomException){
            customException = (CustomException)ex;
        }else{
            //如果抛出的不是系统自定义异常则重新构造一个未知错误异常。
            customException = new CustomException("未知错误,请与系统管理 员联系!");
        }
        
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message", customException.getMessage());
        modelAndView.setViewName("error");
​
        return modelAndView;
    }
​
}

错误页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>错误页面</title>
​
</head>
<body>
您的操作出现错误如下:<br/>
${message }
</body>
​
</html>

异常处理器配置

在springmvc.xml中添加:

<!-- 异常处理器 -->
<bean id="handlerExceptionResolver" class="com.zking.ssm.exception.CustomExceptionResolver"/>

handlerExceptionResolver名称不能更名,同multipartResolver

异常测试

修改商品信息,id输入错误提示商品信息不存在。

修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常:

        // 调用service查询商品信息
        Items item = itemService.findItemById(id);
        
        if(item == null){
            throw new CustomException("商品信息不存在!");
        }
​

请自行实现在service、dao中跑出异常。

json数据交互

@RequestBody

作用:

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json

@ResponseBody

作用:

该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

  1. 在WebMvcConfigurationSupport中的addDefaultHttpMessageConverters添加默认实现

  2. HttpMessageConvert<T>负责将一个请求信息转换为一个对象(类型为T),将对象(类型为T)转换为响应信息。DispatcherServlet默认安装了AnnotationMethodHandlerAdapter做为HandlerAdapter的组件实现类,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,将请求信息转换成对象,或将对象转换成响应信息

  3. 参数解析

    1. InvocableHandlerMethod

      • 在HandlerAdapter执行目标方法之前,需要调用resolveArgument() 依次解析出每个入参对象,然后才会执行目标方法

    2. HandlerMethodArgumentResolverComposite

      • 在resolveArgument()内部,首先调用getArgumentResolver() 获得能解析当前入参的HandlerMethodArgumentResolver解析器,然后调用这个解析器的resolveArgument() 获得一个入参对象。

      • getArgumentResolver() 会返回什么类型的HandlerMethodArgumentResolver,这与入参类型入参上的注解有关,Spring MVC默认注册了以下方法参数解析器:

      • 有一些解析器是调用binder.convertIfNecessary(),使用绑定器的ConversionService内部适当的Converter进行类型转换,通常是字符串转入参对象(因为是超文本协议嘛),比如PathVariableMethodArgumentResolver、RequestParamMethodArgumentResolver等。

      • 有一些解析器是调用readWithMessageConverters(),使用适当的HttpMessageConverter将HTTP请求转换为入参对象,比如RequestResponseBodyMethodProcessor、HttpEntityMethodProcessor等。

响应json实现:

环境准备

Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入fastjson的包,如下:

<!-- 阿里fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.4</version>
</dependency>

乱码

  • 方式一:

    • produces="application/json; charset=utf-8"

    /**
     * 新增用户
     */
    @PostMapping(produces="application/json; charset=utf-8")
    public String add(@RequestBody SysUser sysUser){
        return "新增用户";
    }
  • 方式二:

<mvc:annotation-driven conversion-service="conversionService">
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="failOnEmptyBeans" value="false"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

响应类

public class AjaxResult {
    private boolean success = true;
    private String msg="";
​
    public boolean getSuccess() {
        return success;
    }
​
    public void setSuccess(boolean success) {
        this.success = success;
    }
​
    public String getMsg() {
        return msg;
    }
​
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

controller方式一

@Controller
@RequestMapping("/demo4")
public class JsonController {
    /**
     * 前端通过ajax请求checkUserName方法,判断此用户名是否存在
     * @ResponseBody:将当前方法,以Ajax方式响应客户端
     */
    @ResponseBody
    @RequestMapping("/checkUserName")
    public AjaxResult checkUserName(){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMsg("");
        return ajaxResult;
    }
}

@ResponseBody将当前方法,以Ajax方式响应客户端

controller方式二

@RestController
@RequestMapping("/demo4")
public class JsonController2 {
    /**
     * 前端通过ajax请求checkUserName方法,判断此用户名是否存在
     */
    @RequestMapping("/checkUserName2")
    public AjaxResult checkUserName(){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMsg("");
        return ajaxResult;
    }
}@RestController
@RequestMapping("/demo4")
public class JsonController2 {
    /**
     * 前端通过ajax请求checkUserName方法,判断此用户名是否存在
     */
    @RequestMapping("/checkUserName2")
    public AjaxResult checkUserName(){
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setMsg("");
        return ajaxResult;
    }
}

@RestController将Controller中的所有方法以Ajax方法响应

ajax请求

	$.ajax({
			type:"GET",//请求类型
			url:path+"/jsp/user.do",//请求的url
			data:{method:"ucexist",userCode:userCode.val()},//请求参数
			dataType:"json",//ajax接口(请求url)返回的数据类型
			success:function(data){//data:返回数据(json对象)
				if(data.userCode == "exist"){//账号已存在,错误提示
					validateTip(userCode.next(),{"color":"red"},imgNo+ " 该用户账号已存在",false);
				}else{//账号可用,正确提示
					validateTip(userCode.next(),{"color":"green"},imgYes+" 该账号可以使用",true);
				}
			},
			error:function(data){//当访问时候,404,500 等非200的错误状态码
				validateTip(userCode.next(),{"color":"red"},imgNo+" 您访问的页面不存在",false);
			}
		});


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="ajaxAdd" id="userForm">
    <p>
        登录名:<input type="text" name="userCode" id="userCode"/>
    </p>
    <p>
        密码:<input type="password" name="userPassword" id="userPassword">
    </p>
    <p><input type="button" value="注册" id="add"/> </p>
</form>

<script src="../statics/js/jquery-1.8.3.min.js"></script>
<script>
    $(function() {
        /*$("#add").bind("click",function(){
                var userCode = $("#userCode").val();
                var userPassword = $("#userPassword").val();
                $.ajax({
                    type: "GET",//请求类型
                    url: "../demo4/ajaxAdd",//请求的url
                    data: {userCode: userCode,userPassword:userPassword},//请求参数
                    dataType: "json",//ajax接口(请求url)返回的数据类型
                    success: function (data) {//data:返回数据(json对象)
                        console.log(data.success + ":" + data.msg);
                    },
                    error: function (data) {//当访问时候,404,500 等非200的错误状态码
                        console.log(data)
                    }
                })
        });*/

        /**
          contentType:"application/json"
         */

        /*$("#add").bind("click",function(){
            var userCode = $("#userCode").val();
            var userPassword = $("#userPassword").val();
            $.ajax({
                type: "POST",//请求类型
                url: "../demo4/ajaxAdd",//请求的url
                contentType:"application/json",//请求内容类型
                data: {userCode: userCode,userPassword:userPassword},//请求参数
                dataType: "json",//ajax接口(请求url)返回的数据类型
                success: function (data) {//data:返回数据(json对象)
                    console.log(data.success + ":" + data.msg);
                },
                error: function (data) {//当访问时候,404,500 等非200的错误状态码
                    console.log(data)
                }
            })
        });*/

        /**
         * Content-Type: application/x-www-form-urlencoded; charset=UTF-8
         */
      /*  $("#add").bind("click",function(){
            $.ajax({
                type: "POST",//请求类型
                url: "../demo4/ajaxAdd",//请求的url
                data: $("#userForm").serializeArray(),
                dataType: "json",//ajax接口(请求url)返回的数据类型
                success: function (data) {//data:返回数据(json对象)
                    console.log(data.success + ":" + data.msg);
                },
                error: function (data) {//当访问时候,404,500 等非200的错误状态码
                    console.log(data)
                }
            })
        });*/

        /**
         * 请求数据为符合JSON格式的字符串
         */
        $("#add").bind("click",function(){
            var userCode = $("#userCode").val();
            var userPassword = $("#userPassword").val();
            var user={'name':userCode,'passwd':userPassword} //json对象
            $.ajax({
                type: "POST",//请求类型
                url: "../demo4/ajaxAdd2",//请求的url
                contentType: "application/json",
                data: JSON.stringify(user), //将json对象user转变成字符串
                dataType: "json",//ajax接口(请求url)返回的数据类型
                success: function (data) {//data:返回数据(json对象)
                    console.log(data.success + ":" + data.msg);
                },
                error: function (data) {//当访问时候,404,500 等非200的错误状态码
                    console.log(data)
                }
            })
        });
    })
</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值