REST API

概念

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。

REST API:使用REST风格的应用程序界面(REST Application Interface)
即应用系统提供REST风格的API对外服务。
其他系统通过访问REST API与应用系统进行交互。
REST API是前后端分离最佳实践,是开发的一套标准或者说是一套规范,不是框架。

目前REST API已经成为系统互联的标准,用于各种系统的相互操作和调用。
主流软件厂家都提供了各种REST API供程序员开发项目使用。
未来软件开发就是实现各种API的调用,不一定所有的服务都自己开发,调用相应的REST API即可。

当今REST API已经成为软件项目开发的标准,应用系统基本上都使用REST API方式对外提供服务,尤其是当前系统要支持各种客户端如Web,手机(Android,iOS),平板,IoC设备等,所有客户端都使用REST API方式访问后台系统服务。
如下图是当今软件应用系统的主流架构。
系统架构

组成

  1. 资源(Resource)
  2. 协议(Protocol)
  3. 操作(Operation)

数据格式

当对一个REST风格的API访问时,需要传递数据给API或从API取得数据,目前REST API的数据传输格式基本都采用JSON,也有时候采用XML。
(1)JSON
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
如下是一个REST API请求后返回的信息的JSON数据:

{“coord”:{“lon”:121.47,”lat”:31.23},”sys”:{“type”:1,”id”:7452,”message”:0.1765,”country”:”China”,”sunrise”:1425852688,”sunset”:1425895071},”weather”:[{“id”:804,”main”:”Clouds”,”description”:”overcast clouds”,”icon”:”04d”}],”base”:”cmc stations”,”main”:{“temp”:283.62,”pressure”:1027,”humidity”:81,”temp_min”:282.15,”temp_max”:285.93},”wind”:{“speed”:6,”deg”:310},”clouds”:{“all”:90},”dt”:1425869026,”id”:1796231,”name”:”Shanghai”,”cod”:200}

(2)XML
XML 指可扩展标记语言(EXtensible Markup Language)
XML 是一种标记语言,很类似 HTML
XML 的设计宗旨是传输数据,而非显示数据
XML 标签没有被预定义。需要自行定义标签。
XML 被设计为具有自我描述性。
XML 是 W3C 的推荐标准

如下是一个REST API请求后返回的XML数据案例,表达一个note数据:

<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

状态

请求REST API都会返回其状态,状态使用状态码形式。与HTTP协议的响应状态码相同。

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
405:请求方式错误。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

RESTful web services(REST Web服务)

RESTful web services使用RESTful架构风格构建的服务,由于其轻量的特性和在HTTP上直接传输数据的能力,在互联网服务部署技术的选择上,使用RESTful风格构建服务正在逐渐成为主流,逐步取代了基于SOAP技术的Web服务。

RESTful web services的实现技术

Jersey、Apache Wink,Spring MVC、Restlet、Node.js Express框架,Node.js Sails.js框架,Scala Play框架,Scala Akka HTTP框架,Python Flask框架。
基于基于Spring MVC的Spring Boot成为开发REST API的最受欢迎的框架。

Spring MVC开发REST API的准备

Spring MVC实现REST API的服务非常简单,所有控制器方法主要使用 @ResponseBody进行注解就自动变成了一个REST API,控制器方法的映射地址就是REST API的资源地址,映射的method就是REST API的操作方法。
新版的Spring MVC内置了Jackson JSON解析器,只需引入Jackson的依赖类库即可。

引入JacksonJSON解析器的Maven依赖库
由于目前REST API服务的数据传输格式都是JSON,为此Spring MVC需要配置JSON解析器。
默认情况下,Spring MVC使用著名的Jackson的JSON解析器,当Spring MVC发现在classpath目录内发现Jackson的依赖JAR文件,就自动使用其作为JSON解析器,不需要配置专门的JSON解析器Bean。
maven repository

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.11.0</version>
</dependency>

定制Jackson JSON解析器
Spring MVC如果发现在类路径下发现Jackson类库文件,自动使用它作为JSON解析器。
Jackson的JSON解析器默认使用美国风格的日期作为日期的解析,此时就需要对其进行配置。
可以对其进行定制。
1.使用配置类方式进行定制配置
使用Java Config配置类方式配置Spring MVC的Jackson解析器代码如下:

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

