跳转到内容

Controller 开发实践

Feat Cloud 在设计 Controller 注解时,借鉴了 Spring 的设计理念,降低学习成本的同时进行了极致简化,专注于高性能和易用性。

Feat Cloud 提供以下核心注解:

@Controller 用于标记一个类为控制器,作为 Spring 风格的注解设计,降低学习成本。

通常与 @RequestMapping 一起使用来定义请求映射路径。

定义:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Controller {
String value() default "";
}

value 参数说明:

  • value() 定义了 Controller 的基础路径,其他方法级别的请求路径会基于这个基础路径。
  • 默认情况下,value 是一个空字符串,意味着没有基础路径前缀。
  • 示例:如果设置 @Controller("/user"),那么该 Controller 下的所有方法将通过 /user 路径前缀访问。

@RequestMapping 用于标记方法,表示该方法处理特定的 HTTP 请求路径。

支持指定HTTP请求方法,有效类型:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE 定义:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface RequestMapping {
String value() default "";
RequestMethod[] method() default {};
}

参数说明:

  • value: 指定与该方法关联的 URL 路径,默认为空字符串,意味着使用 Controller 级别的路径前缀(如果有)。
  • method: 指定该方法支持的 HTTP 请求类型,默认不限制请求方法,即所有类型都可以访问该方法。

@Param 用于绑定请求中的查询参数(Query Parameters)到方法参数上。当方法参数为自定义POJO类型时,可无需使用@Param注解,框架会自动进行参数绑定。

定义:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Param {
/**
* 指定要绑定的查询参数的名称。
*
* @return 参数名称
*/
String value();
}

value 参数说明:

  • 如果 HTTP 请求中没有与 value 匹配的查询参数,则方法参数将被赋予默认值(如 null、0 或 false,取决于类型)。
  • 示例:若方法定义为 @Param("id") int userId,则会尝试从请求中获取名为 id 的参数并转换为整数赋值给 userId

@PathParam 用于绑定请求中的路径参数(Path Parameters)到方法参数上。

定义:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface PathParam {
/**
* 参数名称
*
* @return 参数名称
*/
String value();
}

value 参数说明: 指定要绑定的路径参数的名称,该名称必须与路由中定义的占位符名称一致。

  • 示例:若 URL 定义为 /user/{id},则应使用 @PathParam("id") 来绑定路径中的 id 值到方法参数。
  • 如果 HTTP 请求路径中没有匹配的参数名,则框架会抛出异常或自动赋予默认值(如 null、0 等),具体行为取决于框架实现和参数类型。

@InterceptorMapping 用于为特定的请求路径添加拦截器逻辑,在请求到达目标方法之前执行一些操作。

定义:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface InterceptorMapping {
String[] value() default "";
}

value 参数说明: 指定拦截器应用的 URL 路径模式数组。

  • 支持精确匹配(如 /path)和通配符匹配(如 /path/*)。
  • 如果未指定,默认为空数组,表示拦截器不匹配任何请求。
  • 示例:若设置 @InterceptorMapping({"/user", "/api/*"}),则该拦截器会作用于所有 /user/api 下的请求路径。

基于项目中的实际示例,创建一个简单的 Controller:

@Controller("demo2")
public class Demo2Controller {
// 基础路径映射
@RequestMapping("")
public String test1() {
return "hello world";
}
// 支持查询参数
@RequestMapping("/param1")
public String test2(@Param("param") String param) {
return "hello " + param;
}
// 多个查询参数
@RequestMapping("/param2")
public String test3(@Param("param1") String param1, @Param("param2") String param2) {
return "hello " + param1 + " " + param2;
}
// 支持对象参数绑定
@RequestMapping("/param3")
public String test4(TestParam param) {
return "hello " + param.getParam1() + " " + param.getParam2();
}
// 命名参数对象
@RequestMapping("/param4")
public String test5(@Param("param") TestParam param) {
return "hello param is " + param;
}
}

对应的参数对象:

public class TestParam {
private String param1;
private String param2;
// getters and setters
public String getParam1() { return param1; }
public void setParam1(String param1) { this.param1 = param1; }
public String getParam2() { return param2; }
public void setParam2(String param2) { this.param2 = param2; }
}

Feat Cloud 支持 Bean 生命周期注解,可以在适当的时机执行初始化和清理逻辑:

@Controller
class Demo1Controller {
@PostConstruct
public void init() {
System.out.println("Controller 初始化完成");
}
@RequestMapping
public String test1() {
return "hello";
}
@PreDestroy
public void destroy() {
System.out.println("Controller 正在销毁");
}
}

通过 @InterceptorMapping 注解可以为特定路径添加拦截器:

@Controller("api")
public class ApiController {
@RequestMapping("/users")
public String getUsers() {
return "用户列表";
}
// 为API路径添加拦截器
@InterceptorMapping({"/api/users/*", "/api/admin/*"})
public Interceptor authInterceptor() {
return (context, completableFuture, chain) -> {
// 前置处理:权限验证
String token = context.Request.getHeader("Authorization");
if (token == null || !isValidToken(token)) {
context.Response.setStatus(401);
context.Response.write("Unauthorized".getBytes());
completableFuture.complete(null);
return;
}
// 继续执行请求链
chain.proceed(context, completableFuture);
// 后置处理:记录访问日志
System.out.println("API 访问完成: " + context.Request.getRequestURI());
};
}
private boolean isValidToken(String token) {
// 实际的 token 验证逻辑
return "valid-token".equals(token);
}
}

拦截器使用注意事项:

  • 路径匹配:支持精确匹配和通配符匹配(*
  • 执行顺序:多个拦截器按定义顺序执行
  • 异步处理:正确处理 CompletableFuture 以支持异步请求
  • 异常处理:拦截器内需要处理可能的异常情况