Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Set of alternative routing implementations for Javalin 5.x & 6.x

License

NotificationsYou must be signed in to change notification settings

javalin/javalin-routing-extensions

Repository files navigation

Javalin is very flexible and allows you to extend it in many ways.This repository contains a set of extensions for Javalin routing system following some of the most popular patterns.Each approach has pros and cons, so you should choose the one that fits your needs the best.

  1. Installation
  2. Usage
    1. Annotated
    2. DSL
      1. In-place
      2. Properties
    3. Coroutines
    4. Core

Installation

Each module is distributed as a separate artifact:

dependencies {val javalinRoutingExtensions="6.7.0"    implementation("io.javalin.community.routing:routing-core:$javalinRoutingExtensions")    implementation("io.javalin.community.routing:routing-annotated:$javalinRoutingExtensions")    implementation("io.javalin.community.routing:routing-dsl:$javalinRoutingExtensions")    implementation("io.javalin.community.routing:routing-coroutines:$javalinRoutingExtensions")}

Usage

This chapter provides short preview of each module.For more details, please refer to the documentation or full example of each module.First of all, not each module is available for Java users, take a look on the table below to check requirements:

ModuleLanguagesReflections
AnnotatedJava, KotlinYes(as long as we won't provide annotation processor)
DSL In-place
DSL Properties
KotlinOptional
CoroutinesKotlinNo
CoreJava, KotlinNo

Annotated

This module provides set of annotations to simplify routing setup & basic http operations.This is probably the most common approach to routing in Java world,some people may even say that it's the only one.Take a look on the example below to see how it looks like:

importstaticio.javalin.community.routing.annotations.AnnotatedRouting.Annotated;// register endpoints with prefix@Endpoints("/api")staticfinalclassExampleEndpoints {privatefinalExampleServiceexampleService;// pass dependencies required to handle requestspublicExampleEndpoints(ExampleServiceexampleService) {this.exampleService =exampleService;    }// use Javalin-specific routes@BeforevoidbeforeEach(Contextctx) {ctx.header("X-Example","Example");    }// describe http method and path with annotation@Post("/hello")// use parameters to extract data from requestvoidsaveExample(Contextcontext,@Nullable@Header(AUTHORIZATION)Stringauthorization,@BodyExampleDtoentity) {if (authorization ==null) {context.status(401);return;        }exampleService.saveExample(entity);    }// you can combine it with OpenApi plugin@OpenApi(path ="/api/hello/{name}",methods = {GET },summary ="Find example by name",pathParams = {@OpenApiParam(name ="name",description ="Name of example to find") },responses = {@OpenApiResponse(status ="200",description ="Example found",content =@OpenApiContent(from =ExampleDto.class)) },versions = {"default","2" }    )// you can also use out-of-the-box support for versioned routes@Version("2")@Get("/hello/{name}")voidfindExampleV2(Contextcontext,@ParamStringname) {context.result(exampleService.findExampleByName(name).name);    }/* OpenApi [...] */@Version("1")@Get("/hello/{name}")voidfindExampleV1(Contextctx) {thrownewUnsupportedOperationException("Deprecated");    }// register exception handlers alongside endpoints@ExceptionHandler(Exception.class)voiddefaultExceptionHandler(Exceptione,Contextctx) {ctx.status(500).result("Something went wrong: " +e.getClass());    }}publicstaticvoidmain(String[]args) {Javalin.createAndStart(config -> {// prepare dependenciesvarexampleService =newExampleService();// register endpointsconfig.router.mount(Annotated,routing -> {routing.registerEndpoints(newExampleEndpoints(exampleService));        });    });// test request to `saveExample` endpointHttpResponse<?>saved =Unirest.post("http://localhost:7000/api/hello")            .basicAuth("Panda","passwd")            .body(newExampleDto("Panda"))            .asEmpty();System.out.println("Entity saved: " +saved.getStatusText());// Entity saved: OK// test request to `findExampleV2` endpointStringresult =Unirest.get("http://localhost:7000/api/hello/Panda")            .header("X-API-Version","2")            .asString()            .getBody();System.out.println("Entity: " +result);// Entity: Panda}

This approach requires some reflections under the hood to make work at this moment,butwe're working on annotation processor to remove this requirement.

Another thing you can notice is that we're creating endpoint class instance using constructor,not built-in DI framework.This is because in general we consider this as a good practice -not only because you're in full control over the execution flow,but also because it forces you to make concious decision about the scope of your dependencies & architecture.

If you don't really care about it, and you're just looking for a tool that will get the job done,you can use literally any DI framework you want that is available for Java/Kotlin.We may recommend Dagger2, because it verifies your code at compile time,so it's safer than heavy reflection-based alternatives.

Keep in mind, that if you want to use named parameters in your endpoints,you have to pass-parameters flag to your compiler to preserve parameter names in bytecode.

Full example:AnnotationsRoutingExample.java

DSL

DSL provides extensible base for creating custom DSLs for routing.

In-place

By default,this module provides basic implementation of Ktor-like routing for Kotlin appswith support for type-safe paths:

@Path("/panda/{age}")data classPandaPath(valage:Int)funmain() {Javalin.create { config->        config.routing(CustomDsl) {            before {// `endpointHandlerPath` comes from Context class                result("Called endpoint:${matchedPath()}")            }            get("/") {// `helloWorld` comes from CustomScope class                result(helloWorld())            }            get<PandaPath> { path->// support for type-safe paths                result(path.age.toString())            }            exception(Exception::class) { anyException->// support for exception handlers                result(anyException.message?:"Unknown error")            }        }    }.start(8080)}

Because of the extensible nature of DSL, you may adjust it to your needs!You can find base implementation of custom DSL definition here:InPlaceExample.kt

Properties

Property based implementation of DSL allows you to easily define routes in multiple sources,outside the main setup scope.This approach is very similar to Spring Boot's@RestController annotation,but it also supports your custom type-safe DSL, andyou're in full control of your execution flow.

// Some dependenciesclassExampleService {funsave(animal:String)=println("Saved animal:$animal")}// Utility representation of custom routing in your applicationabstractclassExampleRouting :DslRoutes<DslRoute<CustomScope,Unit>,CustomScope,Unit>// Endpoint (domain router)classAnimalEndpoints(privatevalexampleService:ExampleService) : ExampleRouting() {    @OpenApi(        path="/animal/{name}",        methods= [HttpMethod.GET]    )privateval findAnimalByName= route("/animal/<name>",GET) {        result(pathParam("name"))    }    @OpenApi(        path="/animal/{name}",        methods= [HttpMethod.POST]    )privateval saveAnimal= route("/animal/<name>",POST) {        exampleService.save(pathParam("name"))    }privateval defaultExceptionHandler= exceptionHandler(Exception::class) { regularException->println("Exception:${regularException.message}")    }overridefunroutes()=setOf(findAnimalByName, saveAnimal)overridefunexceptionHandlers()=setOf(defaultExceptionHandler)}funmain() {// prepare dependenciesval exampleService=ExampleService()// setup & launch applicationJavalin        .create { it.routing(CustomDsl,AnimalEndpoints(exampleService)/*, provide more classes with endpoints*/) }        .start(8080)}

This example is based on previous in-place example,you can check its source code here:PropertyDslExample.kt

Coroutines

Experimental: This module is more like a reference on how to use coroutines with Javalin.The production-readiness of this module is unknown, especially in complex scenarios.

The coroutines module provides API similar toDSL :: Properties module,but it uses coroutines & suspend directives to provide asynchronous & non-blocking endpoint execution.

// Custom scope used by routing DSLclassCustomScope(valctx:Context) : Context by ctx {// blocks thread using reactive `delay` functionsuspendfunnonBlockingDelay(message:String):String= delay(2000L).let { message }}// Utility class representing group of reactive routesabstractclassExampleRoutes :ReactiveRoutes<ReactiveRoute<CustomScope,Unit>,CustomScope,Unit>()// Endpoint (domain router)classExampleEndpoint(privatevalexampleService:ExampleService) : ExampleRoutes() {// you can use suspend functions in coroutines context// and as long as they're truly reactive, they won't freeze itprivateval nonBlockingAsync= reactiveRoute("/async",GET) {        result(nonBlockingDelay("Non-blocking Async"))    }overridefunroutes()=setOf(nonBlockingAsync)}funmain() {// prepare dependenciesval exampleService=ExampleService()// create coroutines servlet with single-threaded executorval coroutinesServlet=DefaultContextCoroutinesServlet(        executorService=Executors.newSingleThreadExecutor(),        contextFactory= {CustomScope(it) },    )// setup Javalin with reactive routingJavalin        .create { config->            config.reactiveRouting(coroutinesServlet,ExampleEndpoint(exampleService))        }        .events {            it.serverStopping { coroutinesServlet.prepareShutdown() }            it.serverStopped { coroutinesServlet.completeShutdown() }        }        .start("127.0.0.1",8080)}

Full example:ReactiveRoutingExample.kt

Core

The core module contains shared components for the other modules.The most important functionality is theRouteComparatorwhich is used to sort given set of routes in the correct order by associated route path.

Other examples

  • Reposilite - real world app using Javalin with property-based DSL routing

About

Set of alternative routing implementations for Javalin 5.x & 6.x

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Contributors4

  •  
  •  
  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp