SpringBoot-URL路由:@Controller和@RequestMapping

SpringBoot定义URL处理方法:@Controller和@RequestMapping

@Controller标注的类表示的是一个处理HTTP请求的控制器(即MVC中的C),该类中所有被@RequestMapping标注的方法都会用来处理对应URL的请求。

在SpringMVC框架中,使用@RequsetMapping标注可以将URL与处理方法绑定起来,例如:

@RestController
public class HelloworldRestController {
    @RequestMapping("/")
    public String helloworld(){
        return "hello world";
    }
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "fpc";
    }
}

HelloworldRestController类被@Controller标注,其中的两个方法都被@RequestMapping标注,当应用程序运行后,在浏览器中访问:localhost:8089,请求会被SpringMVC框架分发到hellworld()方法进行处理。同理输入localhost:8089/hello会交给hello()方法处理。

@ResponseBody标注表示处理函数直接将函数的返回值传到浏览器端显示。

运行结果:

输入localhost:8089:

输入loalhost:8089/hello:

@RequestMapping标注类

@RequestMapping标注同样可以加在类上:

@RestController
@RequestMapping("/index")
public class HelloworldRestController {
    @RequestMapping("/")
    public String helloworld(){
        return "hello world";
    }
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "fpc";
    }
}

hello()方法绑定的URL路径是/index/hello

运行结果:
如果直接访问:localhost:8089/hello:

如果访问:localhost:8089/index/hello:

提示:每一个类都可以包含一个或者多个@RequestMapping标注的方法,通常我们会将业务逻辑相近的URL放在同一个Controller中处理。

@RequestMapping的简写形式

在web应用中常用的HTTP方法有四种:

  1. PUT方法用来添加资源
  2. GET方法用来获取已有的资源
  3. POST方法用来对资源进行状态转换
  4. DELETE方法用来删除已有的资源

这四个方法可以对应到CRUD操作(Create,Read,Update和Delete)比如博客的创建操作,按照REST风格设计URL就应该使用PUT方法,读取博客使用GET方法,更新博客使用POST方法,删除博客使用DELETE方法。

每一个Web请求都是属于其中一种,在SpringMVC中如果不特殊指定的话,默认是GET请求。

比如@RequestMapping("/")和@RequestMapping("/hello")和对应的Web请求是:

  1. GET /
  2. GET /hello

实际上@RequestMapping("/")是@RequestMapping("/",method = RequestMethod.GET)的简写,即可以通过method属性,设置请求的HTTP方法。

比如PUT /hello请求,对应@RequestMapping("/hello",method = RequestMethod.PUT)

Spring MVC最新的版本中提供了一种更加简洁的配置HTTP方式,增加了四个标注:

  1. PutMapping
  2. GetMapping
  3. PostMapping
  4. DeleteMapping

基于新的标注@RequestMapping("/hello",method = RequestMethod.PUT) 可以简写成@PutMapping("/hello"),@RequestMapping("/hello")和@GetMapping("/hello")等价。

返回HTML

在之前所有的Controller方法中,返回值字符串被直接传送到浏览器端并显示给用户。但是为了能够呈现更加丰富,美观的页面,我们需要将HTML代码返回给浏览器,浏览器在进行页面的渲染显示,一种很直观的方法是在处理请求的方法中,直接返回HTML代码:

@RequestMapping("/blog")
    public String blog(){
        return "<html><head><title>Title</title></head><body><h2>This is a blog</h2><p>This is content of the blog.</p></body></html>";
    }

运行结果:浏览器地址栏输入:localhost:8089/blog:

显然,这样做的问题在于一个复杂的页面的HTML代码往往也非常复杂,并且内嵌在Java代码中十分不利于维护。

更好的做法是将页面的HTML代码卸载模板文件中,然后读取该文件并返回。Spring天然支持这种非常常见的场景,需要现在pom.xml引入Thymelea依赖:

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

将HTML文本保存在:src/main/webapp/pages/blog.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
    <h1>This is title</h1>
    <p>This is Content.</p>
</body>
</html>

Controller中可以去掉@ResponseBody标注(表示不是直接返回字符串,而是返回渲染的HTML模板,并将URL处理函数设置为刚刚保存在pages/文件夹中的文件名(不需要扩展名:))

运行结果并没有出现返回blog.html页面,而是返回了"blog"字符串在浏览器中显示出来:

造成这种现象的原因是:如果类是用@RestController修饰的出现这种情况很正常,如果想要返回页面可以将@RestController改为@Controller