... //其他Bean的配置


@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.indentOutput(true)
.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
.modulesToInstall(new ParameterNamesModule());
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new
MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
}
}

2.使用XML方式进行定制配置

<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper"/>
</bean>
<bean
class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter">
<property name="objectMapper" ref="xmlMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="objectMapper"
class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
p:indentOutput="true"
p:simpleDateFormat="yyyy-MM-dd"
p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/>
<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/>

开发REST API

使用Spring MVC开发REST API非常简单。只需在控制器方法前加入@ResponseBody注解类就可以了。
如果接收数据也采用JSON,在接收数据参数前使用@RequestBody即可。

使用@Controller和@ResponseBody
使用@Controller和@ResponseBody
在Spring4.0版本以前,因为没有提供@RestController,要开发REST API,需要在控制器类的每个方法前都加入
@ResponseBody。

@Controller
@RequestMapping("/api/department/")
public class DepartmentRestController {
	@Autowired
	private IDepartmentService departmentService=null;
	//接收JSON部门提交数据
	@RequestMapping(value="/add",method=RequestMethod.POST,consumes= {"application/json"})
   @ResponseBody
	public String add(@RequestBody DepartmentModel dm) throws Exception{
		departmentService.add(dm);
		return "OK";
	}
	//取得所有的部门列表
	@RequestMapping(value="/list/all")
	@ResponseBody
	public List<DepartmentModel> getListByAll() throws Exception{
		return departmentService.getListByAll();
	}
}

返回结果

  1. 单个对象,(Model类对象)或数值(String,int)的结果
    推荐使用如下结果类:

    //返回单个数据或对象的结果
    public class ResultOne<T> {
    	private T result=null; //返回结果
    	private String status=null; //状态 OK 正常, ERROR:异常
    	private String message=null; //操作信息,如果是异常就是异常原因
    	public T getResult() {
    		return result;
    	}
       //get和set方法
    }
    
    
  2. 多个对象list
    当REST API返回多个对象的集合时,一般是List对象,创建一个包含List对象的结果类,同时包含status, message,也推荐包含分页信息,如page, rows, count, pageCount等,因为一般请求列表都采用分页模式,以防止内存溢出。
    多结果返回的结果类设计推荐如下:

    //用于封装列表方式 包括分页列表结果
    	public class ResultList<T> {
    		private int page=0;
    		private int pageCount=0;
    		private int rows=0;
    		private int count=0; //个数
    		private List<T> list=null;
    		private String status=null; //状态, OK正常,ERROR:异常
    		private String message=null; //操作信息
    	//get和set方法
    	}
    

    泛型T等使用时替换为指定的返回类型,如ResultList
    ,ResultList, ResultList等。
    案例:

    //取得所有的部门列表,不分页模式
    @RequestMapping(value="/list/all")
    @ResponseBody
    public ResultList<DepartmentModel> getListByAll() throws Exception{
    		ResultList<DepartmentModel> result=new ResultList<DepartmentModel>();
    		result.setList(departmentService.getListByAll());
    		result.setStatus("OK");
    		result.setMessage("取得部门列表全部成功");
    		return result;
    }
    //取得所有的部门列表,分页模式
    @RequestMapping(value="/list/all/page")
    @ResponseBody
    public ResultList<DepartmentModel> getListByAllWithPage(@RequestParam(value="rows",required=false,defaultValue="10") int rows,@RequestParam(value="page",required=false,defaultValue="1") int page) throws Exception{
    		ResultList<DepartmentModel> result=new ResultList<DepartmentModel>();
    		result.setCount(departmentService.getCountByAll());
    		result.setPage(page);
    		result.setRows(rows);
    		result.setPageCount(departmentService.getPageCountByAll(rows));
    		result.setList(departmentService.getListByAllWithPage(rows, page));
    		result.setStatus("OK");
    		result.setMessage("取得部门列表分页方式成功");
    		return result;
    }
    
    
  3. 异常结果
    当系统某个层的方法出现异常时,按照Spring MVC的异常处理机制,都交给Controller Advice集中处理异常。
    由于使用Controller Advice类集中处理异常,可编写一个异常结果类,
    也可以直接使用ResultOne。
    这里直接使用ResultOne结果类作为异常返回结果。

    @Component
    @ControllerAdvice
    public class ControllerExceptionHandlerAdvice {
    	@ExceptionHandler
    	@ResponseBody
    	public ResultOne<String> exceptionHandler(Exception ex) throws Exception{
    		ResultOne<String> result=new ResultOne<String>();
    		result.setStatus("ERROR");
    		result.setResult("请求出现异常");
    		result.setMessage("异常原因:"+ex.getLocalizedMessage());
    		return result;
    	}
    }
    
    

