Chapter 4. Defining REST Services
Apache Camel supports multiple approaches to defining REST services. In particular, Apache Camel provides the REST DSL (Domain Specific Language), which is a simple but powerful fluent API that can be layered over any REST component.
4.1. Overview of REST in Camel Copy linkLink copied to clipboard!
Apache Camel provides many different approaches and components for defining REST services in your Camel applications. This section provides a quick overview of these different approaches and components, so that you can decide which implementation and API best suits your requirements.
4.1.1. What is REST? Copy linkLink copied to clipboard!
Representational State Transfer (REST) is an architecture for distributed applications that centers around the transmission of data over HTTP, using only the four basic HTTP verbs: GET, POST, PUT, and DELETE.
In contrast to a protocol such as SOAP, which treats HTTP as a mere transport protocol for SOAP messages, the REST architecture exploits HTTP directly. The key insight is that the HTTP protocol itself, augmented by a few simple conventions, is eminently suitable to serve as the framework for distributed applications.
4.1.2. A sample REST invocation Copy linkLink copied to clipboard!
Because the REST architecture is built around the standard HTTP verbs, in many cases you can use a regular browser as a REST client. For example, to invoke a simple Hello World REST service running on the host and port, localhost:9091, you could navigate to a URL like the following in your browser:
http://localhost:9091/say/hello/Garp
The Hello World REST service might then return a response string, such as:
Hello Garp
Which gets displayed in your browser window. The ease with which you can invoke REST services, using nothing more than a standard browser (or the curl command-line utility), is one of the many reasons why the REST protocol has rapidly gained popularity.
4.1.3. REST wrapper layers Copy linkLink copied to clipboard!
The following REST wrapper layers offer a simplified syntax for defining REST services and can be layered on top of different REST implementations:
- REST DSL
-
The REST DSL (in
camel-core) is a facade or wrapper layer that provides a simplified builder API for defining REST services. The REST DSL does not itself provide a REST implementation: it must be combined with an underlying REST implementation. For example, the following Java code shows how to define a simple Hello World service using the REST DSL:
rest("/say")
.get("/hello/{name}").route().transform().simple("Hello ${header.name}");
- Rest component
The REST component (in
camel-core) is a wrapper layer that enables you to define REST services using a URI syntax. Like the REST DSL, the REST component does not itself provide a REST implementation. It must be combined with an underlying REST implementation.If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is
camel-http.
The following Java code shows how to define a simple Hello World service using the camel-rest component:
from("rest:get:say:/hello/{name}").transform().simple("Hello ${header.name}");
4.1.4. REST implementations Copy linkLink copied to clipboard!
Apache Camel provides several different REST implementations, through the following components:
- REST starter (Required)
The REST starter (camel-rest-starter) allows defining REST endpoints (consumer) using the Rest DSL and plugin to other Camel components as the REST transport.
The REST component can also be used as a client (producer) to call REST services.
URI format:
rest://method:path[:uriTemplate]?[options]- Platform HTTP component (Recommended)
The Platform HTTP component (in
camel-platform-http-starter) is used to allow Camel to use the existing HTTP server from the runtime when running on Camel on Spring Boot, Quarkus, or other runtimes.For example, the following Java code shows how to define a simple Hello World service:
from("platform-http:/hello").setBody(simple("Hello ${header.name}"));
- Servlet component
The Servlet component (in
camel-servlet) is a component that binds a Java servlet to a Camel route. In other words, the Servlet component enables you to package and deploy a Camel route as if it was a standard Java servlet. The Servlet component is therefore particularly useful, if you need to deploy a Camel route inside a servlet container (for example, into an Apache Tomcat HTTP server or into a JBoss Enterprise Application Platform container).The Servlet component on its own, however, does not provide any convenient REST API for defining REST services. The easiest way to use the Servlet component, therefore, is to combine it with the REST DSL, so that you can define REST services with a user-friendly API.
4.1.4.1. JAX-RS REST implementation Copy linkLink copied to clipboard!
JAX-RS (Java API for RESTful Web Services) is a framework for binding REST requests to Java objects, where the Java classes must be decorated with JAX-RS annotations in order to define the binding. The JAX-RS framework is relatively mature and provides a sophisticated framework for developing REST services, but it is also somewhat complex to program.
The JAX-RS integration with Apache Camel is implemented by the CXFRS component, which is layered over Apache CXF. In outline, JAX-RS binds a REST request to a Java class using the following annotations (where this is only an incomplete sample of the many available annotations):
- @Path
- Annotation that can map a context path to a Java class or map a sub-path to a particular Java method.
- @GET, @POST, @PUT, @DELETE
- Annotations that map a HTTP method to a Java method.
- @PathParam
- Annotation that either maps a URI parameter to a Java method argument, or injects a URI parameter into a field.
- @QueryParam
- Annotation that either maps a query parameter to a Java method argument, or injects a query parameter into a field.
The body of a REST request or REST response is normally expected to be in JAXB (XML) data format. But Apache CXF also supports conversion of JSON format to JAXB format, so that JSON messages can also be parsed.
4.2. Defining Services with REST DSL Copy linkLink copied to clipboard!
4.2.1. REST DSL is a facade Copy linkLink copied to clipboard!
The REST DSL is effectively a facade that provides a simplified syntax for defining REST services in a Java DSL or an XML DSL (Domain Specific Language). REST DSL does not actually provide the REST implementation, it is just a wrapper around an existing REST implementation (of which there are several in Apache Camel).
4.2.2. Advantages of the REST DSL Copy linkLink copied to clipboard!
The REST DSL wrapper layer offers the following advantages:
- A modern easy-to-use syntax for defining REST services.
- Compatible with multiple different Apache Camel components.
-
OpenAPI integration (through the
camel-openapi-javacomponent).
4.2.3. Components that integrate with REST DSL Copy linkLink copied to clipboard!
Because the REST DSL is not an actual REST implementation, one of the first things you need to do is to choose a Camel component to provide the underlying implementation. We suggest you use the following Camel components with REST DSL:
-
Servlet component (
camel-servlet). -
Platform HTTP component (
camel-platform-http).
The REST component (part of camel-core) is not a REST implementation. Like the REST DSL, the REST component is a facade, providing a simplified syntax to define REST services using a URI syntax. The REST component also requires an underlying REST implementation.
4.2.4. Configuring REST DSL to use a REST implementation Copy linkLink copied to clipboard!
To specify the REST implementation, you use either the restConfiguration() builder (in Java DSL) or the restConfiguration element (in XML DSL). For example, to configure REST DSL to use the Spark-Rest component, you would use a builder expression like the following in the Java DSL:
restConfiguration().component("platform-http").port(9091);
And you would use an element like the following (as a child of camelContext) in the XML DSL:
<restConfiguration component="platform-http" port="9091"/>
4.2.5. Syntax Copy linkLink copied to clipboard!
The Java DSL syntax for defining a REST service is as follows:
rest("BasePath").Option()+.
.Verb("Path").Option()+.[to() | route().CamelRoute.endRest()]
.Verb("Path").Option()+.[to() | route().CamelRoute.endRest()]
...
.Verb("Path").Option()+.[to() | route().CamelRoute];
Where `CamelRoute ` is an optional embedded Camel route (defined using the standard Java DSL syntax for routes).
The REST service definition starts with the rest() keyword, followed by one or more verb clauses that handle specific URL path segments. The HTTP verb can be one of get(), head(), put(), post(), delete(), patch() or verb(). Each verb clause can use either of the following syntaxes:
Verb clause ending in
to()keyword. For example:get("...").Option()+.to("...")Verb clause ending in
route()keyword (for embedding a Camel route). For example:get("...").Option()+.route("...").CamelRoute.endRest()
4.2.6. REST DSL with Java Copy linkLink copied to clipboard!
In Java, to define a service with the REST DSL, put the REST definition into the body of a RouteBuilder.configure() method, just like you do for regular Apache Camel routes. A simple REST service can be defined as follows, where we use rest() to define the services as shown below:
@Override
public void configure() throws Exception {
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").consumes("application/json").to("direct:bye")
.post("/bye").to("mock:update");
from("direct:hello")
.transform().constant("Hello World");
from("direct:bye")
.transform().constant("Bye World");
}
The preceding example features three different kinds of builder:
rest()-
Defines a service using the REST DSL. Each of the verb clauses are terminated by a
to()keyword, which forwards the incoming message to adirectendpoint (thedirectcomponent splices routes together within the same application). from()- Defines a regular Camel route.
4.2.7. REST DSL with XML Copy linkLink copied to clipboard!
In XML, to define a service with the XML DSL, define a rest element as a child of the camelContext element. The example above can be defined in XML as shown below:
<camel>
<rest path="/say">
<get path="/hello">
<to uri="direct:hello"/>
</get>
<get path="/bye">
<to uri="direct:bye"/>
</get>
</rest>
<route>
<from uri="direct:hello"/>
<transform>
<constant>Hello World</constant>
</transform>
</route>
<route>
<from uri="direct:bye"/>
<transform>
<constant>Bye World</constant>
</transform>
</route>
</camel>
4.2.8. Specifying a base path Copy linkLink copied to clipboard!
The rest() keyword (Java DSL) or the path attribute of the rest element (XML DSL) allows you to define a base path, which is then prefixed to the paths in all verb clauses. For example, given the following snippet of Java DSL:
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").to("direct:bye");
Or given the following snippet of XML DSL:
<rest path="/say">
<get uri="/hello">
<to uri="direct:hello"/>
</get>
<get uri="/bye" consumes="application/json">
<to uri="direct:bye"/>
</get>
</rest>
The REST DSL builder gives you the following URL mappings:
/say/hello
/say/bye
The base path is optional. If you prefer, you could (less elegantly) specify the full path in each of the verb clauses:
rest()
.get("/say/hello").to("direct:hello")
.get("/say/bye").to("direct:bye");
4.2.9. Using Dynamic To Copy linkLink copied to clipboard!
The REST DSL supports the toD dynamic to parameter. Use this parameter to specify URIs.
For example, in JMS a dynamic endpoint URI could be defined in the following way:
public void configure() throws Exception {
rest("/say")
.get("/hello/{language}").toD("jms:queue:hello-${header.language}");
}
In XML DSL, the same details would look like this:
<rest uri="/say">
<get uri="/hello//{language}">
<toD uri="jms:queue:hello-${header.language}"/>
</get>
<rest>
4.2.10. URI templates Copy linkLink copied to clipboard!
In a verb argument, you can specify a URI template, which enables you to capture specific path segments in named properties (which are then mapped to Camel message headers). For example, if you would like to personalize the Hello World application so that it greets the caller by name, you could define a REST service like the following:
rest("/say")
.get("/hello/{name}").to("direct:hello")
.get("/bye/{name}").to("direct:bye");
from("direct:hello")
.transform().simple("Hello ${header.name}");
from("direct:bye")
.transform().simple("Bye ${header.name}");
The URI template captures the text of the {name} path segment and copies this captured text into the name message header. If you invoke the service by sending a GET HTTP Request with the URL ending in /say/hello/Joe, the HTTP Response is Hello Joe.
4.2.11. Embedded route syntax Copy linkLink copied to clipboard!
Instead of terminating a verb clause with the to() keyword (Java DSL) or the to element (XML DSL), you have the option of embedding an Apache Camel route directly into the REST DSL, using the route() keyword (Java DSL) or the route element (XML DSL). The route() keyword enables you to embed a route into a verb clause, with the following syntax:
RESTVerbClause.route("...").CamelRoute.endRest()
Where the endRest() keyword (Java DSL only) is a necessary punctuation mark that enables you to separate the verb clauses (when there is more than one verb clause in the rest() builder).
For example, you could refactor the Hello World example to use embedded Camel routes, as follows in Java DSL:
rest("/say")
.get("/hello").route().transform().constant("Hello World").endRest()
.get("/bye").route().transform().constant("Bye World");
If you define any exception clauses (using onException()) or interceptors (using intercept()) in the current CamelContext, these exception clauses and interceptors are also active in the embedded routes.
4.2.12. REST DSL and HTTP transport component Copy linkLink copied to clipboard!
If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is camel-http.
4.2.13. Specifying the content type of requests and responses Copy linkLink copied to clipboard!
You can filter the content type of HTTP requests and responses using the consumes() and produces() options in Java, or the consumes and produces attributes in XML. For example, some common content types (officially known as Internet media types) are the following:
-
text/plain -
text/html -
text/xml -
application/json -
application/xml
The content type is specified as an option on a verb clause in the REST DSL. For example, to restrict a verb clause to accept only text/plain HTTP requests, and to send only text/html HTTP responses, you would use Java code like the following:
rest("/email")
.post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");
And in XML, you can set the consumes and produces attributes:
<camelContext xmlns="http://camel.apache.org/schema/spring">
...
<rest path="/email">
<post uri="/to/{recipient}" consumes="text/plain" produces="text/html">
<to "direct:foo"/>
</get>
</rest>
</camelContext>
You can also specify the argument to consumes() or produces() as a comma-separated list. For example, consumes("text/plain, application/json").
4.2.14. Additional HTTP methods Copy linkLink copied to clipboard!
Some HTTP server implementations support additional HTTP methods, which are not provided by the standard set of verbs in the REST DSL, get(), head(), put(), post(), delete(), patch(). To access additional HTTP methods, you can use the generic keyword, verb(), in Java DSL and the generic element, verb, in XML DSL.
For example, to implement the TRACE HTTP method in Java:
rest("/say")
.verb("TRACE", "/hello").route().transform();
Where transform() copies the body of the IN message to the body of the OUT message, thus echoing the HTTP request.
To implement the TRACE HTTP method in XML:
<camel>
...
<rest path="/say">
<get path="/hello" method="TRACE">
<to uri="direct:hello"/>
</get>
...
</rest>
...
</camel>
4.2.15. Defining custom HTTP error messages Copy linkLink copied to clipboard!
If your REST service needs to send an error message as its response, you can define a custom HTTP error message as follows:
-
Specify the HTTP error code by setting the
Exchange.HTTP_RESPONSE_CODEheader key to the error code value (for example,400,404, and so on). This setting indicates to the REST DSL that you want to send an error message reply, instead of a regular response. - Populate the message body with your custom error message.
-
Set the
Content-Typeheader, if required. -
If your REST service is configured to marshal to and from Java objects (that is,
bindingModeis enabled), you should ensure that theskipBindingOnErrorCodeoption is enabled (which it is, by default). This is to ensure that the REST DSL does not attempt to unmarshal the message body when sending the response.
The following Java example shows how to define a custom error message:
// Configure the REST DSL, with JSON binding mode
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.json);
// Define the service with REST DSL
rest("/users/")
.post("lives").type(UserPojo.class).outType(CountryPojo.class)
.route()
.choice()
.when().simple("${body.id} < 100")
.bean(new UserErrorService(), "idTooLowError")
.otherwise()
.bean(new UserService(), "livesWhere");
In this example, if the input ID is a number less than 100, we return a custom error message, using the UserErrorService bean, which is implemented as follows:
public class UserErrorService {
public void idTooLowError(Exchange exchange) {
exchange.getMessage().setBody("id value is too low");
exchange.getMessage().setHeader(Exchange.CONTENT_TYPE, "text/plain");
exchange.getMessage().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
}
In the UserErrorService bean we define the custom error message and set the HTTP error code to 400.
4.2.16. Parameter Default Values Copy linkLink copied to clipboard!
Default values can be specified for the headers of an incoming Camel message.
You can specify a default value by using a key word such as verbose on the query parameter. For example, in the code below, the default value is false. This means that if no other value is provided for a header with the verbose key, false will be inserted as a default.
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders")
.param()
.name("verbose")
.type(RestParamType.query)
.defaultValue("false")
.description("Verbose order details")
.endParam()
.to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
4.2.17. Wrapping a JsonParserException in a custom HTTP error message Copy linkLink copied to clipboard!
A common case where you might want to return a custom error message is in order to wrap a JsonParserException exception. For example, you can conveniently exploit the Camel exception handling mechanism to create a custom HTTP error message, with HTTP error code 400:
onException(JsonParseException.class)
.handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
.setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
.setBody().constant("Invalid json data");
4.2.18. REST DSL options Copy linkLink copied to clipboard!
In general, REST DSL options can be applied either directly to the base part of the service definition (that is, immediately following rest()):
rest("/email").*consumes("text/plain").produces("text/html")*
.post("/to/{recipient}").to("direct:foo")
.get("/for/{username}").to("direct:bar");
In which case the specified options apply to all subordinate verb clauses. Or the options can be applied to each individual verb clause:
rest("/email")
.post("/to/{recipient}").*consumes("text/plain").produces("text/html")*.to("direct:foo")
.get("/for/{username}").*consumes("text/plain").produces("text/html")*.to("direct:bar");
In which case the specified options apply only to the relevant verb clause, overriding any settings from the base part.
The REST DSL Options table summarizes the options supported by the REST DSL.
| Java DSL | XML DSL | Description |
|---|---|---|
|
|
|
Specifies the binding mode, which can be used to marshal incoming messages to Java objects (and, optionally, unmarshal Java objects to outgoing messages). Can have the following values: |
|
|
|
Restricts the verb clause to accept only the specified Internet media type (MIME type) in a HTTP Request. Typical values are: |
|
|
| Defines a custom ID for JMX management. |
|
|
| Document the REST service or verb clause. Useful for JMX management and tooling. |
|
|
|
If |
|
|
| Defines a unique ID for the REST service, which is useful to define for JMX management and other tooling. |
|
|
|
Specifies the HTTP method processed by this verb clause. Usually used in conjunction with the generic |
|
|
|
When object binding is enabled (that is, when |
|
|
|
Restricts the verb clause to produce only the specified Internet media type (MIME type) in a HTTP Response. Typical values are: |
|
|
|
When object binding is enabled (that is, when |
| `VerbURIArgument ` |
|
Specifies a path segment or URI template as an argument to a verb. For example, |
| `BasePathArgument ` |
|
Specifies the base path in the |
The folllwing table shows an overview of options supported by the REST DSL.
| Java DSL | XML DSL | Description |
|---|---|---|
|
|
|
Specifies the binding mode, which can be used to marshal incoming messages to Java objects (and, optionally, unmarshal Java objects to outgoing messages). Can have the following values: |
|
|
|
Restricts the verb clause to accept only the specified Internet media type (MIME type) in an HTTP Request. Typical values are: |
|
|
| Defines a custom ID for JMX management. |
|
|
| Document the REST service or verb clause. Useful for JMX management and tooling. |
|
|
|
If |
|
|
| Defines a unique ID for the REST service, which is useful to define for JMX management and other tooling. |
|
|
|
Specifies the HTTP method processed by this verb clause. Usually used in conjunction with the generic |
|
|
|
When object binding is enabled (that is, when |
|
|
|
Restricts the verb clause to produce only the specified Internet media type (MIME type) in an HTTP Response. Typical values are: |
|
|
|
When object binding is enabled (that is, when |
| `VerbURIArgument ` |
|
Specifies a path segment or URI template as an argument to a verb. For example, |
| `BasePathArgument ` |
|
Specifies the base path in the |
4.3. REST DSL Copy linkLink copied to clipboard!
Apache Camel offers a REST styled DSL.
The intention is to allow end users to define REST services (hosted by Camel) using a REST style with verbs such as get, post, delete, etc.
From Camel 4.6 onwards, the REST DSL has been improved with a contract-first approach using vanilla OpenAPI specification files. This is documented in the REST DSL with OpenAPI contract first page. This current page documents the code-first REST DSL that Camel provides for a long time.
4.3.1. How it works Copy linkLink copied to clipboard!
The REST DSL is a facade that builds Rest endpoints as consumers for Camel routes. The actual REST transport is leveraged by using Camel REST components such as Netty HTTP, Servlet, and others that have native REST integration.
4.3.2. Components supporting REST DSL Copy linkLink copied to clipboard!
We suggest the following Camel components for REST DSL:
- camel-rest required contains the base rest component needed by REST DSL
- camel-platform-http (recommended)
- camel-servlet
Also available is:
4.3.3. REST DSL with Java DSL Copy linkLink copied to clipboard!
To use the REST DSL in Java DSL, then just do as with regular Camel routes by extending the RouteBuilder and define the routes in the configure method.
A simple REST service can be defined as follows, where we use rest() to define the services as shown below:
@Override
public void configure() throws Exception {
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").consumes("application/json").to("direct:bye")
.post("/bye").to("mock:update");
from("direct:hello")
.transform().constant("Hello World");
from("direct:bye")
.transform().constant("Bye World");
}
This defines a REST service with the following url mappings:
| Base Path | Uri template | Verb | Consumes |
|---|---|---|---|
|
|
|
| all |
|
|
|
|
|
|
|
|
| all |
Notice that in the REST service we route directly to a Camel endpoint using to(). This is because the REST DSL has a shorthand for routing directly to an endpoint using to().
4.3.4. REST DSL with XML DSL Copy linkLink copied to clipboard!
The example above can be defined in XML as shown below:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<rest path="/say">
<get path="/hello">
<to uri="direct:hello"/>
</get>
<get path="/bye" consumes="application/json">
<to uri="direct:bye"/>
</get>
<post path="/bye">
<to uri="mock:update"/>
</post>
</rest>
<route>
<from uri="direct:hello"/>
<transform>
<constant>Hello World</constant>
</transform>
</route>
<route>
<from uri="direct:bye"/>
<transform>
<constant>Bye World</constant>
</transform>
</route>
</camelContext>
4.3.5. Using a base path Copy linkLink copied to clipboard!
The REST DSL allows defining a base path to help applying the "don’t repeat yourself" (DRY) practice. For example, to define a customer path, we can set the base path in rest("/customer") and then provide the uri templates in the verbs, as shown below:
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders").to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
And using XML DSL, it becomes:
<rest path="/customers/">
<get path="/{id}">
<to uri="direct:customerDetail"/>
</get>
<get path="/{id}/orders">
<to uri="direct:customerOrders"/>
</get>
<post path="/neworder">
<to uri="direct:customerNewOrder"/>
</post>
</rest>
The REST DSL will take care of duplicate path separators when using base path and uri templates. In the example above the rest base path ends with a slash / and the verb starts with a slash /. Camel will take care of this and remove the duplicated slash.
It is not required to use both base path and uri templates. You can omit the base path and define the base path and uri template in the verbs only. The example above can be defined as:
<rest>
<get path="/customers/{id}">
<to uri="direct:customerDetail"/>
</get>
<get path="/customers/{id}/orders">
<to uri="direct:customerOrders"/>
</get>
<post path="/customers/neworder">
<to uri="direct:customerNewOrder"/>
</post>
</rest>
You can combine path parameters to build complex expressions. For example:
rest("items/")
.get("{id}/{filename}.{content-type}")
.to("direct:item")
4.3.6. Managing Rest services Copy linkLink copied to clipboard!
Each of the rest services becomes a Camel route, so in the first example, we have 2 x get and 1 x post REST service, which each becomes a Camel route. This makes it the same from Apache Camel to manage and run these services, as they are just Camel routes. This means any tooling and API today that deals with Camel routes, also work with the REST services.
To use JMX with Camel then camel-management JAR must be included in the classpath.
This means you can use JMX to stop/start routes, and also get the JMX metrics about the routes, such as the number of messages processed, and their performance statistics.
There is also a Rest Registry JMX MBean that contains a registry of all REST services that has been defined.
4.3.7. Inline REST DSL as a single route Copy linkLink copied to clipboard!
Since Camel 4.5 inline-routes are enabled by default.
Each of the rest services becomes a Camel route, and this means, that if the rest service is calling another Camel route via direct, which is a widespread practice. This means that each rest service then becomes two routes. This can become harder to manage if you have many rest services.
When you use direct endpoints then you can enable REST DSL to automatically inline the direct route in the rest route, meaning that there is only one route per rest service.
When using inline-routes, then each REST endpoint should link 1:1 to a unique direct endpoint. The linked direct routes are inlined and therefore does not exists as independent routes, and they cannot be called from other regular Camel routes. In other words the inlined routes are essentially moved inside the rest-dsl and does not exist as a route.
To do this you MUST use direct endpoints, and each endpoint must be unique name per service. And the option inlineRoutes must be enabled.
For example, in the Java DSL below we have enabled inline routes and each rest service uses direct endpoints with unique names.
restConfiguration().inlineRoutes(true);
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders").to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
And in XML:
<restConfiguration inlineRoutes="true"/>
<rest>
<get path="/customers/{id}">
<to uri="direct:customerDetail"/>
</get>
<get path="/customers/{id}/orders">
<to uri="direct:customerOrders"/>
</get>
<post path="/customers/neworder">
<to uri="direct:customerNewOrder"/>
</post>
</rest>
If you use Camel Main, Camel Spring Boot, Camel Quarkus or Camel JBang, you can also enable this in application.properties such as:
camel.rest.inline-routes = true
The REST services above each use a unique 1:1 linked direct endpoint (direct:customerDetail, direct:customerOrders direct:customerNewOrder). This means that you cannot call these routes from another route such as the following would not function:
from("kafka:new-order")
.to("direct:customerNewOrder");
So if you desire to call common routes from both REST DSL and other regular Camel routes then keep these in separate routes as shown:
restConfiguration().inlineRoutes(true);
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders").to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
from("direct:customerNewOrder")
// do some stuff here
.to("direct:commonCustomerNewOrder"); // call common route
from("direct:commonCustomerNewOrder")
// do stuff here
.log("Created new order");
from("kafka:new-order")
.to("direct:commonCustomerNewOrder"); // make sure to call the common route
The common shared route is separated into the route direct:commonCustomerNewOrder. This route can be called from both REST DSL and regular Camel routes.
4.3.8. Disabling REST services Copy linkLink copied to clipboard!
While developing REST services using REST DSL, you may want to temporary disabled some REST endpoints, which you can do using disabled as shown in the following.
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders").to("direct:customerOrders").disabled("{{ordersEnabled}}")
.post("/neworder").to("direct:customerNewOrder").disabled();
And in XML:
<rest>
<get path="/customers/{id}">
<to uri="direct:customerDetail"/>
</get>
<get path="/customers/{id}/orders" disabled="{{ordersEnabled}}">
<to uri="direct:customerOrders"/>
</get>
<post path="/customers/neworder" disabled="true">
<to uri="direct:customerNewOrder"/>
</post>
</rest>
In this example the last two REST endpoints are configured with disabled. You can use Property Placeholder to let an external configuration determine if the REST endpoint is disabled or not. In this example the /customers/{id}/orders endpoint is disabled via a placeholder. The last REST endpoint is hardcoded to be disabled.
4.3.9. Binding to POJOs using Copy linkLink copied to clipboard!
The REST DSL supports automatic binding json/xml contents to/from POJOs using data formats. By default, the binding mode is off, meaning there is no automatic binding happening for incoming and outgoing messages.
You may want to use binding if you develop POJOs that maps to your REST services request and response types. This allows you as a developer to work with the POJOs in Java code.
The binding modes are:
| Binding Mode | Description |
|---|---|
|
| Binding is turned off. This is the default option. |
|
|
Binding is enabled, and Camel is relaxed and supports JSON, XML or both if the necessary data formats are included in the classpath. Notice that if for example |
|
|
Binding to/from JSON is enabled, and requires a JSON capable data format on the classpath. By default, Camel will use |
|
|
Binding to/from XML is enabled, and requires |
|
| Binding to/from JSON and XML is enabled and requires both data formats to be on the classpath. |
When using camel-jaxb for XML bindings, then you can use the option mustBeJAXBElement to relax the output message body must be a class with JAXB annotations. You can use this in situations where the message body is already in XML format, and you want to use the message body as-is as the output type. If that is the case, then set the dataFormatProperty option mustBeJAXBElement to false value.
The binding from POJO to JSon/JAXB will only happen if the content-type header includes the word json or xml representatively. This allows you to specify a custom content-type if the message body should not attempt to be marshalled using the binding. For example, if the message body is a custom binary payload, etc.
When automatic binding from POJO to JSON/JAXB takes place the existing content-type header will by default be replaced with either application/json or application/xml. To disable the default behavior and be able to produce JSON/JAXB responses with custom content-type headers (e.g. application/user.v2+json) you configure this in Java DSL as shown below:
restConfiguration().dataFormatProperty("contentTypeHeader", "false");
To use binding you must include the necessary data formats on the classpath, such as camel-jaxb and/or camel-jackson. And then enable the binding mode. You can configure the binding mode globally on the rest configuration, and then override per rest service as well.
To enable binding, you configure this in Java DSL as shown below:
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.auto);
And in XML DSL:
<restConfiguration bindingMode="auto" component="platform-http" port="8080"/>
When binding is enabled, Camel will bind the incoming and outgoing messages automatic, accordingly to the content type of the message. If the message is JSON, then JSON binding happens; and so if the message is XML, then XML binding happens. The binding happens for incoming and reply messages. The table below summaries what binding occurs for incoming and reply messages.
| Message Body | Direction | Binding Mode | Message Body |
|---|---|---|---|
| XML | Incoming | auto,xml,json_xml | POJO |
| POJO | Outgoing | auto,xml, json_xml | XML |
| JSON | Incoming | auto,json,json_xml | POJO |
| POJO | Outgoing | auto,json,json_xml | JSON |
When using binding, you must also configure what POJO type to map to. This is mandatory for incoming messages, and optional for outgoing.
When using binding mode json, xml or json_xml then Camel will automatically set consumers and produces on the rest endpoint (according to the mode), if not already explicit configured. For example, with binding mode json and setting the outType as UserPojo then Camel will define this rest endpoint as producing application/json.
For example, to map from xml/json to a pojo class UserPojo you do this in Java DSL as shown below:
// configure to use platformhttp on localhost with the given port
// and enable auto binding mode
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.auto);
// use the rest DSL to define the rest services
rest("/users/")
.post().type(UserPojo.class)
.to("direct:newUser");
Notice we use type to define the incoming type. We can optionally define an outgoing type (which can be a good idea, to make it known from the DSL and also for tooling and JMX APIs to know both the incoming and outgoing types of the REST services). To define the outgoing type, we use outType as shown below:
// configure to use platformhttp on localhost with the given port
// and enable auto binding mode
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.auto);
// use the rest DSL to define the rest services
rest("/users/")
.post().type(UserPojo.class).outType(CountryPojo.class)
.to("direct:newUser");
And in XML DSL:
<rest path="/users/">
<post type="UserPojo" outType="CountryPojo">
<to uri="direct:newUser"/>
</post>
</rest>
To specify input and/or output using an array, append [] to the end of the canonical class name as shown in the following Java DSL:
// configure to use platformhttp on localhost with the given port
// and enable auto binding mode
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.auto);
// use the rest DSL to define the rest services
rest("/users/")
.post().type(UserPojo[].class).outType(CountryPojo[].class)
.to("direct:newUser");
The UserPojo is just a plain pojo with getter/setter as shown:
public class UserPojo {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The UserPojo only supports JSON, as XML requires using JAXB annotations, so we can add those annotations if we want to support XML also
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserPojo {
@XmlAttribute
private int id;
@XmlAttribute
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
By having the JAXB annotations, the POJO supports both JSON and XML bindings.
4.3.9.1. Camel Rest-DSL configurations Copy linkLink copied to clipboard!
The REST DSL supports the following options:
| Name | Description | Default | Type |
|---|---|---|---|
| apiComponent | Sets the name of the Camel component to use as the REST API (such as swagger or openapi) | String | |
| apiContextPath | Sets a leading API context-path the REST API services will be using. This can be used when using components such as camel-servlet where the deployed web application is deployed using a context-path. | String | |
| apiHost | To use a specific hostname for the API documentation (such as swagger or openapi) This can be used to override the generated host with this configured hostname | String | |
| apiProperties | Sets additional options on api level | Map | |
| apiVendorExtension |
Whether a vendor extension is enabled in the Rest APIs. If enabled, then Camel will include additional information as a vendor extension (e.g., keys starting with | false | boolean |
| bindingMode | Sets the binding mode to be used by the REST consumer | RestBindingMode.off | RestBindingMode |
| clientRequestValidation | Whether to enable validation of the client request to check: 1) Content-Type header matches what the REST DSL consumes; returns HTTP Status 415 if validation error. 2) Accept header matches what the REST DSL produces; returns HTTP Status 406 if validation error. 3) Missing required data (query parameters, HTTP headers, body); returns HTTP Status 400 if validation error. 4) Parsing error of the message body (JSON, XML or Auto binding mode must be enabled); returns HTTP Status 400 if validation error. | false | boolean |
| clientResponseValidation | Whether to check what Camel is returning as response to the client: 1) Status-code and Content-Type matches REST DSL response messages. 2) Check whether expected headers is included according to the REST DSL repose message headers. 3) If the response body is JSon then check whether its valid JSon. Returns 500 if validation error detected. | false | boolean |
| component | Sets the name of the Camel component to use as the REST consumer | String | |
| componentProperties | Sets additional options on component level | Map | |
| consumerProperties | Sets additional options on consumer level | Map | |
| contextPath | Sets a leading context-path the REST services will be using. This can be used when using components such as camel-servlet where the deployed web application is deployed using a context-path. Or for components such that include an HTTP server. | String | |
| corsHeaders | Sets the CORS headers to use if CORS has been enabled. | Map | |
| dataFormatProperties | Sets additional options on data format level | Map | |
| enableCORS | To specify whether to enable CORS, which means Camel will automatically include CORS in the HTTP headers in the response. This option is default false | false | boolean |
| enableNoContentResponse | To specify whether to return HTTP 204 with an empty body when a response contains an empty JSON object or XML root object. | false | boolean |
| endpointProperties | Sets additional options on endpoint level | Map | |
| host | Sets the hostname to use by the REST consumer | String | |
| hostNameResolver | Sets the resolver to use for resolving hostname | RestHostNameResolver.allLocalIp | RestHostNameResolver |
| inlineRoutes | Inline routes in rest-dsl which are linked using direct endpoints. By default, each service in REST DSL is an individual route, meaning that you would have at least two routes per service (rest-dsl, and the route linked from rest-dsl). Enabling this allows Camel to optimize and inline this as a single route. However, this requires using direct endpoints, which must be unique per service. This option is default false. | false | boolean |
| jsonDataFormat | Sets a custom JSON data format to be used Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance. | String | |
| port | Sets the port to use by the REST consumer | int | |
| producerApiDoc | Sets the location of the api document (swagger api) the REST producer will use to validate the REST uri and query parameters are valid accordingly to the api document. This requires adding camel-openapi-java to the classpath, and any miss configuration will let Camel fail on startup and report the error(s). The location of the api document is loaded from classpath by default, but you can use file: or http: to refer to resources to load from file or http url. | String | |
| producerComponent | Sets the name of the Camel component to use as the REST producer | String | |
| scheme | Sets the scheme to use by the REST consumer | String | |
| skipBindingOnErrorCode | Whether to skip binding output if there is a custom HTTP error code, and instead use the response body as-is. This option is default true. | true | boolean |
| useXForwardHeaders | Whether to use X-Forward headers to set host etc. for Swagger. This option is default true. | true | boolean |
| xmlDataFormat | Sets a custom XML data format to be used. Important: This option is only for setting a custom name of the data format, not to refer to an existing data format instance. | String |
For example, to configure to use the Platform HTTP component on port 9091, then we can do as follows:
restConfiguration().component("platform-http").port(9091).componentProperty("foo", "123");
And with XML DSL:
<restConfiguration component="platform-http" port="9091">
<componentProperty key="foo" value="123"/>
</restConfiguration>
If no component has been explicitly configured, then Camel will look up if there is a Camel component that integrates with the REST DSL, or if a org.apache.camel.spi.RestConsumerFactory is registered in the registry. If either one is found, then that is being used.
You can configure properties on these levels.
- component - Is used to set any options on the Component class. You can also configure these directly on the component.
- endpoint - Is used set any option on the endpoint level. Many of the Camel components has many options you can set on endpoint level.
- consumer - Is used to set any option on the consumer level.
- data format - Is used to set any option on the data formats. For example, to enable pretty print in the JSON data format.
- cors headers - If cors is enabled, then custom CORS headers can be set. See below for the default values which are in used. If a custom header is set then that value takes precedence over the default value.
You can set multiple options of the same level, so you can, for example, configure two component options, and three endpoint options, etc.
4.3.10. Enabling or disabling Jackson JSON features Copy linkLink copied to clipboard!
When using JSON binding, you may want to turn specific Jackson features on or off. For example, to disable failing on unknown properties (e.g., JSON input has a property which cannot be mapped to a POJO) then configure this using the dataFormatProperty as shown below:
restConfiguration().component("platform-http").host("localhost").port(getPort()).bindingMode(RestBindingMode.json)
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES");
You can disable more features by separating the values using comma, such as:
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE");
Likewise, you can enable features using the enableFeatures such as:
restConfiguration().component("platform-http").host("localhost").port(getPort()).bindingMode(RestBindingMode.json)
.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
.dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");
The values that can be used for enabling and disabling features on Jackson are the names of the enums from the following three Jackson classes
-
com.fasterxml.jackson.databind.SerializationFeature -
com.fasterxml.jackson.databind.DeserializationFeature -
com.fasterxml.jackson.databind.MapperFeature
The rest configuration is, of course, also possible using XML DSL:
<restConfiguration component="platform-http" host="localhost" port="9090" bindingMode="json">
<dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"/>
<dataFormatProperty key="json.in.enableFeatures" value="FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS"/>
</restConfiguration>
4.3.11. Default CORS headers Copy linkLink copied to clipboard!
If CORS is enabled, then the "follow headers" is in use by default. You can configure custom CORS headers that take precedence over the default value.
| Key | Value |
|---|---|
|
| * |
|
| GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH |
|
| Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers |
|
| 3600 |
4.3.12. Defining a custom error message as-is Copy linkLink copied to clipboard!
If you want to define custom error messages to be sent back to the client with a HTTP error code (e.g., such as 400, 404 etc.) then you set a header with the key Exchange.HTTP_RESPONSE_CODE to the error code (must be 300+) such as 404. And then the message body with any reply message, and optionally set the content-type header as well. There is a little example shown below:
restConfiguration().component("platform-http").host("localhost").port(portNum).bindingMode(RestBindingMode.json);
// use the rest DSL to define the rest services
rest("/users/")
.post("lives").type(UserPojo.class).outType(CountryPojo.class)
.to("direct:users-lives");
from("direct:users-lives")
.choice()
.when().simple("${body.id} < 100")
.bean(new UserErrorService(), "idToLowError")
.otherwise()
.bean(new UserService(), "livesWhere");
In this example, if the input id is a number that is below 100, we want to send back a custom error message, using the UserErrorService bean, which is implemented as shown:
public class UserErrorService {
public void idToLowError(Exchange exchange) {
exchange.getMessage().setBody("id value is too low");
exchange.getMessage().setHeader(Exchange.CONTENT_TYPE, "text/plain");
exchange.getMessage().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
}
In the UserErrorService bean, we build our custom error message, and set the HTTP error code to 400. This is important, as that tells rest-dsl that this is a custom error message, and the message should not use the output pojo binding (e.g., would otherwise bind to CountryPojo).
4.3.12.1. Catching JsonParserException and returning a custom error message Copy linkLink copied to clipboard!
You can return a custom message as-is (see previous section). So we can leverage this with Camel error handler to catch JsonParserException, handle that exception and build our custom response message. For example, to return a HTTP error code 400 with a hardcoded message, we can do as shown below:
onException(JsonParseException.class)
.handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
.setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
.setBody().constant("Invalid json data");
4.3.13. Query/Header Parameter default Values Copy linkLink copied to clipboard!
You can specify default values for parameters in the rest-dsl, such as the verbose parameter below:
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders")
.param().name("verbose").type(RestParamType.query).defaultValue("false").description("Verbose order details").endParam()
.to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
The default value is automatic set as header on the incoming Camel Message. So if the call to /customers/id/orders do not include a query parameter with key verbose then Camel will now include a header with key verbose and the value false because it was declared as the default value. This functionality is only applicable for query parameters. Request headers may also be defaulted in the same way.
rest("/customers/")
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders")
.param().name("indicator").type(RestParamType.header).defaultValue("disabled").description("Feature Enabled Indicator").endParam()
.to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
4.3.14. Client Request and Response Validation Copy linkLink copied to clipboard!
It is possible to enable validation of the incoming client request. The validation checks for the following:
- Content-Type header matches what the REST DSL consumes. (Returns HTTP Status 415)
- Accept header matches what the REST DSL produces. (Returns HTTP Status 406)
- Missing required data (query parameters, HTTP headers, body). (Returns HTTP Status 400)
- Checking if query parameters or HTTP headers has not-allowed values. (Returns HTTP Status 400)
- Parsing error of the message body (JSON, XML or Auto binding mode must be enabled). (Returns HTTP Status 400)
If the validation fails, then REST DSL will return a response with an HTTP error code.
The validation is by default turned off (to be backwards compatible). It can be turned on via clientRequestValidation as shown below:
restConfiguration().component("platform-http").host("localhost")
.clientRequestValidation(true);
The validator is pluggable and Camel provides a default implementation out of the box. However, the camel-openapi-validator uses the third party Atlassian Swagger Request Validator library instead for client request validator. This library is a more extensive validator than the default validator from camel-core, such as being able to validate the payload is structured according to the OpenAPI specification.
In Camel 4.13 we added a response validator as well which is intended more as development assistance that you can enable while building your Camel integrations, and help ensure what Camel is sending back to the HTTP client is valid. The response validator checks for the following:
- Status-code and Content-Type matches REST DSL response messages.
- Check whether expected headers is included according to the REST DSL repose message headers.
- If the response body is JSon then check whether its valid JSon.
If any error is detected the HTTP Status 500 is returned.
Also, the camel-openapi-validator can be added to the classpath to have a more powerful response validator, that can be used to validate the response payload is structured according to the OpenAPI specification.
4.3.15. OpenAPI / Swagger API Copy linkLink copied to clipboard!
The REST DSL supports OpenAPI and Swagger by the camel-openapi-java modules.
You can define each parameter fine-grained with details such as name, description, data type, parameter type and so on, using the param. For example, to define the id path parameter, you can do as shown below:
<!-- this is a rest GET to view an user by the given id -->
<get path="/{id}" outType="org.apache.camel.example.rest.User">
<description>Find user by id</description>
<param name="id" type="path" description="The id of the user to get" dataType="int"/>
<to uri="bean:userService?method=getUser(${header.id})"/>
</get>
And in Java DSL
.get("/{id}").description("Find user by id").outType(User.class)
.param().name("id").type(path).description("The id of the user to get").dataType("int").endParam()
.to("bean:userService?method=getUser(${header.id})")
The body parameter type requires to use body as well for the name. For example, a REST PUT operation to create/update an user could be done as:
<!-- this is a rest PUT to create/update an user -->
<put type="org.apache.camel.example.rest.User">
<description>Updates or create a user</description>
<param name="body" type="body" description="The user to update or create"/>
<to uri="bean:userService?method=updateUser"/>
</put>
And in Java DSL:
.put().description("Updates or create a user").type(User.class)
.param().name("body").type(body).description("The user to update or create").endParam()
.to("bean:userService?method=updateUser")
4.3.15.1. Vendor Extensions Copy linkLink copied to clipboard!
The generated API documentation can be configured to include vendor extensions (https://swagger.io/specification/#specificationExtensions) which document the operations and definitions with additional information, such as class name of model classes, camel context id and route id’s. This information can be very helpful for developers, especially during troubleshooting. However, at production usage you may wish to not have this turned on to avoid leaking implementation details into your API docs.
The vendor extension information is stored in the API documentation with keys starting with x-.
Not all third party API gateways and tools support vendor-extensions when importing your API docs.
The vendor extensions can be turned on RestConfiguration via the apiVendorExtension option:
restConfiguration()
.component("servlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true")
.apiContextPath("api-doc")
.apiVendorExtension(true)
.apiProperty("api.title", "User API").apiProperty("api.version", "1.0.0")
.apiProperty("cors", "true");
And in XML DSL:
<restConfiguration component="servlet" bindingMode="json"
apiContextPath="api-docs"
apiVendorExtension="true">
<!-- we want json output in pretty mode -->
<dataFormatProperty key="prettyPrint" value="true"/>
<!-- setup swagger api descriptions -->
<apiProperty key="api.version" value="1.0.0"/>
<apiProperty key="api.title" value="User API"/>
</restConfiguration>
4.3.15.2. Supported API properties Copy linkLink copied to clipboard!
The following table lists supported API properties and explains their effect. To set them use apiProperty(String, String) in the Java DSL or <apiProperty> when defining the REST API via XML configuration. Properties in bold are required by the OpenAPI 2.0 specification. Most of the properties affect the OpenAPI Info object, License object or Contact object.
| Property | Description |
| api.version | Version of the API |
| api.title | Title of the API |
| api.description | Description of the API |
| api.termsOfService | API Terms of Service of the API |
| api.license.name | License information of the API |
| api.license.url | URL for the License of the API |
| api.contact.name | The identifying name of the contact person/organization |
| api.contact.url | The URL pointing to the contact information |
| api.contact.email | The email address of the contact person/organization |
| api.specification.contentType.json |
The Content-Type of the served OpenAPI JSON specification, |
| api.specification.contentType.yaml |
The Content-Type of the served OpenAPI YAML specification, |
| externalDocs.url | The URI for the target documentation. This must be in the form of a URI |
| externalDocs.description | A description of the target documentation |
4.4. Marshalling to and from Java Objects Copy linkLink copied to clipboard!
4.4.1. Marshalling Java objects for transmission over HTTP Copy linkLink copied to clipboard!
One of the most common ways to use the REST protocol is to transmit the contents of a Java bean in the message body. In order for this to work, you need to have a mechanism for marshalling the Java object to and from a suitable data format. The following data formats, which are suitable for encoding Java objects, are supported by the REST DSL:
- JSON
JSON (JavaScript object notation) is a lightweight data format that can easily be mapped to and from Java objects. The JSON syntax is compact, lightly typed, and easy for humans to read and write. For all of these reasons, JSON has become popular as a message format for REST services.
For example, the following JSON code could represent a
Userbean with two property fields,idandname:{ "id" : 1234, "name" : "Jane Doe" }- JAXB
JAXB (Java Architecture for XML Binding) is an XML-based data format that can easily be mapped to and from Java objects. In order to marshal the XML to a Java object, you must also annotate the Java class that you want to use.
For example, the following JAXB code could represent a
Userbean with two property fields,idandname:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <User> <Id>1234</Id> <Name>Jane Doe</Name> </User>NoteJAXB data format and type converter supports the conversion from XML to POJO for classes, that use
ObjectFactoryinstead ofXmlRootElement. Also, the camel context should include theCamelJaxbObjectFactoryproperty with value true. However, due to optimization the default value is false.
4.4.2. Integration of JSON and JAXB with the REST DSL Copy linkLink copied to clipboard!
You could, of course, write the required code to convert the message body to and from a Java object yourself. But the REST DSL offers the convenience of performing this conversion automatically. In particular, the integration of JSON and JAXB with the REST DSL offers the following advantages:
- Marshalling to and from Java objects is performed automatically (given the appropriate configuration).
- The REST DSL can automatically detect the data format (either JSON or JAXB) and perform the appropriate conversion.
- The REST DSL provides an abstraction layer, so that the code you write is not specific to a particular JSON or JAXB implementation. So you can switch the implementation later on, with minimum impact to your application code.
4.4.2.1. Supported data format components Copy linkLink copied to clipboard!
Apache Camel provides a number of different implementations of the JSON and JAXB data formats. The following data formats are currently supported by the REST DSL:
JSON
-
Jackson data format (
camel-jackson) (default) -
GSon data format (
camel-gson) -
XStream data format (
camel-xstream)
-
Jackson data format (
JAXB
-
JAXB data format (
camel-jaxb)
-
JAXB data format (
4.4.3. How to enable object marshalling Copy linkLink copied to clipboard!
To enable object marshalling in the REST DSL, observe the following points:
-
Enable binding mode, by setting the
bindingModeoption (there are several levels at which you can set the binding mode. -
Specify the Java type to convert to (or from), on the incoming message with the
typeoption (required), and on the outgoing message with theoutTypeoption (optional). - If you want to convert your Java object to and from the JAXB data format, you must remember to annotate the Java class with the appropriate JAXB annotations.
-
Specify the underlying data format implementation (or implementations), using the
jsonDataFormatoption and/or thexmlDataFormatoption (which can be specified on therestConfigurationbuilder). If your route provides a return value in JAXB format, you are normally expected to set the Out message of the exchange body to be an instance of a class with JAXB annotations (a JAXB element). If you prefer to provide the JAXB return value directly in XML format, however, set the
dataFormatPropertywith the key,xml.out.mustBeJAXBElement, tofalse(which can be specified on therestConfigurationbuilder). For example, in the XML DSL syntax:<restConfiguration ...> <dataFormatProperty key="xml.out.mustBeJAXBElement" value="false"/> ... </restConfiguration>Add the required dependencies to your project build file. For example, if you are using the Maven build system and you are using the Jackson data format, you would add the following dependency to your Maven POM file:
<project ...> ... <dependencies> ... *<!-- use for json binding --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> </dependency>* ... </dependencies> </project>
4.4.4. Configuring the binding mode Copy linkLink copied to clipboard!
The bindingMode option is off by default, so you must configure it explicitly, in order to enable marshalling of Java objects. TABLE shows the list of supported binding modes.
The binding from POJO to JSon/JAXB will only happen if the content-type header includes json or xml. This allows you to specify a custom content-type if the message body should not attempt to be marshalled using the binding. This is useful if, for example, the message body is a custom binary payload.
| Binding Mode | Description |
|---|---|
|
| Binding is turned off (default). |
|
| Binding is enabled for JSON and/or XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are not required to enable both kinds of data format, however: either a JSON implementation, an XML implementation, or both can be provided on the classpath. |
|
|
Binding is enabled for JSON only. A JSON implementation must be provided on the classpath (by default, Camel tries to enable the |
|
|
Binding is enabled for XML only. An XML implementation must be provided on the classpath (by default, Camel tries to enable the |
|
| Binding is enabled for both JSON and XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are required to provide both kinds of data format on the classpath. |
In Java, these binding mode values are represented as instances of the following enum type:
org.apache.camel.model.rest.RestBindingMode
There are several different levels at which you can set the bindingMode:
- REST DSL configuration
You can set the
bindingModeoption from therestConfigurationbuilder, as follows:restConfiguration().component("servlet").port(8181).bindingMode(RestBindingMode.json);- Service definition base part
You can set the
bindingModeoption immediately following therest()keyword (before the verb clauses), as follows:rest("/user").*bindingMode(RestBindingMode.json)*.get("/{id}")._VerbClause_- Verb clause
You can set the
bindingModeoption in a verb clause:rest("/user") .get("/{id}").*bindingMode(RestBindingMode.json)*.to("...");
4.4.5. Java type for responses Copy linkLink copied to clipboard!
The example application passes User type objects back and forth in HTTP Request and Response messages. The User Java class is defined as shown below.
User Class for JSON Response
package org.apache.camel.example.rest;
public class User {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The User class has a relatively simple representation in the JSON data format. For example, a typical instance of this class expressed in JSON format is:
{
"id" : 1234,
"name" : "Jane Doe"
}
4.4.6. REST DSL route with JSON binding Copy linkLink copied to clipboard!
The REST DSL configuration and the REST service definition for this example are shown below.
REST DSL Route with JSON Binding
<camel>
<bean name="userService" type="org.apache.camel.example.rest.UserService">
</bean>
<rest description="User rest service" path="/user" produces="application/json" consumes="application/json">
<get description="Find user by id" outType="org.apache.camel.example.rest.User" path="/{id}">
<param name="id"/>
<to uri="bean:userService?method=getUser(${header.id})"/>
</get>
<put description="Updates or create a user" type="org.apache.camel.example.rest.User">
<param dataType="org.apache.camel.example.rest.User" name="body" type="body"/>
<to uri="bean:userService?method=updateUser"/>
</put>
<get description="Find all users" outType="org.apache.camel.example.rest.User[]" path="/findAll">
<to uri="bean:userService?method=listUsers"/>
</get>
</rest>
</camel>
4.4.7. REST operations Copy linkLink copied to clipboard!
The REST service from the above example defines the following REST operations:
GET /camel-example-servlet-rest-blueprint/rest/user/{id}-
Get the details for the user identified by
{id}, where the HTTP response is returned in JSON format. PUT /camel-example-servlet-rest-blueprint/rest/user-
Create a new user, where the user details are contained in the body of the PUT message, encoded in JSON format (to match the
Userobject type). GET /camel-example-servlet-rest-blueprint/rest/user/findAll- Get the details for all users, where the HTTP response is returned as an array of users, in JSON format.
4.4.8. URLs to invoke the REST service Copy linkLink copied to clipboard!
By inspecting the REST DSL definitions, you can piece together the URLs required to invoke each of the REST operations. For example, to invoke the first REST operation, which returns details of a user with a given ID, the URL is built up as follows:
http://localhost:8181-
In
restConfiguration, the protocol defaults tohttpand the port is set explicitly to8181. /camel-example-servlet-rest/rest-
Specified by the
contextPathattribute of therestConfigurationelement. /user-
Specified by the
pathattribute of therestelement. /{id}-
Specified by the
uriattribute of thegetverb element.
Hence, it is possible to invoke this REST operation with the curl utility, by entering the following command at the command line:
curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/123
Similarly, the remaining REST operations could be invoked with curl, by entering the following sample commands:
curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/findAll
curl -X PUT -d "{ \"id\": 666, \"name\": \"The devil\"}" -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user