package springboot;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@Controller
//@RequestMapping("/index")
public class HelloworldRestController {
    @RequestMapping("/")
    public String helloworld(){
        return "hello world";
    }
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        return "fpc";
    }
    
    @RequestMapping("/blog")
    public String blog(){
        return "blog";
    }
}

继续运行项目:又出现问题了,报错信息为:

org.xml.sax.SAXParseException: 元素类型 "meta" 必须由匹配的结束标记 "</meta>" 终止。

报错的原因是blog.html代码中,meta元素没有封闭:

你可以直接封闭meta元素:

<meta charset="UTF-8"/>

也可修改SpringBoot的配置文件application.properties:

spring.thymeleaf.mode=LEGACYHTML5

SpringBoot的配置文件通常在/resource根目录下,以application.properties命名,没有这个文件则创建一个。

另外为了保证Thymeleaf能够正确识别HTML5,还需要添加Maven依赖到pom.xml:

<dependency>
  <groupId>net.sourceforge.nekohtml</groupId>
  <artifactId>nekohtml</artifactId>
  <version>1.9.22</version>
</dependency>

再次运行程序:

注意:在编写HTML代码时,请务必保证每一个标签都是闭合的,容易忽略的标签包括<meta/>,<link/>,<br/><hr/>,<img/>,<input/>等等

但是在HTML5中,有些标签并不要求闭合,Thymeleaf遇到这样的HTML文件会报错。为了支持HTML5,你可以在SpringBoot配置文件中增加一行配置:如上面的过程。

静态资源处理

在编写HTML代码的过程中,我们会遇到几类外部静态资源:

  • CSS文件:<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
  • JavaScript文件:<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  • 图像:<img src="http://assets0.tianmaying.com/img/appicon/ios.png"/>

这些外部资源都是通过HTTP协议访问得到--也就是说,当我们用浏览器打开我们编写的HTML页面(无论是通过本地文件直接打开,还是访问SpringBoot服务器),在获取页面内容本身之外,还需要向外部服务器(例如maxcdn.bootstrapcdn.com)发起HTTP请求以获取我们需要的CSS/JavaScript资源。

但是在我们开发过程中,如果某个时刻不能访问Internet,那我们的页面也就无法正确地展现出它应有的样式。另一方面,除了使用第三方库,我们自己还会编写大量的CSS/JavaScript文件,这就要求我们必须有一种很快的方式能够在修改后立马在本地看到结果。

本地资源文件组织

首先我们抛开本地HTTP服务器,简单来看在本地编写一个HTML文件以及使用CSS资源,那么我们可以这样组织项目结构:

.
├── index.html
├── css
    └── style.css
└── js
    └── main.js

在index.html文件中你可以这样引用它们:

<link rel="stylesheet" href="css/style.css"/>
<script src="js/main.js"></script>

css/style.css和js/main.js都是使用相对路径描述。

服务器中的静态资源文件

如果需要将index.html放在服务器中呢?index.html位于templates目录下,通过http://localhost:8089/可以访问首页内容,但是CSS和JavaScript外部资源呢?因为我们的HTTP服务器根本没有处理它们,所以不可能通过类似http://localhost:8089/css/style.css这样的方式来访问他们使得我们的页面正确显示。

默认情况下,SpringBoot会将类路径上的/static/目录的内容Serve起来,意思就是对静态资源的请求,都会返回/static/目录中对应路径的文件内容,于是我们可以这样组织文件目录结构来处理静态资源(以下是src/main/resources 目录结构,这个目录经过编译后被添加到类路径上)

├── static
    ├── css
        └── style.css
    └── js
        └── main.js
└── templates
    └── index.html

这样,当我们经过以上布局,重启应用后,就可以通过访问http://localhost:8089/css/style.css和http://localhost:8089/js/main.js来获取CSS和JavaScript资源了。

在HTML中引入资源

最后我们将静态资源引入到HTML页面中,我们往往需要一种介于相对路径(css/style.css)和绝对路径(http://localhost:8089/css/style.css)之间的资源访问方式--context路径:

<link rel="stylesheet" href="/css/style.css"/>
<script src="/js/main.js"></script>

这里只是简单的在相对路径URL的最前面加上了/,但是意义和相对路径就完全不同了,此时服务器会将其视为访问当前host中的“绝对路径”,也就是自动在这个路径前面加上协议,主机名,端口(都是当前服务器的相同信息),那么无论我们访问的是当前网站下的任何路径,它都会给出统一的结果,从而正确引用到外部资源。

posted @ 2017-11-12 18:24  起床oO  阅读(14161)  评论(0编辑  收藏  举报