First, the goal
- Understand what Restful is, the basic concept and style;
- can use Springboot to achieve a set of basic Restful style interface;
- Use swagger to generate clear interface documentation.
Ii. the basics of Restful
What is rest
From the definition of Wikipedia: Rest is representational state transfer (English: representational, Transfer, rest)
is a software architecture style proposed by Dr. Roy Fielding (the main contributor to the HTTP specification) in a 2000 paper.
is a design and development method for network application, which can reduce the complexity of development and increase the scalability of the system.
In layman's terms, rest is a set of architectural constraints, and many of these guidelines take advantage of existing web standards capabilities.
The ultimate goal is to simplify the design and development of the current business layer.
The RESTful API is an API that conforms to the rest schema constraints, which have been very popular in the early years, but most developers still
Being in a wait-and-see state does not necessarily apply immediately. This belief is closely related to the maturity and atmosphere of the technical community at that time.
In any case, as the microservices architecture is so popular today, Restful APIs have become a must-have standard design style .
Key points
Understanding Restful styles requires understanding the following points:
Resource refers to an abstract information entity, which can be a user, a song, an article, as long as the object that can be referenced is a resource.
Each resource is typically mapped to a URI that can be accessed by accessing the URI.
- Presentation of resources
Resource representation (representation) refers to the external representation of resources
such as a post, can be displayed in HTML format, or through XML, JSON and other formats to the client.
As mentioned in the previous article (Springboot-scope), the HTTP protocol uses MIME to uniformly define the format criteria for data information.
Generally,accept,content-type can be used to specify the client and the server acceptable information format, and this is the expression of resources
During HTTP access, the state of the resource changes. Here are some of the following verbs:
name |
Semantics |
GET |
Get Resources |
POST |
New Resource |
PUT |
Update Resources |
DELETE |
Delete a resource |
For different access methods, the server generates the corresponding behavior and causes the resource state to be transformed.
About stateless
Restful is a stateless design, which means that requests in the interactive process should contain all the information needed,
Without having to rely on an existing context.
However, there are some breaches in Java EE, such as setting jsessionid in cookies,
Passing this value between multiple requests is a unique identifier for the session, which identifies the server that must hold the session state data.
The Playframework framework implements a stateless session that encrypts and encodes session data into a cookie.
In this way, the client's request will carry all the information directly, is a stateless request * *, which is very beneficial to the scalability of the server.
Iii. springboot Realization of Restful
Next, we use Springboot to implement a restful style sample.
Description
Based on the case of Petstore (Pet Shop), the Pet (PET) under the name of a customer is modified and deleted.
1. Entity definition
Customer
public class Customer { private String name; public Customer() { super(); } public Customer(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
The Customer contains only one name attribute, which we assume is the only flag.
Pet
public class Pet { private String petId; private String name; private String type; private String description; public String getPetId() { return petId; } public void setPetId(String petId) { this.petId = petId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; }}
Pet contains the following properties
Property name |
Description |
Petid |
Pet ID Number |
Name |
Pet Name |
Type |
Pet Type |
Description |
Description of the Pet |
2. URL Resources
Based on the principle of restful, we define the following set of URLs:
Interface |
Method |
URL |
Add a Pet |
POST |
/rest/pets/{customer} |
Get a list of pets |
GET |
/rest/pets/{customer} |
Get pet information |
GET |
/rest/pets/{customer}/{petid} |
Update your pet information |
PUT |
/rest/pets/{customer}/{petid} |
Delete a pet |
DELETE |
/rest/pets/{customer}/{petid} |
3. Data management
A Petmanager class is then implemented to simulate the pruning of pet data in memory
The code is as follows:
@Componentpublic class Petmanager {private static map<string, customer> customers = new Concurrenthashmap<str ING, customer> (); private static map<string, map<string, pet>> pets = new concurrenthashmap<string, map<string, Pet>& gt; (); @PostConstruct public void init () {string[] customernames = new string[] {"Lilei", "Hanmeimei", "Jim Green"}; for (String customername:customernames) {customers.put (CustomerName, New Customer (CustomerName)); }}/** * Get customer * * @param customer * @return */public customer GetCustomer (stri Ng customer) {if (Stringutils.isempty (customer)) {return null; } return Customers.get (customer); }/** * Get the pet list in customer name * * @param customer * @return */public list<pet> getpets (St Ring customer) {if (Stringutils.isempty (customer)) {return collections.emptylist (); } if (!pets.containskey (customer)) {return collections.emptylist (); Return Pets.get (Customer). values (). Stream (). Collect (Collectors.tolist ()); }/** * Get a pet * * @param customer * @param petid * @return */public Pet Getpet (String cu Stomer, String Petid) {if (Stringutils.isempty (customer) | | Stringutils.isempty (Petid)) {return null; } if (!pets.containskey (customer)) {return null; } return Pets.get (Customer). get (Petid); }/** * Delete Pet * * @param customer * @param petid * @return */public boolean removepet (stri Ng customer, String petid) {if (Stringutils.isempty (customer) | | Stringutils.isempty (Petid)) {return false; } if (!pets.containskey (customer)) {return false; } return Pets.get (Customer). Remove (petid)! = NULL; }/** * Add pet * * @param cUstomer * @param pet * @return */Public pet Addpet (String customer, pet Pet) {if (Stringutils.isem Pty (Customer) | | Pet = = null) {return null; } map<string, pet> customerpets = null; if (!pets.containskey (customer)) {customerpets = new linkedhashmap<string, pet> (); Map<string, pet> previous = Pets.putifabsent (customer, customerpets); already exists if (previous! = null) {customerpets = previous; }} else {customerpets = Pets.get (customer); } if (Pet.getpetid () = = null) {Pet.setpetid (Uuid.randomuuid (). toString ()); } customerpets.put (Pet.getpetid (), pet); return pet; }/** * Update a pet * * @param customer * @param petpojo * @return */public Pet Updatepet (stri Ng customer, Pet Petpojo) {if (Stringutils.isempty (customer) | | | petpojo = = NULL) { return null; } if (Petpojo.getpetid () = = null) {return null; } Pet pet = Getpet (Customer, Petpojo.getpetid ()); Pet.settype (Petpojo.gettype ()); Pet.setname (Petpojo.getname ()); Pet.setdescription (Petpojo.getdescription ()); return pet; }}
4. Control Layer Implementation
Springboot provides @RestControllerfor quickly defining a restful-style controller class
@[email protected] + @Controller
@RestController @requestmapping ("/rest/pets/{customer}") public class Restapicontroller {@Autowired private petmanage R DataManager; /** * Add Pet * * @param customer * @param pet * @return */@PostMapping public responseentity <Object> Addpet (@PathVariable String customer, @RequestBody Pet Pet) {Validatecustomer (customer); Pet Newpet = Datamanager.addpet (customer, PET); Returns the 201.created if (newpet! = null) {URI location = Servleturicomponentsbuilder.fromcurrentrequest (). Path ("/{petid}"). Buildandexpand (Newpet.getpetid ()). Touri (); return responseentity.created (location). build (); }//returns 204.noContent return Responseentity.nocontent (). build (); /** * Get Pet List * * @param customer * @return */@GetMapping @ResponseBody public list< Pet> listpets (@PathVariable String customer) {Validatecustomer (customer); List<pet> pets = Datamanager.getpets (customer); return pets; /** * Get a pet * * @param customer * @param petid */@GetMapping ("/{petid}") @ResponseBody Public Pet Getpet (@PathVariable string customer, @PathVariable string petid) {Validatecustomer (customer); Validatepet (Customer, Petid); Pet Pet = Datamanager.getpet (Customer, Petid); return pet; */** * UPDATE pet information * * @param customer * @param petid * @param pet */@PutMapping ("/{petid}") Public responseentity<object> Updatepet (@PathVariable string customer, @PathVariable string Petid, @RequestBody P ET pet) {validatecustomer (customer); Validatepet (Customer, Petid); Pet.setpetid (Petid); Pet Petobject = Datamanager.updatepet (customer, PET); if (petobject! = null) {return Responseentity.ok (petobject); } return Responseentity.nocontent (). build (); } /** *Delete a pet * * @param customer * @param petid * @return */@DeleteMapping ("/{petid}") Public respon Seentity<object> Removepet (@PathVariable string customer, @PathVariable string petid) {Validatecustomer (cust Omer); Validatepet (Customer, Petid); Datamanager.removepet (Customer, Petid); Return Responseentity.ok (). build (); }
The above code has realized the complete deletion and modification of the semantics.
In restful-style API interface definitions, HTTP status codes are often used to denote different results, such as some wrong state types.
Here we customer, Pet for the existence of check, if the resource does not exist return 404_notfound.
/** * 校验customer是否存在 * * @param customer */ private void validateCustomer(String customer) { if (dataManager.getCustomer(customer) == null) { throw new ObjectNotFoundException(String.format("the customer['%s'] is not found", customer)); } } /** * 校验pet是否存在 * * @param customer */ private void validatePet(String customer, String petId) { if (dataManager.getPet(customer, petId) == null) { throw new ObjectNotFoundException(String.format("the pet['%s/%s'] is not found", customer, petId)); } }
Custom Exception Blocking
/** * 自定义异常,及拦截逻辑 * * @author atp * */ @SuppressWarnings("serial") public static class ObjectNotFoundException extends RuntimeException { public ObjectNotFoundException(String msg) { super(msg); } } @ResponseBody @ExceptionHandler(ObjectNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String objectNotFoundExceptionHandler(ObjectNotFoundException ex) { return ex.getMessage(); }
5. Interface Verification 1. Add a Pet
Url
POST Http://{{server}}/rest/pets/lilei
Request Content
{ "name": "Smart Baby", "description": "very small and smart also.", "type": "Dog"}
return example
201 createdContent-Length →0Date →Mon, 09 Jul 2018 05:15:01 GMTLocation →http://localhost:8090/rest/pets/LiLei/b5400334-e7b3-42f1-b192-f5e7c3193543
2. Get a list of pets
Url
GET Http://{{server}}/rest/pets/lilei
Request Content
<Empty>
return example
200 OKContent-Type →application/json;charset=UTF-8Date →Mon, 09 Jul 2018 05:23:27 GMTTransfer-Encoding →chunked[ { "petId": "b5400334-e7b3-42f1-b192-f5e7c3193543", "name": "Smart Baby", "type": "Dog", "description": "very small and smart also." }, { "petId": "610780af-94f1-4011-a175-7a0f3895163d", "name": "Big Cat", "type": "Cat", "description": "very old but I like it." }]
3. Check your pet information
Url
GET http://{{server}}/rest/pets/lilei/b5400334-e7b3-42f1-b192-f5e7c3193543
Request Content
<Empty>
return example
200 OKContent-Type →application/json;charset=UTF-8Date →Mon, 09 Jul 2018 05:25:24 GMTTransfer-Encoding →chunked{ "petId": "b5400334-e7b3-42f1-b192-f5e7c3193543", "name": "Smart Baby", "type": "Dog", "description": "very small and smart also."}
4. Update your pet information
Url
PUT http://{{server}}/rest/pets/lilei/b5400334-e7b3-42f1-b192-f5e7c3193543
Request Content
{ "name": "Big Cat V2", "description": "I don't like it any more", "type": "Cat"}
return example
200 OKContent-Type →application/json;charset=UTF-8Date →Mon, 09 Jul 2018 05:31:28 GMTTransfer-Encoding →chunked{ "petId": "a98e4478-e754-4969-851b-bcaccd67263e", "name": "Big Cat V2", "type": "Cat", "description": "I don't like it any more"}
5. Delete a pet
Url
DELETE http://{{server}}/rest/pets/lilei/b5400334-e7b3-42f1-b192-f5e7c3193543
Request Content
<empty>
return example
200 OKContent-Length →0Date →Mon, 09 Jul 2018 05:32:51 GMT
Related error
- Customer does not exist: 404 the customer[' test ' is not found
- Pet not present: 404 the Pet[' lilei/b5400334-e7b3-42f1-b192-f5e7c31935431 ' is not found
Iv. Use of Swagger
About Swagger
Swagger is currently a very popular API Design development framework (based on OPENAPI),
Available for API design, management, code generation, mock testing, and more.
At present, the application of swagger is very wide, it covers more open source modules, which will use Swagger-ui to implement API online doc generation.
Introducing Dependencies
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Defining the API Configuration
@EnableSwagger2 @configurationpublic class Swaggerconfig {public static final String VERSION = "1.0.0"; @Value ("${swagger.enable}") Private Boolean enabled; Apiinfo Apiinfo () {return new Apiinfobuilder (). Title ("Pet Api Definition"). Description ("The Petstore CRUD Example"). License ("Apache 2.0") . Licenseurl ("http://www.apache.org/licenses/LICENSE-2.0.html"). Termsofserviceurl ("") . version. Contact (new Contact ("," "," [email protected] "). Build () ; } @Bean Public Docket Customimplementation () {return new docket (documentationtype.swagger_2). Select () . APIs (Requesthandlerselectors.withclassannotation (Api.class)). Build (). Enable (Enab LED). Apiinfo (Apiinfo ()); }}
The @EnableSwagger2 declares the swagger enabled, and the docket bean definition is the gateway to the API configuration.
You can set the API name, version number, scan range, and so on.
Declaring API Descriptions
Add a declaration on the API to the original controller method, as follows:
@Api(value = "Pet Restful api")@RestController@RequestMapping("/rest/pets/{customer}")public class RestApiController { @ApiOperation("添加宠物") @ApiImplicitParams({ @ApiImplicitParam(paramType = "path", name = "customer", dataType = "String", required = true, value = "客户名", defaultValue = ""), @ApiImplicitParam(paramType = "body", name = "pet", dataType = "Pet", required = true, value = "pet 请求", defaultValue = "") }) @ApiResponses({ @ApiResponse(code = 201, message = "添加成功"), @ApiResponse(code = 404, message = "资源不存在") }) @PostMapping public ResponseEntity<Object> addPet(@PathVariable String customer, @RequestBody Pet pet) { ...
To be able to describe the document description of the returned object, make an API declaration for the Pet class:
@ApiModel("宠物信息")public class Pet { @ApiModelProperty(name="petId", value="宠物ID") private String petId; @ApiModelProperty(name="name", value="宠物名称") private String name; @ApiModelProperty(name="type", value="宠物类型") private String type; @ApiModelProperty(name="description", value="宠物描述") private String description;
Related annotations:
Annotations |
Description |
@ApiModelProperty |
Used on fields in the Access parameter object |
@Api |
For controller classes |
@ApiOperation |
For the Controller method, describes the operation |
@ApiResponses |
For the Controller method that describes the response |
@ApiResponse |
Used in @apiresponses to describe a single response result |
@ApiImplicitParams |
The method used for the controller, which describes the entry parameter |
@ApiImplicitParam |
Used in @apiimplicitparams to describe a single entry parameter |
@ApiModel |
Used to return an object class |
accessing documents
Finally, to access http://localhost:8000/swagger_ui.html, you can see the resulting document interface:
Reference documents
Springboot-tutorials-bookmarks
Nanyi-Understanding the RESTful architecture
Sprintboot-using the Swagger Publishing API
Swagger-2-documentation-for-spring-rest-api
Welcome to continue to pay attention to "American Code Master's Tutorial series-springboot", look forward to more exciting content ^-^
Tutorial Series-springboot-restful Application