Skip to content

Commit 2a331c0

Browse files
authored
Bring the README more in line with the Docs
1 parent b68be8c commit 2a331c0

File tree

1 file changed

+267
-61
lines changed

1 file changed

+267
-61
lines changed

README.md

Lines changed: 267 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,328 @@
1-
# avaje-http
1+
# [Avaje-HTTP](https://avaje.io/http/)
22

3-
Http server and client libraries and code generation.
3+
HTTP server and client libraries via code generation.
44

5-
## http server
5+
## HTTP Server
66

77
A jax-rs style controllers with annotations (`@Path`, `@Get` ...)
88
that is lightweight by using source code generation (annotation processors)
9-
to generate adapter code for Javalin and Helidon SE.
9+
to generate adapter code for Javalin and Helidon SE/Nima.
1010

1111
- Lightweight as in 65Kb library + generated source code
12-
- Full use of Javalin or Helidon SE as desired
12+
- Full use of Javalin or Helidon SE/Nima as desired
1313

14+
## Quick Start
1415

15-
## Define a Controller
16+
#### 1. Add dependencies
17+
```xml
18+
<dependency>
19+
<groupId>io.avaje</groupId>
20+
<artifactId>avaje-http-api</artifactId>
21+
<version>${avaje.http.version}</version>
22+
</dependency>
23+
```
24+
#### 2. Add the generator module for your desired microframework as a annotation processor.
25+
26+
```xml
27+
<!-- Annotation processors -->
28+
<dependency>
29+
<groupId>io.avaje</groupId>
30+
<artifactId>avaje-inject-generator</artifactId>
31+
<version>${avaje-inject.version}</version>
32+
<scope>provided</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>io.avaje</groupId>
36+
<artifactId>avaje-http-javalin-generator</artifactId>
37+
<version>${avaje-http.version}</version>
38+
<scope>provided</scope>
39+
</dependency>
40+
```
41+
If there are other annotation processors and they are specified via <i>maven-compiler-plugin</i> then we add avaje-http-generator there instead.
42+
```xml
43+
<plugin>
44+
<groupId>org.apache.maven.plugins</groupId>
45+
<artifactId>maven-compiler-plugin</artifactId>
46+
<configuration>
47+
<annotationProcessorPaths> <!-- All annotation processors specified here -->
48+
<path>
49+
<groupId>io.avaje</groupId>
50+
<artifactId>avaje-inject-generator</artifactId>
51+
<version>${avaje-inject.version}</version>
52+
</path>
53+
<path>
54+
<groupId>io.avaje</groupId>
55+
<artifactId>avaje-http-javalin-generator</artifactId>
56+
<version>${avaje-http.version}</version>
57+
</path>
58+
<path>
59+
... other annotation processor ...
60+
</path>
61+
</annotationProcessorPaths>
62+
</configuration>
63+
</plugin>
64+
```
65+
#### 3. Define a Controller (These APT processors work with both Java and Kotlin.)
1666
```java
17-
package org.example.hello
67+
package org.example.hello;
1868

19-
import io.avaje.http.api.Controller
20-
import io.avaje.http.api.Get
21-
import io.avaje.http.api.Path
69+
import io.avaje.http.api.Controller;
70+
import io.avaje.http.api.Get;
71+
import io.avaje.http.api.Path;
72+
import java.util.List;
2273

2374
@Path("/widgets")
2475
@Controller
25-
class WidgetController(private val hello: HelloComponent) {
76+
public class WidgetController {
77+
private final HelloComponent hello;
78+
public WidgetController(HelloComponent hello) {
79+
this.hello = hello;
80+
}
81+
82+
@Get("/{id}")
83+
Widget getById(int id) {
84+
return new Widget(id, "you got it"+ hello.hello());
85+
}
2686

27-
@Get("/:id")
28-
fun getById(id : Int): Widget {
29-
return Widget(id, "you got it${hello.hello()}")
87+
@Get()
88+
List<Widget> getAll() {
89+
return List.of(new Widget(1, "Rob"), new Widget(2, "Fi"));
3090
}
3191

32-
@Get
33-
fun getAll(): MutableList<Widget> {
92+
record Widget(int id, String name){};
93+
}
94+
```
3495

35-
val list = mutableListOf<Widget>()
36-
list.add(Widget(1, "Rob"))
37-
list.add(Widget(2, "Fi"))
96+
## Usage
97+
The annotation processor will generate controller adapters that can register routes to Javalin/Helidon. The natural way to use the generated adapters is to get a DI library to find and wire them. This is what the below examples do and they use [Avaje-Inject](https://avaje.io/inject/) to do this.
3898

39-
return list
40-
}
99+
Note that there isn't a requirement to use Avaje for dependency injection. Any DI library that can find and wire the generated @Singleton beans can be used. You can even use Dagger2 or Guice to wire the controllers if you so desire.
41100