测试

测试REST API服务一般不用浏览器测试,因为使用浏览器无法模拟各种请求类型,如POST, PUT,DELETE等,浏览器测试GET方式较为方便,其他方法特别麻烦。
市场流行的REST API测试工具主要有如下:
1.POSTMAN
2.SOAPUI

jQuery调用REST API

jQuery框架通过AJAX方式调用后台的REST API接口,实现异步方式的调用

jQuery 引入

  1. 下载jQuery:https://jquery.com/,目前最新版是3.5.1
  2. 将jQuery文件保存到Web站点的指定目录中
    如/web/app/dist/jquery/jquery-3.5.1.min.js
  3. 在Web页面中引入:
    如在项目的主页/index.jsp中引入:
  1. 编写主页的JS文件,与index.jsp或index.html保存在同一个文件夹内。
    /main.js

  2. 在main.js内编写jQuery页面载入成功回调函数,所有JS代码都在此函数内写。

    $(document).ready(function(){
        //页面的处理代码。
    });
    简化的函数:
    $(function(){
      //处理代码
    });
    

ajax函数

语法: jquery.ajax(setting对象) 或 $.ajax(setting);
其中:setting是JavaScript对象。格式: {属性:值,属性:值,属性:值}

$.ajax({
url:../api/department/add.mvc”,
data: {code:D01,name:”财务部”},
dataType:”json”,
headers:{info:”测试值”},
beforeSend:function(){
   var code=$(“input[name=’code’]).val();
   if(code==null||code==’’){
alert(“部门编码不能为空”);
return false;
}
},
sucess:function(resultData){
$.each(resultData.list,function(index,dm){
  var tr=<tr><td>+td.no+<td></td>+dm.code+</td><td>+dm.name+</td></tr>;
$(“table#departmentList body”).append(tr);
});
},
error:function(xhr,status,message){
  alert(message);
}


});

$.ajax({
url:../api/department/add.mvc”,
data: {code:D01,name:”财务部”},
dataType:”json”,
headers:{info:”测试值”},
beforeSend:function(){
   var code=$(“input[name=’code’]).val();
   if(code==null||code==’’){
alert(“部门编码不能为空”);
return false;
}
}
}).done(function(resultDate,status){
   //与success相同的代码
}).fail(fucntion(xhr,status,message){
  //与error相同的代码
});

常用的属性如下:
(1)type:指定请求方式,如GET, POST, PUT,DELETE等。
(2)contentType:指定请求的数据类型,MIME。默认是’application/x-www-form-urlencoded; charset=UTF-8’,也可设定为其他类型如application/json
(3)url: 请求的REST API地址, 绝对路径,或者相对路径。
(4)data:发送的数据。可以是JS对象,也可以是JSON字符串。
(5)dataType:期望的返回数据类型,默认是自动识别。可以设定为“xml, json, script, html”
(6)headers:设置请求头,默认{}, 值为JS对象。
(7)method:请求方式,GET, POST, 与type功能相同,新版推荐使用method, 1.9.0版本之前必须使用type
(8)beforeSend: Function( jqXHR jqXHR, PlainObject settings ) 发送前回调函数,可以返回false,阻止请求发送,一般用于发送前检查,验证。
(9)complete: Function( jqXHR jqXHR, String textStatus ) 方法结束后回调函数。
(10)success: Function( Anything data, String textStatus, jqXHR jqXHR ) 请求成功后的回调函数,data为接收的数据
(11)error:Function( jqXHR jqXHR, String textStatus, String errorThrown ) 请求失败后的回调函数,errorThrown为错误信息。

$.ajax()函数返回jqXHR对象。该对象提供了回调函数的替代函数:
jqXHR.done(function( data, textStatus, jqXHR ) {}); 取代success回调函数。
解决了jQuery AJAX函数的回调函数坑问题。

jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {}); 用于取代error回调函数.

jqXHR.always(function( data|jqXHR, textStatus, jqXHR|errorThrown ) { }); (added in jQuery 1.6) 用于取代complete回调函数,无论成功还是失败的调用always函数总能执行。

$.ajax函数的测试案例:请求后台的取得指定部门信息的REST API。
案例中REST API地址使用相对路径,表示HTML页面与后台在一个Web站点内。如果是访问外部的REST API需要使用全URL地址
如果HTML页面站点与REST API不在一个Web应用内,REST API的站点需要设置跨域访问。

$(function(){
	
	$.ajax({
		url:"../api/department/get.mvc", 
		data:{no:1},
		method:"GET",
		dataType:"json",
		
	}).done(function(resultData,textStatus, jqXHR){
		
		var viewdata="<div>部门名称:"+resultData.result.name+"<td>";
		viewdata+="<div>状态:"+resultData.status+"<td>";
		viewdata+="<div>操作信息:"+resultData.message+"<td>";
		
		$("div#area01").html(viewdata);
		
	}).fail(function(jqXHR, textStatus, errorThrown ) {
		alert("ERROR01");
		alert(textStatus);
	});;
	
});

ajax()函数是比较底层的函数,选项较多。实际开发中直接使用ajax函数比较少,一般都使用其高级的简化函数,如$.get, $.post, $.getJSON, $.load函数。

getJSON()函数

getJSON函数是简化版的ajax函数,其中dataType默认是json。其语法为:
$.getJSON( url [, data ] [, success ] )
参数:url指定请求地址;data是传递的参数对象,格式{属性:值};success是成功后的回调函数。方法返回jqXHR对象,也可以使用done和fail函数。
调用REST API案例:

$.getJSON("../api/department/get.mvc",{no:1},function(resultData){
		var viewdata="<div>部门名称:"+resultData.result.name+"<td>";
		viewdata+="<div>状态:"+resultData.status+"<td>";
		viewdata+="<div>操作信息:"+resultData.message+"<td>";
		$("div#area02").html(viewdata);		
});

或者使用done

$.getJSON("../api/department/get.mvc",{no:1}).done(function(resultData){
		var viewdata="<div>部门名称:"+resultData.result.name+"<td>";
		viewdata+="<div>状态:"+resultData.status+"<td>";
		viewdata+="<div>操作信息:"+resultData.message+"<td>";
		$("div#area02").html(viewdata);		
}).fail(function(){});

可见使用getJSON要比ajax函数简单得多。

使用done和fail的案例:

var jqxhr = $.getJSON( "example.json", function() {
  console.log( "success" );
})
.done(function() {
   console.log( "second success" );
})
.fail(function() {
    console.log( "error" );
})
.always(function() {
    console.log( "complete" );
});

get() 函数

$.get函数也是$.ajax函数的简化版,底层依然调用ajax函数。
语法:jQuery.get( url [, data ] [, success ] [, dataType ] )
比getJSON函数增加dataType参数,指定返回的数据类型,如json,xml,html等。
使用案例:

//测试get
$.get("../api/department/get.mvc",{no:1},function(resultData){
	var viewdata="<div>部门名称:"+resultData.result.name+"<td>";
	viewdata+="<div>状态:"+resultData.status+"<td>";
	viewdata+="<div>操作信息:"+resultData.message+"<td>";
	$("div#area03").html(viewdata);		
},"json");

post函数

$.post请求一般用于调用增加操作的REST API。
语法:jQuery.post( url [, data ] [, success ] [, dataType ] )

其参数与get函数一样,只是实现post方式请求
使用案例:调用增加部门的REST API

//测试post
$.post("../api/department/add.mvc",{code:"DD99",name:"军工部"},function(resultData){
		var viewdata="<div>状态:"+resultData.status+"<td>";
		viewdata+="<div>操作信息:"+resultData.message+"<td>";
		$("div#area04").html(viewdata);		
},"json");

案例中通过post函数的第2个参数:{code:“DD99”,name:“军工部”}传递给REST API发送的数据,通过第3个参数的函数取得REST API返回的值。
并把状态和操作信息显示在页面上。
实际开始时应用把form表单元素的输入值取得。

load函数

$.load函数用于嵌入其他页面到此函数的位置。这是使用jQuery开发SPA(单网页 Single Page Application)应用的重点函数。
语法:jQuery.load( url [, data ] [, success ]
其中:url指定嵌入的页面的地址,data是要传递给url的参数,success是载入成功后的回调函数。

SPA应用并不是整个Web项目只有一个HTML页面,而是多个HTML页面,每个页面完成一个业务信息的显示和输入。系统只有一个主页如index.html, 浏览器只访问该主页页面,点击主页的功能菜单,在主页面中使用load函数载入功能对应的HTML页面到主页的指定区域中(通常使用div标记指定)。
从操作者角度看,浏览器永远在index.html的请求地址上,不会改变,感觉就是访问一个页面。

$.load("subfunction.html",{no:1},function(resultData){
alert("页面载入成功");
});
//注意:最新版的jQuery已经删除了load函数,可以使用get替代之。
//使用$.get函数替代$.load函数
$.get("subfunction.html",{no:1},function(resultData){
	$("div#area05").html(resultData);
});

JS 对象与JSON的相互转换

1、JS对象转JSON
方式:JSON.stringify(obj)

var json = {"name":"iphone","price":666}; //创建对象;
var jsonStr = JSON.stringify(json);       //转为JSON字符串
console.log(jsonStr);

2.JS数组转JSON

//数组转json串
var arr = [1,2,3, { a : 1 } ];
JSON.stringify( arr );

3、JS对象数组转JSON

//数组转json串
var arr = [1,2,3, { a : 1 } ];
JSON.stringify( arr );

4、JSON转JS数组

//json字符串转数组
var jsonStr = '[1,2,3,{"a":1}]';
var jsarr=JSON.parse( jsonStr );
alert(jsarr[0]);

5、JSON转JS对象

var jsonString = '{"bar":"property","baz":3}';
var jsObject = JSON.parse(jsonString);    //转换为json对象
alert(jsObject.bar);    //取json中的值

表单提交的AJAX编程

在使用前端框架与后端REST API交互模式中,对于表单提交,一般是为表单设置表单提交触发事件,在事件处理函数中取得表单元素输入的数据,组装提交的数据,再调用jQuery的post函数。
如下部门增加表单

<form id="departmentAddForm" action="../api/department/add.mvc" method="post" >
部门编码:<input type="text" name="code" />
部门名称:<input type="text" name="name" />
<input type="submit" value="提交" >
</form>

使用jQuery捕获其表单提交的事件:

$("form#departmentAddForm").on("submit",function(event){
   event.preventDefault(); //阻止表单的默认提交
    var code=$("form#departmentAddForm input[name='code']").val();
    var name=$("form#departmentAddForm input[name='name']").val();
    $.post("../api/department/add.mvc", {code:code,name:name}, function(resultData){
       if(resultData.status=="OK"){
            loadList();  //重新刷新列表显示
       }
       alert(resultData.message); //任何情况均显示返回的操作信息
       
   });
});

上面代码中通过收到取得各个表单元素输入的值,在组成提交参数对象:
{code:code,name:name}
如果表单元素比较少还可以,如果表单元素比较多,并且含有文件上传表单元素,处理是非常麻烦的。
在jQuery模式下,推荐使用一个专门处理表单提交的框架jquery.form

下载框架文件:jquery.form.min.js,并保存到项目的jquery目录中,再引入到使用该框架的页面中。

<script type="text/javascript" src="jquery-3.5.1.min.js"></script>
<script type="text/javascript" src="jquery.form.min.js"></script>
<script type="text/javascript" src="main.js"></script>

记住要放置在引入jquery的下面。
正常写表单代码:

<form id="departmentAddForm" 
action="../api/department/add.mvc" method="post" >
部门编码:<input type="text" name="code" />
部门名称:<input type="text" name="name" />
<input type="submit" value="提交" >
</form>

在main.js中加入表单的拦截代码:

$('form#departmentAddForm').ajaxForm(function(resultData) {
     alert(resultData.message);
});

当点击提交按钮后,jquery from自动使用ajaxForm函数实现表单的异步提交,其回调函数参数接收服务器端返回的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值