All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.shacocloud.mirage.restful.example.ExampleServer Maven / Gradle / Ivy

package cc.shacocloud.mirage.restful.example;

import cc.shacocloud.mirage.restful.*;
import cc.shacocloud.mirage.restful.bind.annotation.*;
import cc.shacocloud.mirage.restful.bind.validation.BindingResult;
import cc.shacocloud.mirage.restful.bind.validation.Validated;
import cc.shacocloud.mirage.restful.bind.validation.errors.BindingResultError;
import cc.shacocloud.mirage.restful.exception.BindingException;
import cc.shacocloud.mirage.restful.exception.MethodArgumentNotValidException;
import cc.shacocloud.mirage.restful.http.MultipartFile;
import cc.shacocloud.mirage.restful.http.MultipartFileUpload;
import cc.shacocloud.mirage.utils.reflection.DefaultParameterNameDiscoverer;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerOptions;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;

public class ExampleServer {
    
    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        startServer(vertx, new ExampleController());
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   Object... handlers) {
        startServer(vertx, 8080, handlers);
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   int port,
                                   Object... handlers) {
        startServer(vertx, port, vertx1 -> new MirageRequestMappingHandler(vertx1, new DefaultParameterNameDiscoverer()), handlers);
    }
    
    /**
     * 启动样例服务
     */
    public static void startServer(Vertx vertx,
                                   int port,
                                   Function mappingHandlerFunc,
                                   Object @NotNull ... handlers) {
        // 构建 RequestMapping
        MirageRequestMappingHandler requestMappingHandler = buildRouterMappingHandler(vertx, mappingHandlerFunc);
        
        for (Object handler : handlers) {
            requestMappingHandler.detectHandlerMethods(handler);
        }
        
        CountDownLatch downLatch = new CountDownLatch(1);
        
        HttpServerOptions serverProperties = new HttpServerOptions();
        serverProperties.setLogActivity(false);
        serverProperties.setPort(port);
        
        // 启动服务
        VertxRouterDispatcherOptions routerDispatcherOptions = new VertxRouterDispatcherOptions();
        routerDispatcherOptions.setServerOptions(serverProperties);
        routerDispatcherOptions.setRouterMappingHandlers(
                Arrays.asList(requestMappingHandler)
        );
        VertxRouterDispatcherHandler.createHttpServer(vertx, routerDispatcherOptions)
                .onComplete(ar -> {
                    downLatch.countDown();
                    
                    if (ar.failed()) throw new RuntimeException(ar.cause());
                });
        
        // 阻塞等待初始化完成
        try {
            downLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 构建路由映射处理器
     */
    public static @NotNull MirageRequestMappingHandler buildRouterMappingHandler(Vertx vertx,
                                                                                 @NotNull Function mappingHandlerFunc) {
        
        // 绑定请求处理映射器
        MirageRequestMappingHandler mirageRequestMappingHandler = mappingHandlerFunc.apply(vertx);
        
        // 异常处理器
        ExceptionHandlerExceptionResolver exceptionResolver = mirageRequestMappingHandler.getExceptionHandlerExceptionResolver();
        exceptionResolver.registerExceptionHandler(new DefaultExceptionHandler());
        
        // 初始化
        mirageRequestMappingHandler.init();
        return mirageRequestMappingHandler;
    }
    
    // ------------------ 业务代码
    
    /**
     * 默认异常处理器
     */
    @ResponseBody
    public static class DefaultExceptionHandler {
        
        @ExceptionHandler(Exception.class)
        public Future> defaultEx(@NotNull Exception e, @NotNull HttpResponse response) {
            Map result = new HashMap<>();
            result.put("code", "1010");
            result.put("message", "服务器错误!");
            result.put("ex", e.getMessage());
            return Future.succeededFuture(result);
        }
        
        /**
         * 参数校验违规异常处理方法
         * 

* 方法参数处理示例,便于查看 */ @ExceptionHandler({MethodArgumentNotValidException.class, BindingException.class}) public Future constraintViolation(Exception e, HttpResponse response) { BindingResult bindingResult; if (e instanceof BindingException) { bindingResult = ((BindingException) e).getBindingResult(); } else if (e instanceof MethodArgumentNotValidException) { bindingResult = ((MethodArgumentNotValidException) e).getBindingResult(); } else { Map result = new HashMap<>(); result.put("code", "1010"); result.put("message", "服务器错误!"); result.put("ex", e.getMessage()); return Future.succeededFuture(result); } // 循环错误字段消息 Map>> fieldMap = new HashMap<>(); for (BindingResultError bindingResultError : bindingResult.getAllErrors()) { String messageCode = bindingResultError.getCode(); Map resp = new HashMap<>(); resp.put("code", "invalid"); resp.put("message", messageCode); resp.put("rejectedValue", bindingResultError.getRejectedValue()); fieldMap.computeIfAbsent(bindingResultError.getNestedPath(), k -> new ArrayList<>(2)).add(resp); } response.setStatusCode(HttpResponseStatus.UNPROCESSABLE_ENTITY.code()); Map obj = new HashMap<>(); obj.put("message", HttpResponseStatus.UNPROCESSABLE_ENTITY.reasonPhrase()); obj.put("fields", fieldMap); return Future.succeededFuture(obj); } } /** * 示例控制器 */ @RestController @RequestMapping("/test") public static class ExampleController { @GetMapping("/demo") public Future demo() { return Future.succeededFuture("hello word! mirage..."); } @GetMapping("/get") public Future get(@QueryParam("name") String name) { return Future.succeededFuture(name); } @GetMapping("/mirage/:version") public Future version(@PathVariable("version") Integer version) { return Future.succeededFuture(version); } @RequestMapping("/ex") public Future ex() { int a = 1 / 0; // 模拟异常状态 return Future.succeededFuture(); } @PostMapping("/validation") public Future validationBean(@RequestBody @Validated ValidationBean bean) { // 如果需要即时抛出参数检查异常 请去掉当前方法的 BindingResult 参数 // 如果需要在方法内做特殊判断,可以使用 BindingResult 处理完在抛出 return Future.succeededFuture(bean); } @GetMapping("/queryParams") public Future queryParams(@QueryParam("bean") @Validated ValidationBean bean) { return Future.succeededFuture(bean); } @PostMapping("/form") public Future form(@FormAttribute("name") String name) { return Future.succeededFuture(name); } @PostMapping("/fileUpload") public Future fileUpload(@FileUpload @NotNull MultipartFileUpload fileUpload) { MultipartFile multipartFile = fileUpload.get(); return multipartFile.readBuf() .compose(buf -> Future.succeededFuture(buf.toString())); } @PostMapping("/fileUpload-withParams") public Future fileUploadWithParams(@FileUpload @NotNull MultipartFileUpload fileUpload, @FormAttribute("options") String options) { MultipartFile multipartFile = fileUpload.get(); return multipartFile.readBuf() .compose(buf -> Future.succeededFuture(buf.toString() + ": " + options)); } @GetMapping("/header") public Future header(@RequestHeader(name = "host") String host) { return Future.succeededFuture(host); } @GetMapping("/restful") public Future restfulGet() { return Future.succeededFuture("get"); } @PostMapping("/restful") public Future restfulPost() { return Future.succeededFuture("post"); } } @Setter @Getter public static class ValidationBean { @NotBlank(message = "名称不可以为空!") private String name; @NotBlank(message = "值不可以为空!") private String value; } }