42-
data class Widget(var id: Int, var name: String)
43-
}
101+
### Usage with Javalin
102+
103+
The annotation processor will generate controller classes implementing the WebRoutes interface, which means we can
104+
get all the WebRoutes and register them with Javalin using:
44105

106+
```java
107+
var routes = BeanScope.builder().build().list(WebRoutes.class);
108+
109+
Javalin.create()
110+
.routes(() -> routes.forEach(WebRoutes::registerRoutes))
111+
.start();
112+
```
113+
114+
### Usage with Helidon SE
115+
116+
The annotation processor will generate controller classes implementing the Helidon Service interface, which we can use
117+
get all the Services and register them with Helidon `RoutingBuilder`.
118+
119+
```java
120+
var routes = BeanScope.builder().build().list(Service.class);
121+
var routingBuilder = Routing.builder().register(routes.stream().toArray(Service[]::new));
122+
WebServer.builder()
123+
.addMediaSupport(JacksonSupport.create())
124+
.routing(routingBuilder)
125+
.build()
126+
.start();
45127
```
46128

47-
## Generated source
129+
### Usage with Helidon Nima
130+
131+
The annotation processor will generate controller classes implementing the Helidon HttpService interface, which we can use
132+
get all the services and register them with the Helidon `HttpRouting`.
133+
134+
```java
135+
var routes = BeanScope.builder().build().list(HttpService.class);
136+
final var builder = HttpRouting.builder();
137+
138+
for (final HttpService httpService : routes) {
139+
httpService.routing(builder);
140+
}
48141

49-
The annotation processor will generate a `$Route` for the controller like below.
142+
WebServer.builder()
143+
.addRouting(builder.build())
144+
.build()
145+
.start();
146+
```
147+
## Generated sources
50148

51-
Note that this class implements the WebRoutes interface, which means we can
52-
get all the WebRoutes and register them with Javalin using.
149+
### (Javalin) The generated WidgetController$Route.java is:
53150

54151
```java
55-
fun main(args: Array<String>) {
152+
@Generated("avaje-javalin-generator")
153+
@Component
154+
public class WidgetController$Route implements WebRoutes {
56155

57-
// get all the webRoutes
58-
val webRoutes = ApplicationScope.list(WebRoutes::class.java)
156+
private final WidgetController controller;
59157

60-
val javalin = Javalin.create()
158+
public WidgetController$Route(WidgetController controller) {
159+
this.controller = controller;
160+
}
61161

62-
javalin.routes {
63-
// register all the routes with Javalin
64-
webRoutes.forEach { it.registerRoutes() }
162+
@Override
163+
public void registerRoutes() {
164+
165+
ApiBuilder.get("/widgets/{id}", ctx -> {
166+
ctx.status(200);
167+
var id = asInt(ctx.pathParam("id"));
168+
var result = controller.getById(id);
169+
ctx.json(result);
170+
});
171+
172+
ApiBuilder.get("/widgets", ctx -> {
173+
ctx.status(200);
174+
var result = controller.getAll();
175+
ctx.json(result);
176+
});
65177

66-
// other routes etc as desired
67-
ApiBuilder.get("/foo") { ctx ->
68-
ctx.html("bar")
69-
ctx.status(200)
70-
}
71-
...
72178
}
73179

74-
javalin.start(7000)
75180
}
76-
77181
```
78182

79-
### The generated WidgetController$Route.java is:
183+
### (Helidon SE) The generated WidgetController$Route.java is:
184+
```java
185+
@Generated("io.dinject.helidon-generator")
186+
@Singleton
187+
public class WidgetController$Route implements Service {
188+
189+
private final WidgetController controller;
80190

191+
public WidgetController$Route(WidgetController controller) {
192+
this.controller = controller;
193+
}
194+
195+
@Override
196+
public void update(Routing.Rules rules) {
197+
198+
rules.get("/widgets/{id}", this::_getById);
199+
rules.post("/widgets", this::_getAll);
200+
}
201+
202+
private void _getById(ServerRequest req, ServerResponse res) {
203+
int id = asInt(req.path().param("id"));
204+
res.send(controller.getById(id));
205+
}
206+
207+
private void _getAll(ServerRequest req, ServerResponse res) {
208+
res.send(controller.getAll());
209+
}
210+
211+
}
212+
```
213+
214+
### (Helidon Nima) The generated WidgetController$Route.java is:
81215

