WebFlux-Annotated Controllers

本文深入探讨了 Spring WebFlux 框架中基于注解的控制器使用方法,包括 RequestMapping 的详细配置,如何处理不同类型的请求,如 POST 和 GET 请求,参数的解析与绑定,以及响应的定制。同时,文章还介绍了如何使用 Matrix Variables,以及如何通过 @ModelAttribute 进行数据绑定和验证。此外,还讲解了如何处理异常,并通过 ControllerAdvice 实现全局异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于注解的Controller

Request Mapping

消费资源类型的请求

@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
    // ...
}
其中的"application/json"在MediaType中可以找到:APPLICATION_JSON_VALUE and APPLICATION_XML_VALUE

生产者类型的请求

@GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
    // ...
}
"application/json;charset=UTF-8"在MediaType可以找到: APPLICATION_JSON_UTF8_VALUE, APPLICATION_XML_VALUE.

请求URL中加参数或者头部加参数

匹配的路径: /pet/pet?myParam=myValue

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

匹配的路径: 必须在头部消息中包含 myHeader=myValue

@GetMapping(path = "/pets", headers = "myHeader=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

明确的注册

可以动态的注册处理URL请求的方法,这种可以用在很高端的地方,例如:不同的实例请求不同的URL,但是这些 URL处理方法都一样,这时候就可以这样子做:

@Configuration
public class MyConfig {

    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) 
            throws NoSuchMethodException {

        RequestMappingInfo info = RequestMappingInfo
                .paths("/user/{id}").methods(RequestMethod.GET).build(); 

        Method method = UserHandler.class.getMethod("getUser", Long.class); 

        mapping.registerMapping(info, handler, method); 
    }

}

Handler Methods

方法的参数

Controller方法参数描述
ServerWebExchange可以理解为应用上下文,可以得到request,response,session等
ServletHttpRequestrequest
ServletHttpResponseresponse
WebSession可以得到session
HttpMethod得到请求的方法
Locale可以得到请求的语言环境
@PathVariable得到url模板参数
@RequestParam得到url请求的参数
@RequestHeader得到请求头
@CookieValue得到Cookie
@RequestBody得到HTTP请求的请求体的信息
HttpEntity<B>得到请求头和请求体的集合
@RequestPart得到表单数据
java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap.用于访问HTML控制器中使用的模型
@ModelAttribute获取模型中的参数
Errors,BindingResult得到验证和数据绑定的错误信息
SessionStatus+ class-level@SessionAttributes不怎么明白
UriComponentBuilder构建请求URL
@SessionAttribute目前
@RequestAttribute访问请求参数
其他任何的参数如果是简单类型,默认会被认为是@RequestParam参数

返回值

返回值描述
@ResponseBody返回值通过HttpMessageWriter实例写入响应,其实就是返回
HttpEntity OR ResponseEntity这个可以指定请求头和请求体,very 强大
HttpHeaders返回只有请求头,没有请求体
String返回可以由ViewResolver解析的视图名称
View返回某个动态页面文件
java.util.Map,org.springframework.ui.Model添加各种参数到隐式对象中,相当于request.setAttribute
@ModelAttribute模型参数
void根据请求的url返回视图
Rendering不清楚
Flux<ServerSentEvent>, Observable<ServerSentEvent>, or other reactive type暂时没见过 ?
其他返回类型作为视图名处理

类型转换

对于一些基于注解的controller方法参数: @RequestParam,@PathVariable等,可能需要进行类型转换,他们默认的类型是String类型,对于简单的类型转换,如由String转换成int ,long等,Sping自动就转换了,对于其他的可以参考Spring FIeld Formatting

矩阵变量

Spring默认没有开启矩阵变量这种写法,需要进行配置

package com.yoke.lab.springbootlab.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

/**
 * @Author Yoke
 * @Date 2019/01/14 上午10:39
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUrlPathHelper(urlPathHelper());
    }

    @Bean
    public UrlPathHelper urlPathHelper() {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        return urlPathHelper;
    }
}
  1. 最简单的矩阵变量

类似于普通的查询参数,矩阵变量也需要一个类似于占位符的东西,去表示 它

// GET /pets/42;q=11;r=22

@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11
}
  1. 所有的路径段可能全都包含矩阵变量,有时候需要区分矩阵变量
// GET /owners/42;q=11/pets/21;q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable(name="q", pathVar="ownerId") int q1,
        @MatrixVariable(name="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22
}
  1. 可以给矩阵变量设置为option和一个默认值
// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

    // q == 1
}
  1. 得到所有的矩阵变量
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable MultiValueMap<String, String> matrixVars,
        @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {

    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}

@ReuqestParam

可以获取请求参数

@Controller
@RequestMapping("/pets")
public class EditPetForm {

    // ...

    @GetMapping
    public String setupForm(@RequestParam("petId") int petId, Model model) { 
        Pet pet = this.clinic.loadPet(petId);
        model.addAttribute("pet", pet);
        return "petForm";
    }

    // ...

}

Servlet API 中请求参数的概念包含了查询参数,表单数据,和multipart的数据,在WebFlux中这些数据都可以通过ServletWebExchange获取,@ReuqestParam默认只绑定了查询参数

@RequestParam还可以用来加在Map<String, String> or MultiValueMap<String, String>上,这样可以得到所有的查询参数

@RequestParam注解使用后,后边参数的类型就不会被其他参数解析器去解析,说白了就是它后边智能加常见的基本类型,不能加自定义的Java Bean

@Requestheader

获取请求头信息

例如:

Host                    localhost:8080
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language         fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding         gzip,deflate
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive              300

可以指定得到哪些请求头

下面这个例子: 得到Accept-Encoding和Keep-Alive的头信息

@GetMapping("/demo")
public void handle(
        @RequestHeader("Accept-Encoding") String encoding, 
        @RequestHeader("Keep-Alive") long keepAlive) { 
    //...
}

获取全部的头信息

可以把@RequestHeader加在Map<String, String>, MultiValueMap<String, String或者HttpHeaders参数上,这样可以得到

Spring内置支持将一个由,分割的字符串,转变成字符串数据或者集合,例如: @RequestHeader(“Accept” 返回的结果可能是String但是也可能是String[],List<String>

@CookieValue

获取Cookie信息

@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) { 
    //...
}

@ModelAttribute

可以将请求的参数,进行数据绑定成一个Java Bean

模型参数包含请求的查询参数和与字段名相同的表单字段信息,然后进行封装,这就是数据绑定WebExchangeDataBinder会进行查询参数,表单字段与声明的字段信息进行匹配,封装成想要的Java Bean

package com.yoke.lab.springbootlab.web;

import com.yoke.lab.springbootlab.pojo.User;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Yoke
 * @Date 2019/01/14 上午9:36
 */
