Web 开发 —— 进阶

说明

在基础篇当中,我们讲究了 Solon 对 MVC 的支持和提供的基础的Web 能力。在这个章节我们会讲解 Solon 提供了哪些能力可以供我们进行控制。

首先我们来看看 Solon Web 的请求过程,理解 Solon 的基础概念,这样方便我们后续的定制。

请求过程

在这里插入图片描述

从图中,我们可以看到在Web 的请求过程中,会经过几个环节:Filter(全局过滤器)->

RouterInterCeptor(路由拦截器)-> Handler(处理器)-> Interceptor(拦截器)。

Context

Solon 采用 Handler + Context 架构,Context 是请求的上下文。 在 Context 中可以获取请求和响应相关的数据,包括请求相关的对象与接口,会话相关的对象与接口,响应相关的对象与接口等。Solon 提供了多种方式获取Context,也为Context 其他了一些常用的接口,更详细的内容可以查看 https://solon.noear.org/article/216 。

Filter

过滤器,可以对所有请求(无论动态还是静态的请求)进行全局的控制。通常用于全局的 Web 请求异常处理,全局的性能记录,全局的响应状态调整,全局的上下文日志记录,全局的链路跟踪等等,当然也可以作用在局部上。

RouterInterceptor

路由拦截器,可以对动态请求进行全局的控制,功能基本与 Filter 一致。主要的区别是:1. 只能对动态请求(路由器范围)进行拦截;2. 可以修改参数和返回结果。

Interceptor

拦截器,拦截器使用切面(AOP)的方式实现,通过定义注解、设置切点、注册拦截器,Solon 将对方法进行拦截,从而增加拦截器方法的执行。拦截器可以用于实现缓存、事务等等功能。

Action

实际的请求方法,注册到路由器的方法(通常是@Mapping 函数,可以通过 Controller 的方式定义,也可以通过 Handler 的方式定义,等等)。Action 本质上是 Handler 和 Class Method 的结合,可以请求方法进行局部的控制。

示例 demo-web02

在 IDEA 中通过 File->New->Modules... 可以创建新的模块 demo-web02,基础代码和配置可以从demo-web01中拷贝过来。

这里我们继续补充demo-web01 ,增加定制化的处理。

  • 增加简单的鉴权用于演示的Filter。
  • 增加RouterInterceptor,用于演示Filter和RouterInterceptor的区别。
  • 增加日志审计的功能用于演示 Interceptor 拦截器。

全局过滤器

使用全局的Filter做简单的 token 验证。这里需要指定Component的注解,也就是统一有Solon 来管理。如果有多个过滤器是可以指定Component的 index 参数,参数越小,越外层,可以理解为优先级越高。

package com.example.demo.web.common.filter;

import com.jfinal.kit.Kv;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.StrUtil;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;

/**
 * @author airhead
 */
@Slf4j
@Component
public class GlobalFilter implements Filter {
  @Override
  public void doFilter(Context ctx, FilterChain chain) throws Throwable {
    String path = ctx.path();
    log.info("1. GlobalFilter, path: {}", path);
    // 简单逻辑,非静态的路径都需要token
    if (!path.contains(".")) {
      String token = ctx.param("token");
      if (StrUtil.isBlank(token)) {
        ctx.outputAsJson(ONode.stringify(Kv.of("state", false)));
        return;
      }
    }

    chain.doFilter(ctx);
  }
}

路由拦截器

这边只是为了对比和Filter的不同,如果是静态资源请求时,不会进入路由拦截器,也就不会打印对应的日志。

package com.example.demo.web.common.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Handler;
import org.noear.solon.core.route.RouterInterceptor;
import org.noear.solon.core.route.RouterInterceptorChain;

/**
 * @author airhead
 */
@Slf4j
@Component
public class DemoRouteInterceptor implements RouterInterceptor {
  @Override
  public void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain)
      throws Throwable {
    String path = ctx.path();
    log.info("2. DemoRouteInterceptor, path: {}", path);

    chain.doIntercept(ctx, mainHandler);
  }
}

局部过滤器

局部过滤器可以通过 Addition 注解直接使用,可以通过注解继承的方式使用。

WhitelistFilter
package com.example.demo.web.common.filter;

import lombok.extern.slf4j.Slf4j;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Filter;
import org.noear.solon.core.handle.FilterChain;

/**
 * @author airhead
 */
@Slf4j
public class WhitelistFilter implements Filter {
  @Override
  public void doFilter(Context ctx, FilterChain chain) throws Throwable {
    String path = ctx.path();
    log.info("3. WhitelistFilter, path: {}", path);
    chain.doFilter(ctx);
  }
}
Whitelist
package com.example.demo.web.common.annotation;

import com.example.demo.web.common.filter.WhitelistFilter;
import java.lang.annotation.*;
import org.noear.solon.annotation.Addition;

/**
 * @author airhead
 */
@Addition(WhitelistFilter.class)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Whitelist {}

拦截器

拦截器可以通过 Around 注解直接使用,可以通过注解继承的方式使用。

LogInterceptor
package com.example.demo.web.common.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.noear.solon.core.aspect.Interceptor;
import org.noear.solon.core.aspect.Invocation;
import org.noear.solon.core.handle.Context;

/**
 * @author airhead
 */
@Slf4j
public class LogInterceptor implements Interceptor {
  @Override
  public Object doIntercept(Invocation inv) throws Throwable {
    Context context = Context.current();
    String path = context.path();
    log.info("4. LogInterceptor, path: {}", path);

    return inv.invoke();
  }
}
OperateLog
package com.example.demo.web.common.annotation;

import com.example.demo.web.common.interceptor.LogInterceptor;
import java.lang.annotation.*;
import org.noear.solon.annotation.Around;

/**
 * @author airhead
 */
@Around(LogInterceptor.class)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {}

Controller的调整

package com.example.demo.web.manage.controller;

import com.example.demo.web.common.annotation.OperateLog;
import com.example.demo.web.common.annotation.Whitelist;
import com.example.demo.web.common.filter.WhitelistFilter;
import com.example.demo.web.common.interceptor.LogInterceptor;
import com.example.demo.web.manage.dto.DeptDto;
import com.example.demo.web.manage.service.DeptService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import org.noear.solon.annotation.*;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.handle.UploadedFile;

@Controller
@Mapping("/dept")
@Api("部门管理")
public class DeptController {
  @Inject private DeptService service;

  @Mapping("/index")
  @Get
  @ApiOperation("浏览部门")
  @OperateLog
  @Whitelist
  public ModelAndView index() {
    ModelAndView mv = new ModelAndView();
    mv.put("depts", service.list());
    mv.view("dept.shtm");

    return mv;
  }

  @Mapping(value = "list")
  @Get
  @ApiOperation("获取列表")
  @Around(LogInterceptor.class)
  @Addition(WhitelistFilter.class)
  public List<DeptDto> list() {
    return service.list();
  }
}

验证

  • 请求 /dept/index,不带token得情况,演示全局的过滤器效果

在这里插入图片描述

  • 请求 /dept/list?tokent=1,带token得情况,验证请求过程,使用的是 Addition 和 Around 的方式添加局部过滤器和拦截器。

在这里插入图片描述
在这里插入图片描述

  • 请求 /dept/index?tokent=1,带token得情况,验证请求过程,使用注解继承的方式添加局部过滤器和拦截器。

在这里插入图片描述
在这里插入图片描述

  • 请求 /img/wechat.png,验证静态请求不会通过RouterInterceptor。如果静态资源不存在的情况,依然会继续查找动态路由,此时会进入RouterInterceptor。

在这里插入图片描述

小结

本章节通过示意图的方式简单描述Solon web的请求过程,并对其中的重要概念做了讲解,通过实例的方式正式了对过滤器,路由拦截器和拦截器的定制。

官网对这些内容做了多篇文章的介绍,可以通过官网进一步的了解这些概念和使用方法。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值