了解Restful是什么,基本概念及风格;
能使用SpringBoot实现一套基础的Restful风格接口;
利用Swagger生成清晰的接口文档。
什么是REST
摘自百科的定义:REST即表述性状态转移(英文:RepresentationalStateTransfer,简称REST)是RoyFielding博士(HTTP规范主要贡献者)在2000年的论文中提出来的一种软件架构风格。是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
通俗点说,REST就是一组架构约束准则;在这些准则中,有不少是利用了现有的WEB标准能力。而最终的目的则是简化当前业务层的设计及开发工作。
关键要点
理解Restful风格需要理解以下几点:
资源
资源的表述
资源表述(Representation)指的则是资源的外在表现形式比如一个帖子,可以通过HTML格式展现,也可以通过XML、JSON等格式输出到客户端。
状态转移
在HTTP访问过程中,资源的状态发生变化。这里会涉及到以下的几个动词:
对于不同的访问方法,服务器会产生对应的行为并促使资源状态产生转换。
关于无状态
Restful是无状态的设计,这点意味着交互过程中的请求应该能包含所有需要的信息,而不需要依赖于已有的上下文。然而JavaEE中存在一些违背的做法,比如Cookie中设置JSESSIONID,在多次请求间传递该值作为会话唯一标识,这标识着服务端必须保存着这些会话状态数据。
PlayFramework框架实现了无状态的Session,其将会话数据经过加密编码并置入Cookie中,这样客户端的请求将直接携带上全部的信息,是无状态的请求**,这点非常有利于服务端的可扩展性。
接下来,我们利用SpringBoot来实现一个Restful风格的样例。
说明基于PetStore(宠物店)的案例,实现对某顾客(Customer)名下的宠物(Pet)的增删改查。
Customer
publicclassCustomer{
privateStringname;
publicCustomer(){
super();
}
publicCustomer(Stringname){
this.name=name;
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
Customer只包含一个name属性,我们假定这是唯一的标志。
Pet
publicclassPet{
privateStringpetId;
privateStringtype;
privateStringdescription;
publicStringgetPetId(){
returnpetId;
publicvoidsetPetId(StringpetId){
this.petId=petId;
publicStringgetType(){
returntype;
publicvoidsetType(Stringtype){
this.type=type;
publicStringgetDescription(){
returndescription;
publicvoidsetDescription(Stringdescription){
this.description=description;
Pet包含了以下几个属性
基于Restful的原则,我们定义了以下的一组URL:
接下来实现一个PetManager类,用于模拟在内存中对Pet数据进行增删改查代码如下:
@Component
publicclassPetManager{
privatestaticMap
privatestaticMap
@PostConstruct
publicvoidinit(){
for(StringcustomerName:customerNames){
customers.put(customerName,newCustomer(customerName));
/**
*获取customer
*
*@paramcustomer
*@return
*/
publicCustomergetCustomer(Stringcustomer){
if(StringUtils.isEmpty(customer)){
returnnull;
returncustomers.get(customer);
*获取customer名下的pet列表
publicList
returnCollections.emptyList();
if(!pets.containsKey(customer)){
returnpets.get(customer).values().stream().collect(Collectors.toList());
*获取某个pet
*@parampetId
publicPetgetPet(Stringcustomer,StringpetId){
if(StringUtils.isEmpty(customer)||StringUtils.isEmpty(petId)){
returnpets.get(customer).get(petId);
*删除pet
publicbooleanremovePet(Stringcustomer,StringpetId){
returnfalse;
returnpets.get(customer).remove(petId)!=null;
*添加pet
*@parampet
publicPetaddPet(Stringcustomer,Petpet){
if(StringUtils.isEmpty(customer)||pet==null){
Map
customerPets=newLinkedHashMap
Map
//已经存在
if(previous!=null){
customerPets=previous;
}else{
customerPets=pets.get(customer);
if(pet.getPetId()==null){
pet.setPetId(UUID.randomUUID().toString());
customerPets.put(pet.getPetId(),pet);
returnpet;
*更新某个pet
*@parampetPojo
publicPetupdatePet(Stringcustomer,PetpetPojo){
if(StringUtils.isEmpty(customer)||petPojo==null){
if(petPojo.getPetId()==null){
Petpet=getPet(customer,petPojo.getPetId());
pet.setType(petPojo.getType());
pet.setName(petPojo.getName());
pet.setDescription(petPojo.getDescription());
SpringBoot提供了@RestController,用于快速定义一个Restful风格的Controller类@RestController=@ResponseBody+@Controller
@RestController
publicclassRestApiController{
@Autowired
privatePetManagerdataManager;
*添加宠物
@PostMapping
publicResponseEntity
validateCustomer(customer);
PetnewPet=dataManager.addPet(customer,pet);
//返回201.created
if(newPet!=null){
.buildAndExpand(newPet.getPetId()).toUri();
returnResponseEntity.created(location).build();
//返回204.noContent
returnResponseEntity.noContent().build();
*获取宠物列表
@GetMapping
@ResponseBody
publicList
List
returnpets;
*获取某个宠物
publicPetgetPet(@PathVariableStringcustomer,@PathVariableStringpetId){
validatePet(customer,petId);
Petpet=dataManager.getPet(customer,petId);
*更新宠物信息
publicResponseEntity
pet.setPetId(petId);
PetpetObject=dataManager.updatePet(customer,pet);
if(petObject!=null){
returnResponseEntity.ok(petObject);
*删除某个宠物
publicResponseEntity
dataManager.removePet(customer,petId);
returnResponseEntity.ok().build();
上述代码中已经实现了完整的增删改查语义。在Restful风格的API接口定义中,往往会引用HTTP状态码用于表示不同的结果,比如一些错误的状态类型。
这里我们对Customer、Pet进行存在性校验,若资源不存在返回404_NotFound。
*校验customer是否存在
privatevoidvalidateCustomer(Stringcustomer){
if(dataManager.getCustomer(customer)==null){
*校验pet是否存在
privatevoidvalidatePet(Stringcustomer,StringpetId){
if(dataManager.getPet(customer,petId)==null){
自定义异常拦截
*自定义异常,及拦截逻辑
*@authoratp
publicstaticclassObjectNotFoundExceptionextendsRuntimeException{
publicObjectNotFoundException(Stringmsg){
super(msg);
@ExceptionHandler(ObjectNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
publicStringobjectNotFoundExceptionHandler(ObjectNotFoundExceptionex){
returnex.getMessage();
{
返回示例
201created
Content-Length→0
Date→Mon,09Jul201805:15:01GMT
200OK
Content-Type→application/json;charset=UTF-8
Date→Mon,09Jul201805:23:27GMT
Transfer-Encoding→chunked
[
},
]
Date→Mon,09Jul201805:25:24GMT
Date→Mon,09Jul201805:31:28GMT
Date→Mon,09Jul201805:32:51GMT
关于Swagger
Swagger是目前非常流行的一个API设计开发框架(基于OpenApi),可用于API的设计、管理、代码生成以及Mock测试等。
目前Swagger的应用非常广,其涵盖的开源模块也比较多,这里将使用swagger-ui实现API在线DOC的生成。
引入依赖
定义API配置
@EnableSwagger2
@Configuration
publicclassSwaggerConfig{
privatebooleanenabled;
ApiInfoapiInfo(){
returnnewApiInfoBuilder().
.version(VERSION)
.build();
@Bean
publicDocketcustomImplementation(){
returnnewDocket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))