@RestController
@RequestMapping(path = "/")
public class IndexController {


    @PostMapping("/index/{id}/{name}/{pwd}")
    public String findPet(@ModelAttribute User user) {
        System.out.println(user);
        return "aaa";
    }

}

数据绑定可能出错,可以通过使用BindingResult来对可能出现的异常进行处理

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { 
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}

还可以在数据绑定之后加入验证javax.validation.Valid,Spring的@Validated注解

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { 
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}

在SpringWebFlux中,支持响应式的模型,不能再@ModelAttribute之前申明任何一个响应式的模型,响应式可以这样子处理错误

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
    return petMono
        .flatMap(pet -> {
            // ...
        })
        .onErrorResume(ex -> {
            // ...
        });
}

@SessionAttributes

@SessionAttributes用来将请求之间的模型数据存储到WebSession中,它是一个类层级的注解

@Controller
@SessionAttributes("pet") 
public class EditPetForm {
    // ...
}

当一个请求,带有pet的模型参数时,它的数据就会被自动存储到WebSession中,直到一个Controlelr方法使用SessionStatus为参数进行清除存储

@Controller
@SessionAttributes("pet") 
public class EditPetForm {

    // ...

    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status) { 
        if (errors.hasErrors) {
            // ...
        }
            status.setComplete();
            // ...
        }
    }
}

@SessionAttribute

可以访问全局的已经存在的session

@GetMapping("/")
public String handle(@SessionAttribute User user) { 
    // ...
}

如果想要添加或者删除全局的session,可以在controller中注入WbbSession

@RequestAttribute

@SessionAttribute类似,它可以获取已经存在的请求参数

Multipart Content

我没用过?

@RequestBody

读请求体的内容,通过HttpMessageReader反序列化为对象

@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
    // ...
}

与Spring MVC不同的是,注解参数支持响应式类型

@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
    // ...
}

这个可以结合javax.validation.Valid,Spring的@Validated使用,默认情况下,验证错误将会引起WebExchangeBindException会出现400(BAD_REQUEST),我们可以使用Errorsor BindingResult参数来处理错误

@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, BindingResult result) {
    // ...
}

HttpEntity

或多或少等同于@RequestBody,它包含了request暴露出来的请求头和请求体

@ResponseBody

将返回的信息序列化使用HttpMessageWriter到请求体中

@ResponseBody支持方法级别,也支持类级别

ResponseEntity

包含了请求的头,体,状态码,三种信息

Jackson JSON

Spring WebFlux提供了Jackson JSON(支持序列化)

管理异常

对于一个REST 服务来说,通常需要将错误信息展示在请求体中,Spring没有自动实现,因为响应正文中的错误是针对特定的应用的。我们可以在@RestController类中,写一个@ExceptionHanlder注解的方法,用ResponseENtity作为返回值,去设置返回的各种信息。也可以将这种方法写在@ControllerAdvice注解的类中,用于全局

Controller Advice

对于一些全局的操作可以放在这里,常用的就是异常处理了

默认,每次请求,都会经过这里,但是可以通过注解的一些参数来限制

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

Example

package com.yoke.lab.springbootlab.config;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * @Author Yoke
 * @Date 2019/01/15 上午10:43
 */
@ControllerAdvice
public class Advice {

    @ExceptionHandler
    public ResponseEntity<String> response(Exception e) {
        System.out.println(e.getMessage());
        System.out.println("handle");
        return new ResponseEntity<>("error", new HttpHeaders(), HttpStatus.BAD_REQUEST);
    }
}
package com.yoke.lab.springbootlab.web;

import com.yoke.lab.springbootlab.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Yoke
 * @Date 2019/01/14 上午9:36
 */
@RestController
@RequestMapping(path = "/")
public class IndexController {


    @ModelAttribute
    @GetMapping(path = "/test")
    public User getUser() {
        int i = 10 / 0;
        return new User(1, "sd", "ds");
    }
}

result

/ by zero
handle
/ by zero
handle
/ by zero
handle

浏览器的结果自然就是设定的ResponseEntity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值