springboot Restful 风格开发

到底什么样的风格才是RESTful风格呢?

1,约束请求命令如下:

GET,获取资源。例如:/employees表示获取列表资源,/employees/{id}表示获取单个对象资源。
POST,新增。例如:/employees,body为json对象,表示新增。
PUT,更新。例如:/employees/{id},body为json对象,表示更新。
DELETE,删除。例如: /employees/{id},表示删除。
2,约束返回结果: 返回数据为列表,则每个对象资源附加自己的资源链接、列表资源链接以及可操作性链接。

参考链接:

官网demo地址:https://spring.io/guides/tutorials/bookmarks/
官网demo的git地址:https://github.com/spring-guides/tut-rest/

1 get 获取所有员工

curl -G http://localhost:8080/employees
{
	"_embedded": {
		"employeeList": [{
			"id": 1,
			"firstName": "Bilbo",
			"lastName": "Baggins",
			"role": "burglar",
			"name": "Bilbo Baggins",
			"_links": {
				"self": {
					"href": "http://localhost:8080/employees/1"
				},
				"employees": {
					"href": "http://localhost:8080/employees"
				}
			}
		}, {
			"id": 2,
			"firstName": "Frodo",
			"lastName": "Baggins",
			"role": "thief",
			"name": "Frodo Baggins",
			"_links": {
				"self": {
					"href": "http://localhost:8080/employees/2"
				},
				"employees": {
					"href": "http://localhost:8080/employees"
				}
			}
		}]
	},
	"_links": {
		"self": {
			"href": "http://localhost:8080/employees"
		}
	}
}

返回所有员工,以及每个员工对应的可调用的接口。每次都是动态生成。相比传统http模式,更加灵活。

从上面接口返回的_links–>self,获取指定员工信息

curl -G http://localhost:8080/employees/1
{
	"id": 1,
	"firstName": "Bilbo",
	"lastName": "Baggins",
	"role": "burglar",
	"name": "Bilbo Baggins",
	"_links": {
		"self": {
			"href": "http://localhost:8080/employees/1"
		},
		"employees": {
			"href": "http://localhost:8080/employees"
		}
	}
}

post 请求新增用户

curl localhost:8080/employees  -X POST -d  "{\"firstName\":\"nigulasi\",\"lastName\":\"zhaosi\",\"role\":\"yingdi\"}"  --header "Content-Type: application/json"
{
	"id": 3,
	"firstName": "nigulasi",
	"lastName": "zhaosi",
	"role": "yingdi",
	"name": "nigulasi zhaosi",
	"_links": {
		"self": {
			"href": "http://localhost:8080/employees/3"
		},
		"employees": {
			"href": "http://localhost:8080/employees"
		}
	}
}

put 请求 更新员工信息,3代表指定员工id,具体信息通过data传递。

curl localhost:8080/employees/3  -X PUT -d  "{\"firstName\":\"xie\",\"lastName\":\"guangkun\",\"role\":\"zhemowang\"}"  --header "Content-Type: application/json"
{
	"id": 3,
	"firstName": "xie",
	"lastName": "guangkun",
	"role": "zhemowang",
	"name": "xie guangkun",
	"_links": {
		"self": {
			"href": "http://localhost:8080/employees/3"
		},
		"employees": {
			"href": "http://localhost:8080/employees"
		}
	}
}

delete 请求 删除指定员工 3代表指定员工id

curl -X DELETE  http://localhost:8080/employees/3

如果删除成功,此接口没有返回结果。

使用H2作为内存数据库,创建员工

@Configuration
class LoadDatabase {

	private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class);

	@Bean
	CommandLineRunner initDatabase(EmployeeRepository repository) {

		return args -> {
			// tag::new_constructor[]
			log.info("Preloading " + repository.save(new Employee("Bilbo", "Baggins", "burglar")));
			log.info("Preloading " + repository.save(new Employee("Frodo", "Baggins", "thief")));
			// end::new_constructor[]
		};
	}
}

使用的jar包

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>

原理解释:
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的,作用为:配置spring容器(应用上下文)
@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的,作用为:注册bean对象
(1)@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与标注的方法名相同;
(2)@Bean注解默认作用域为单例singleton作用域,可通过@Scope(“prototype”)设置为原型作用域;
(3)既然@Bean的作用是注册bean对象,那么完全可以使用@Component、@Controller、@Service、@Ripository等注解注册bean,当然需要配置@ComponentScan注解进行自动扫描。

使用JPA访问数据库

interface EmployeeRepository extends JpaRepository<Employee, Long> {

}
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

JPA创建实体类

@Entity
class Employee {

	private @Id @GeneratedValue Long id;
	private String firstName;
	private String lastName;
	private String role;

	Employee() {}

	Employee(String firstName, String lastName, String role) {

		this.firstName = firstName;
		this.lastName = lastName;
		this.role = role;
	}

	public String getName() {
		return this.firstName + " " + this.lastName;
	}

	public void setName(String name) {
		String[] parts = name.split(" ");
		this.firstName = parts[0];
		this.lastName = parts[1];
	}

	public Long getId() {
		return this.id;
	}

	public String getFirstName() {
		return this.firstName;
	}

	public String getLastName() {
		return this.lastName;
	}

	public String getRole() {
		return this.role;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public void setRole(String role) {
		this.role = role;
	}

	@Override
	public boolean equals(Object o) {

		if (this == o)
			return true;
		if (!(o instanceof Employee))
			return false;
		Employee employee = (Employee) o;
		return Objects.equals(this.id, employee.id) && Objects.equals(this.firstName, employee.firstName)
				&& Objects.equals(this.lastName, employee.lastName) && Objects.equals(this.role, employee.role);
	}

	@Override
	public int hashCode() {
		return Objects.hash(this.id, this.firstName, this.lastName, this.role);
	}

	@Override
	public String toString() {
		return "Employee{" + "id=" + this.id + ", firstName='" + this.firstName + '\'' + ", lastName='" + this.lastName
				+ '\'' + ", role='" + this.role + '\'' + '}';
	}

增加@ControllerAdvice注解,实现异常处理器:

@ControllerAdvice
class EmployeeNotFoundAdvice {

	@ResponseBody
	@ExceptionHandler(EmployeeNotFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	String employeeNotFoundHandler(EmployeeNotFoundException ex) {
		return ex.getMessage();
	}
}

class EmployeeNotFoundException extends RuntimeException {

	EmployeeNotFoundException(Long id) {
		super("Could not find employee " + id);
	}
}

二、restful的http服务
pom.xml增加hateoas依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

控制器更改,在原来的返回json基础之上附加操作链接

@GetMapping("/employees")
CollectionModel<EntityModel<Employee>> all() {
    List<EntityModel<Employee>> employees = repository.findAll().stream()
            .map(employee -> EntityModel.of(employee,
                    linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel(), // 附加自身链接
                    linkTo(methodOn(EmployeeController.class).all()).withRel("employees"))) // 附加all操作链接
            .collect(Collectors.toList());
    return CollectionModel.of(employees, linkTo(methodOn(EmployeeController.class).all()).withSelfRel()); // 附加自身链接
}
@GetMapping("/employees/{id}")
EntityModel<Employee> one(@PathVariable Long id) {
    Employee employee = repository.findById(id) //
            .orElseThrow(() -> new EmployeeNotFoundException(id));

    return EntityModel.of(employee, linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel(), // 附加自身链接
            linkTo(methodOn(EmployeeController.class).all()).withRel("employees")); // 附加all操作链接
}

三、附加可操作链接的restful服务
订单实体转换器,如果订单状态为可执行的订单则附加取消和完成链接 :

@Component
public class OrderModelAssembler implements RepresentationModelAssembler<Order, EntityModel<Order>> {
   @Override
   public EntityModel<Order> toModel(Order order) {
      // Unconditional links to single-item resource and aggregate root
      EntityModel<Order> orderModel = EntityModel.of(order,
            linkTo(methodOn(OrderController.class).one(order.getId())).withSelfRel(),
            linkTo(methodOn(OrderController.class).all()).withRel("orders"));
      // Conditional links based on state of the order
      if (order.getStatus() == Status.IN_PROGRESS) {
         orderModel.add(linkTo(methodOn(OrderController.class).cancel(order.getId())).withRel("cancel")); // 附加cancel链接
         orderModel.add(linkTo(methodOn(OrderController.class).complete(order.getId())).withRel("complete")); // 附加complete链接
      }
      return orderModel;
   }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值