RequestMapping

RequestMapping在 AndServer 是扮演的角色非常重要,它可以规定一个 HTTP API 请求路径、请求方法、参数校验、请求头校验、Accept、ContentType 等重要规则。

RequestMapping的变体有GetMappingPostMappingPutMappingPatchMappingDeleteMappingGetMapping表示仅支持GET请求、PostMapping表示只支持POST请求,以此类推,下文将统称为RequestMapping

RequestMapping既可以用在类上,也可以用在方法上。当它仅仅用在类上时没有任何作用,当使用它的类中有方法也使用了它时,相当于把用在类上的RequestMapping的参数合并在方法的RequestMapping上。

使用RequestMapping注解的方法,其所在的类必须使用Controller注解或者RestController注解才能生效,否则是无意义的。


文本目录:

path 示例

@RestController
public class UserController {

    @GetMapping("/user/info")
    void info() {
        ...
    }

    @PostMapping("/user/register")
    void register() {
        ...
    }
}

假设服务器的 IP 地址是192.168.1.11,监听的端口是8080,上述示例的访问地址则是:http://192.168.1.11:8080/user/infohttp://192.168.1.11:8080/user/register

如上所示,一个Controller中一般会写多个属于同一个模块的 HTTP API,则每个 HTTP API 的路径都会以/user开头,这样会显得很麻烦,因此我们可以在类上添加RequestMapping来简化它:

@RequestMapping("/user")
@RestController
public class UserController {

    @GetMapping("/info")
    void info() {
        ...
    }

    @PostMapping("/register")
    void register() {
        ...
    }
}

以上两个示例是等价的。

另外同一个方法可以拥有多个path

@RestController
public class UserController {

    @PostMapping(path = {"/user/register", "/user/create"})
    void register() {
        ...
    }
}

或者:

@RequestMapping("/user")
@RestController
public class UserController {

    @PostMapping(path = {"/register", "/create"})
    void register() {
        ...
    }
}

如果客户端请求的地址在服务器上不存在,将会抛出NotFoundException异常,异常处理请参考ExceptionResolver

method 示例

除了使用PostMapping这样明确请求方法的注解之外,还可以使用RequestMapping指定请求方法,而且可以支持一个 HTTP API 支持多种请求方法:

@RestController
public class UserController {

    @RequestMapping(path = "/info"
        method = RequestMethod.GET)
    void info() {
        ...
    }

    @RequestMapping(path = "/register"
        method = {RequestMethod.GET, RequestMethod.POST})
    void register() {
        ...
    }
}

也可以由方法所在的类来指定它的请求方法是什么:

@RequestMapping(method = RequestMethod.GET)
@RestController
public class UserController {

    @RequestMapping(path = "/info")
    void info() {
        ...
    }

    @RequestMapping(path = "/register")
    void register() {
        ...
    }

    @DeleteMapping("/delete")
    void delete() {
        ...
    }
}

如上示例,前两个方法没有指定请求方法,但是由它们所在的类指定了它们的请求方法是GET,第三个方法同事支持GET请求方法和DELETE请求方法。

如果客户端请求的地址不支持客户端使用的请求方法,将会抛出MethodNotSupportException异常,异常处理请参考ExceptionResolver

param 示例

为了方便展开说明,我们先看一段示例:

@RestController
public class UserController {

    @GetMapping(path = "/info", param = "name=123")
    void info() {
        ...
    }
}

示例中的代码中param="name=123"param的其中一个语法,param有四种语法:

  1. key=value,规定了某 key 必须等于某 value,例如:param = "name=123"
  2. key!=value,规定了某 key 必须不等于某 value,例如:param = "name!=123"
  3. key,规定了参数中必须有某 key,且值不能为空,例如:param = "name"
  4. !key,规定了参数中必须不能由某 key,例如:param = "!name"

上述语法是可以混合使用的:

@RestController
public class UserController {

    @GetMapping(path = "/info", param = {"name!=123", "password"})
    void info() {
        ...
    }
}

上述示例中,param的含义是不能包含name=123这一参数键值对,但是必须包含password参数。

如果客户端的请求违反约束,则会抛出ParamValidateException异常,异常处理请参考ExceptionResolver

header 示例

header的使用方法和param完全一致,只是它用来规定请求头,而param用来规定请求参数。

如果客户端的请求违反约束,则会抛出HeaderValidateException异常,异常处理请参考ExceptionResolver

consume 示例

Consume 单词的字面意思是消耗、消费,在转换到程序中来就是说:你能消费什么?你能处理什么?因此它适合用于校验客户端的Content-Type头,Contnet-Type 的意思是内容类型,因此consume的含义就是能消费什么内容了。

consume的语法有两种:

  1. application/json,规定了客户端提交的Content-Type须是 JSON 格式。
  2. !applicatioln/xml,规定了客户端提交的Content-Type不能是 XML 格式。

同时它支持*,例如application/*支持客户端提交application/jsonapplication/zip等类型数据,例如!text/*不支持客户端提交text/plaintext/xml等类型数据。

示例:

@RestController
public class UserController {

    @PostMapping(path = "/info", consume = "application/json")
    void info() {
        ...
    }

    @PostMapping(path = "/create", consume = {"!text/*", "!application/xml"})
    void create() {
        ...
    }
}

上述示例中,第一个方法表示客户端请求时的Content-Type只能是application/json,比如客户端是application/json; charset=utf-8也是允许通过的;第二个示例表示客户端请求时Content-Type不能是text/plaintext/xmltext/css等,也不能是application/xml

如果客户端的请求违反约束,则会抛出ContentNotSupportedException异常,异常处理请参考ExceptionResolver

produce 示例

produce的语法和consume完全一致,只是它用来规定客户端的Accept头,而consume用来规定客户端的Content-Type头。

consume不同的是,它在服务端不支持*,但是它支持客户端的*。因为对于Content-Type来说,客户端上行或者服务端下行时内容类型都是明确的,因此服务端校验Content-Type时可以用非明确的值去做包含。而对于Accept,因为不知道服务端下发的Content-Type,所以客户端可以用非明确的值做包含,因此客户端的Accept值有可能是*/*

例如,规定客户端能接受 JSON:

@RestController
public class UserController {

    @PostMapping(path = "/info", produce = "application/json")
    String info() {
        ...
    }
}

上述示例中,如果客户端的Accept*/*或者application/json就可以校验通过。

例如,规定能接受 JSON 的客户端校验不通过:

@RestController
public class UserController {

    @PostMapping(path = "/info", produce = "!application/json")
    String info() {
        ...
    }
}

上述示例中,如果客户端的Accept*/*或者application/json是不能通过校验的。

如果客户端的请求违反约束,则会抛出ContentNotAcceptableException异常,异常处理请参考ExceptionResolver

特别注意produce的值会作为服务端响应消息的Content-Type发送到客户端。

如下所示,produce不会作为Content-Type被发送客户端:

@RestController
public class UserController {

    @PostMapping(path = "/info", produce = "!application/json")
    String info() {
        ...
    }
}

如下所示,produce会作为Content-Type被发送客户端:

@RestController
public class UserController {

    @PostMapping(path = "/info", produce = "application/json; charset=utf-8")
    String info() {
        ...
    }
}

相关阅读推荐:

results matching ""

    No results matching ""