82216
```java
83-
package org.example.hello;
217+
@Generated("avaje-helidon-nima-generator")
218+
@Component
219+
public class WidgetController$Route implements HttpService {
84220

85-
import static io.avaje.http.api.PathTypeConversion.*;
86-
import io.avaje.http.api.WebRoutes;
87-
import io.javalin.apibuilder.ApiBuilder;
88-
import javax.annotation.Generated;
89-
import javax.inject.Singleton;
90-
import org.example.hello.WidgetController;
221+
private final WidgetController controller;
222+
public WidgetController$Route(WidgetController controller) {
223+
this.controller = controller;
224+
}
91225

92-
@Generated("io.avaje.javalin-generator")
93-
@Singleton
226+
@Override
227+
public void routing(HttpRules rules) {
228+
rules.get("/widgets/{id}", this::_getById);
229+
rules.get("/widgets", this::_getAll);
230+
}
231+
232+
private void _getById(ServerRequest req, ServerResponse res) {
233+
var pathParams = req.path().pathParameters();
234+
int id = asInt(pathParams.first("id").get());
235+
var result = controller.getById(id);
236+
res.send(result);
237+
}
238+
239+
private void _getAll(ServerRequest req, ServerResponse res) {
240+
var pathParams = req.path().pathParameters();
241+
var result = controller.getAll();
242+
res.send(result);
243+
}
244+
245+
}
246+
```
247+
248+
## Generated sources ([Avaje-Jsonb](https://github.com/avaje/avaje-jsonb))
249+
If [Avaje-Jsonb](https://github.com/avaje/avaje-jsonb) is detected, http generators with support will use it for faster Json message processing.
250+
251+
### (Javalin) The generated WidgetController$Route.java is:
252+
```java
253+
@Generated("avaje-javalin-generator")
254+
@Component
94255
public class WidgetController$Route implements WebRoutes {
95256

96-
private final WidgetController controller;
257+
private final WidgetController controller;
258+
private final JsonType<List<Widget>> listWidgetJsonType;
259+
private final JsonType<Widget> widgetJsonType;
97260

98-
public WidgetController$route(WidgetController controller) {
99-
this.controller = controller;
100-
}
261+
public WidgetController$Route(WidgetController controller, Jsonb jsonB) {
262+
this.controller = controller;
263+
this.listWidgetJsonType = jsonB.type(Widget.class).list();
264+
this.widgetJsonType = jsonB.type(Widget.class);
265+
}
101266

102267
@Override
103268
public void registerRoutes() {
104269

105-
ApiBuilder.get("/widgets/:id", ctx -> {
106-
int id = asInt(ctx.pathParam("id"));
107-
ctx.json(controller.getById(id));
270+
ApiBuilder.get("/widgets/{id}", ctx -> {
108271
ctx.status(200);
272+
var id = asInt(ctx.pathParam("id"));
273+
var result = controller.getById(id);
274+
widgetJsonType.toJson(result, ctx.contentType("application/json").outputStream());
109275
});
110276

111277
ApiBuilder.get("/widgets", ctx -> {
112-
ctx.json(controller.getAll());
113278
ctx.status(200);
279+
var result = controller.getAll();
280+
listWidgetJsonType.toJson(result, ctx.contentType("application/json").outputStream());
114281
});
115282

116283
}
284+
117285
}
118286
```
119287

120-
Note that this APT processor works with both Java and Kotlin.
288+
### (Helidon Nima) The generated WidgetController$Route.java is:
289+
290+
```java
291+
@Generated("avaje-helidon-nima-generator")
292+
@Component
293+
public class WidgetController$Route implements HttpService {
294+
295+
296+
private final WidgetController controller;
297+
private final JsonType<Widget> widgetJsonType;
298+
private final JsonType<List<Widget>> listWidgetJsonType;
121299

300+
public WidgetController$Route(WidgetController controller, Jsonb jsonB) {
301+
this.controller = controller;
302+
this.widgetJsonType = jsonB.type(Widget.class);
303+
this.listWidgetJsonType = jsonB.type(Widget.class).list();
304+
}
305+
306+
@Override
307+
public void routing(HttpRules rules) {
308+
rules.get("/widgets/{id}", this::_getById);
309+
rules.get("/widgets", this::_getAll);
310+
}
311+
312+
private void _getById(ServerRequest req, ServerResponse res) {
313+
var pathParams = req.path().pathParameters();
314+
int id = asInt(pathParams.first("id").get());
315+
var result = controller.getById(id);
316+
res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON);
317+
widgetJsonType.toJson(result, res.outputStream());
318+
}
319+
320+
private void _getAll(ServerRequest req, ServerResponse res) {
321+
var pathParams = req.path().pathParameters();
322+
var result = controller.getAll();
323+
res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON);
324+
listWidgetJsonType.toJson(result, res.outputStream());
325+
}
122326

327+
}
328+
```

0 commit comments

Comments
 (0)