RESTful请求
REST全称是Representational State Transfer,如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。可以唯一标识和定位资源,对于该URL标识的资源做何种操作是由Http方法决定的。请求方法有4种,get、post、put、delete分别对应获取资源,添加资源,更新资源及删除资源。
每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
例如有一个博客系统,包含了博客、评论等信息,它的路径如下所示:
https://api.myblog.com/blogs 有关博客的url
https://api.myblog.com/blogs/ID/comments 有关评论的url
根据不同的HTTP动作对博客进行不同的操作:
GET /blogs/ID:获取指定某篇博客的内容
PUT /blogs/ID:更新某篇指定博客
DELETE /blogs/ID:删除某篇博客
GET /blogs/ID/comments:获取某篇博客的评论
DELETE /blogs/ID/comments/ID:删除某篇博客的某个评论
可以在地址后面通过?附加参数来对获取的内容进行过滤
GET /blogs/ID/comments?limit=10:指定返回10条评论
GET /blogs/ID/comments?page=2&per_page=10:指定返回第2页的评论,每页10条记录
在Spring MVC中,可以用一个Controller类来对应一个资源集合,然后在其中定义对不同类型请求的响应方法。
例如首先定义BlogController类,通过注解@RequestMapping("/blogs")
代表响应/blogs相关的资源请求
之后分别用注解@GetMapping
、@PostMapping
、@DeleteMapping
来定义对不同类型请求的响应方法
@Controller
@RequestMapping("/blogs")
public class BlogController {
//阅读博客
@GetMapping("{id}")
public String getBlogById(@PathVariable("id") Long id, Model model) {
...
}
//保存博客
@PostMapping("{id}")
public ResponseEntity<Response> saveBlog(@PathVariable("id") Long blogId, @RequestBody Blog blog) {
...
}
//删除博客
@DeleteMapping("{id}")
public ResponseEntity<Response> deleteBlog(@PathVariable("id") Long blogId) {
...
}
}
在前端进行发起请求时,浏览器默认使用GET请求页面,使用POST提交表单,除此之外,还可以在发起Ajax请求时指定请求方法
// 删除博客
$(".blog-content-container").on("click", ".blog-delete-blog", function () {
$.ajax({
url: blogUrl,
type: 'DELETE', //指定请求的方法
success: function (data) {
}
});
});
返回响应
ModelAndView
对于GET请求,我们可以使用ModelAndView的方式返回数据,即用model来保存数据,用view表示返回的页面。
其中Model对象可以从方法参数中传入,然后返回页面字符串
也可以自定义ModelAndView 对象,然后向其中添加数据和指定页面
@GetMapping("/blogs/{id}")
public String getBlogById(@PathVariable("id") Long id, Model model) {
model.addAttribute("blogModel",blogService.getBlogById(id)); //保存数据
return "/userspace/blog"; //返回页面视图
}
@GetMapping("/blogs/edit")
public ModelAndView createBlog() {
ModelAndView mav = new ModelAndView();
mav.addObject("blog", new Blog(null, null, null)); //添加数据
mav.setViewName("/userspace/blogedit"); //指定页面
return mav;
}
ResponseEntity
ResponseEntity标识整个http相应:状态码、头部信息以及相应体内容。因此我们可以使用其对http响应实现完整配置。
如下所示返回一个简单的ResponseEntity响应,其包括String类型的内容"Hello World!",自定义的头部headers,和状态码HttpStatus.OK
@GetMapping("/hello")
ResponseEntity<String> hello() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "foo");
return new ResponseEntity<>("Hello World!", headers, HttpStatus.OK);
}
ResponseEntity提供了两个内嵌的构建器接口: HeadersBuilder 和其子接口 BodyBuilder。因此我们能通过ResponseEntity的静态方法直接访问。如下所示为一些常见的状态
BodyBuilder accepted();
BodyBuilder badRequest();
BodyBuilder created(java.net.URI location);
HeadersBuilder<?> noContent();
HeadersBuilder<?> notFound();
BodyBuilder ok();
我们可以直接将内容放到状态码对应的构造器内返回
@GetMapping("/hello")
ResponseEntity<String> hello() {
return ResponseEntity.ok("Hello World!");
}
也可以使用方法链的形式设置状态码并返回内容
@GetMapping("/customHeader")
ResponseEntity<String> customHeader() {
return ResponseEntity.ok()
.header("Custom-Header", "foo")
.body("Custom header set");
}
以上返回的ResponseEntity内容都是String类型ResponseEntity<String>
,还可以自定义返回内容的类型,例如定义返回类Response
用于统一返回数据的格式 ,其中success用于标识操作是否成功,message表示返回信息,body存放返回数据
public class Response {
private boolean success; //操作是否成功
private String message; //操作返回的信息
private Object body; //处理的返回内容
public Response(boolean success, String message, Object body) {
this.success = success;
this.message = message;
this.body = body;
}
...//getter & setter
}
public ResponseEntity<Response> deleteBlog(@PathVariable("username") String username,@PathVariable("id") Long id) {
blogService.removeBlog(id);
String redirectUrl = "/blog/u/" + username + "/blogs";
Response res = new Response(true, "删除成功", redirectUrl); //返回自定义的Response类
return ResponseEntity.ok().body(res);
}
}
请求Response返回给前端后,在ajax中通过回调函数的data获取返回数据,此时可以通过data.success判断操作是否成功,data.message获取操作信息,data.body获取返回的数据
$(".blog-content-container").on("click", ".blog-delete-blog", function () {
$.ajax({
url: blogUrl,
type: 'DELETE',
success: function (data) {
if (data.success) { //判断操作是否成功
window.location = data.body; //处理返回的数据
} else {
toastr.error(data.message); //弹出返回的信息
}
}
});
});