Repository: camel Updated Branches: refs/heads/master e9f8c6cdf -> 3f617a168
CAMEL-11786: move rest-dsl out of rest-api component docs Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/3f617a16 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/3f617a16 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/3f617a16 Branch: refs/heads/master Commit: 3f617a1689d3781d55d60ac71c5648079a202734 Parents: e9f8c6c Author: Claus Ibsen <davscl...@apache.org> Authored: Tue Sep 19 15:00:27 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Tue Sep 19 15:00:27 2017 +0200 ---------------------------------------------------------------------- .../src/main/docs/rest-api-component.adoc | 715 +------------------ camel-core/src/main/docs/rest-dsl.adoc | 714 ++++++++++++++++++ 2 files changed, 717 insertions(+), 712 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/3f617a16/camel-core/src/main/docs/rest-api-component.adoc ---------------------------------------------------------------------- diff --git a/camel-core/src/main/docs/rest-api-component.adoc b/camel-core/src/main/docs/rest-api-component.adoc index 479cddf..6dc44dd 100644 --- a/camel-core/src/main/docs/rest-api-component.adoc +++ b/camel-core/src/main/docs/rest-api-component.adoc @@ -2,477 +2,9 @@ *Available as of Camel version 2.16* -Apache Camel offers a REST styled DSL which can be used with Java or -XML. The intention is to allow end users to define REST services using a -REST style with verbs such as get, post, delete etc. +The rest-api component is used for providing Swagger API of the REST services which has been defined using the rest-dsl in Camel. -=== How it works - -The Rest DSL is a facade that builds link:rest.html[Rest] endpoints as -consumers for Camel routes. The actual REST transport is leveraged by -using Camel REST components such -as link:restlet.html[Restlet], link:spark-rest.html[Spark-rest], and -others that has native REST integration. - -=== Components supporting Rest DSL - -The following Camel components supports the Rest DSL. See the bottom of -this page for how to integrate a component with the Rest DSL. - -* camel-coap -* link:netty-http.html[camel-netty-http] (also -supports link:swagger-java.html[Swagger Java]) -* link:netty4-http.html[camel-netty4-http] (also -supports link:swagger-java.html[Swagger Java]) -* link:jetty.html[camel-jetty] (also -supports link:swagger-java.html[Swagger Java]) -* link:restlet.html[camel-restlet] (also -supports link:swagger-java.html[Swagger Java]) -* link:servlet.html[camel-servlet] (also -supports link:swagger-java.html[Swagger Java]) -* link:spark-rest.html[camel-spark-rest] (also -supports link:swagger-java.html[Swagger Java] from Camel 2.17 onwards) -* link:undertow.html[camel-undertow] (also -supports link:swagger-java.html[Swagger Java] from Camel 2.17 onwards) - -=== Rest DSL with Java - -To use the Rest DSL in Java 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 define as follows, where we use rest() to -define the services as shown below: - -[source,java] ----- -protected RouteBuilder createRouteBuilder() throws Exception { - return new RouteBuilder() { - @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: - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|Base Path |Uri template |Verb |Consumes - -|/say |/hello |get |_all_ - -|/say |/bye |get |application/json - -|/say |/bye |post |_all_ -|=== - -Notice that in the REST service we route directly to a Camel endpoint -using the to(). This is because the Rest DSL has a short-hand for -routing directly to an endpoint using to(). An alternative is to embed a -Camel route directly using route() - there is such an example further -below. - -=== Rest DSL with XML - -The REST DSL supports the XML DSL also using either Spring or Blueprint. -The example above can be define in XML as shown below: - -[source,xml] ----- -<camelContext xmlns="http://camel.apache.org/schema/spring"> - <rest path="/say"> - <get uri="/hello"> - <to uri="direct:hello"/> - </get> - <get uri="/bye" consumes="application/json"> - <to uri="direct:bye"/> - </get> - <post uri="/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> ----- - -=== Using base path - -The REST DSL allows to define base path to make the DSL a bit more DRY. -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: - -[source,java] ----- -rest("/customers/") - .get("/{id}").to("direct:customerDetail") - .get("/{id}/orders").to("direct:customerOrders") - .post("/neworder").to("direct:customerNewOrder"); ----- - -And using XML DSL it becomes: - -[source,xml] ----- -<rest path="/customers/"> - <get uri="/{id}"> - <to uri="direct:customerDetail"/> - </get> - <get uri="/{id}/orders"> - <to uri="direct:customerOrders"/> - </get> - <post uri="/neworder"> - <to uri="direct:customerNewOrder"/> - </post> -</rest> ----- - -TIP: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 ( / ). But Apache -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 bast path and define the base path and uri template in the verbs -only. The example above can be defined as: - -[source,xml] ----- -<rest> - <get uri="/customers/{id}"> - <to uri="direct:customerDetail"/> - </get> - <get uri="/customers/{id}/orders"> - <to uri="direct:customerOrders"/> - </get> - <post uri="/customers/neworder"> - <to uri="direct:customerNewOrder"/> - </post> -</rest> ----- - -=== Using Dynamic To in Rest DSL - -*Available as of Camel 2.16* - -The link:rest-dsl.html[Rest DSL] supports the new .toD <toD> as dynamic -to in the rest-dsl. For example to do a request/reply -over link:jms.html[JMS] where the queue name is dynamic defined - -[source,java] ----- - public void configure() throws Exception { - rest("/say") - .get("/hello/{language}").toD("jms:queue:hello-${header.language}"); -} ----- - -=== And in XML DSL - -[source,xml] ----- -<rest uri="/say"> - <get uri="/hello//{language}"> - <toD uri="jms:queue:hello-${header.language}"/> - </get> -<rest> ----- - -See more details at link:message-endpoint.html[Message Endpoint] about -the dynamic to, and what syntax it supports. By default it uses -the link:simple.html[Simple] language, but it has more power than so. - -=== Embedding Camel routes - -Each of the rest service becomes a Camel route, so in the first example -we have 2 x get and 1 x post REST service, which each become a Camel -route. And we have 2 regular Camel routes, meaning we have 3 + 2 = 5 -routes in total. - -There are two route modes with the Rest DSL - -* mini using a singular to -* embedding a Camel route using route - -The first example is using the former with a singular to. And that is -why we end up with 3 + 2 = 5 total routes. - -The same example could use embedded Camel routes, which is shown below: - -[source,java] ----- -protected RouteBuilder createRouteBuilder() throws Exception { - return new RouteBuilder() { - @Override - public void configure() throws Exception { - rest("/say/hello") - .get().route().transform().constant("Hello World"); - rest("/say/bye") - .get().consumes("application/json").route().transform().constant("Bye World").endRest() - .post().to("mock:update"); - }; -} ----- - -In the example above, we are embedding routes directly in the rest -service using .route(). Notice we need to use .endRest() to tell Camel -where the route ends, so we can _go back_ to the Rest DSL and continue -defining REST services. - -#### Configuring route options - -In the embedded route you can configure the route settings such as -routeId, autoStartup and various other options you can set on routes -today. - -[source,java] ----- -.get() - .route().routeId("myRestRoute").autoStartup(false) - .transform().constant("Hello World"); ----- - - -=== Managing Rest services - -Each of the rest service becomes a Camel route, so in the first example -we have 2 x get and 1 x post REST service, which each become a Camel -route. This makes it _the same_ from 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. - -This means you can use JMX to stop/start routes, and also get the JMX -metrics about the routes, such as number of message processed, and their -performance statistics. - -There is also a Rest Registry JMX MBean that contains a registry of all -REST services which has been defined. - -=== Binding to POJOs using - -The Rest DSL supports automatic binding json/xml contents to/from POJOs -using Camels link:data-format.html[Data Format]. 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: - -[width="100%",cols="10%,90%",options="header",] -|=== -|Binding Mode |Description - -|off |Binding is turned off. This is the default option. - -|auto |Binding is enabled and Camel is relaxed and support json, xml or both if -the needed data formats are included in the classpath. Notice that if -for example `camel-jaxb` is not on the classpath, then XML binding is -not enabled. - -|json |Binding to/from json is enabled, and requires a json capabile data -format on the classpath. By default Camel will use `json-jackson` as the -data format. See the INFO box below for more details. - -|xml |Binding to/from xml is enabled, and requires `camel-jaxb` on the -classpath. See the INFO box below for more details. - -|json_xml |Binding to/from json and xml is enabled and requires both data formats to -be on the classpath. See the INFO box below for more details. -|=== - -TIP:From *Camel 2.14.1* onwards 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. - -INFO:From *Camel 2.16.3* onwards 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. - -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 - -[source,java] ----- -restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.auto); ----- - -And in XML DSL - -[source,xml] ----- -<restConfiguration bindingMode="auto" component="restlet" 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. - -[width="100%",cols="25%,25%,25%,25%",options="header",] -|=== -|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. - -For example to map from xml/json to a pojo class `UserPojo` you do this -in Java DSL as shown below: - -[source,java] ----- -// configure to use restlet on localhost with the given port -// and enable auto binding mode -restConfiguration().component("restlet").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: - -[source,java] ----- -// configure to use restlet on localhost with the given port -// and enable auto binding mode -restConfiguration().component("restlet").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"); ----- - -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: - -[source,java] ----- -// configure to use restlet on localhost with the given port -// and enable auto binding mode -restConfiguration().component("restlet").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: - -[source,java] ----- -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 to use JAXB -annotations, so we can add those annotations if we want to support XML -also - -[source,java] ----- -@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. - -=== Configuring Rest DSL +=== URI Options // component options: START @@ -514,248 +46,7 @@ with the following path and query parameters: // endpoint options: END -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. Some -components has consumer options, which you can also configure from -endpoint level by prefixing the option with "consumer." -* 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 can for -example configure 2 component options, and 3 endpoint options etc. - - - -=== Enabling or disabling Jackson JSON features - -*Available as of Camel 2.15* - -When using JSON binding you may want to turn specific Jackson features -on or off. For example to disable failing on unknown properties (eg json -input has a property which cannot be mapped to a POJO) then configure -this using the dataFormatProperty as shown below: - -[source,java] ----- -restConfiguration().component("jetty").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: - -[source,java] ----- -.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"); ----- - -Likewise you can enable features using the enableFeatures such as: - -[source,java] ----- -restConfiguration().component("jetty").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 - -[source,xml] ----- -<restConfiguration component="jetty" 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> ----- - -=== Default CORS headers - -*Available as of Camel 2.14.1* - -If CORS is enabled then the follow headers is in use by default. You can -configure custom CORS headers which takes precedence over the default -value. - -[width="100%",cols="50%,50%",options="header",] -|=== -|Key |Value - -|Access-Control-Allow-Origin |* - -|Access-Control-Allow-Methods |GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH - -|Access-Control-Allow-Headers |Origin, Accept, X-Requested-With, Content-Type, -Access-Control-Request-Method, Access-Control-Request-Headers - -|Access-Control-Max-Age |3600 -|=== - -=== Defining a custom error message as-is - -If you want to define custom error messages to be sent back to the -client with a HTTP error code (eg such as 400, 404 etc.) then -from *Camel 2.14.1* onwards you just 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: - -[source,java] ----- -restConfiguration().component("restlet").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) - .route() - .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: - -[source,java] ----- -public class UserErrorService { - public void idToLowError(Exchange exchange) { - exchange.getIn().setBody("id value is too low"); - exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain"); - exchange.getIn().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 (eg would otherwise bind to CountryPojo). - -=== Catching JsonParserException and returning a custom error message - -From *Camel 2.14.1* onwards you 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: - -[source,java] ----- -onException(JsonParseException.class) - .handled(true) - .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400)) - .setHeader(Exchange.CONTENT_TYPE, constant("text/plain")) - .setBody().constant("Invalid json data"); ----- - -=== Query Parameter default Values - -You can specify default values for parameters in the rest-dsl, such as -the verbose parameter below: - -[source,java] ----- - 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"); ----- - -From *Camel 2.17* onwards then the default value is automatic set as -header on the incoming Camel `Message`. So if the call -the `/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. - -=== Integrating a Camel component with Rest DSL - -Any Apache Camel component can integrate with the Rest DSL if they can -be used as a REST service (eg as a REST consumer in Camel lingo). To -integrate with the Rest DSL, then the component should implement -the `org.apache.camel.spi.RestConsumerFactory`. The Rest DSL will then -invoke the `createConsumer` method when it setup the Camel routes from -the defined DSL. The component should then implement logic to create a -Camel consumer that exposes the REST services based on the given -parameters, such as path, verb, and other options. For example see the -source code for camel-restlet, camel-spark-rest. - -=== Swagger API - -The Rest DSL supports link:swagger-java.html[Swagger Java] by -the `camel-swagger-java` module. See more details at - link:swagger-java.html[Swagger] and the `camel-swagger-java` example -from the Apache Camel distribution. - -From *Camel 2.16* onwards 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: - -[source,xml] ----- -<!-- this is a rest GET to view an user by the given id --> -<get uri="/{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 - -[source,java] ----- -.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: - -[source,xml] ----- -<!-- 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 - -[source,java] ----- -.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") ----- - - === See Also -* link:rest.html[Rest] +* link:rest-dsl.html[Rest DSL] * link:swagger-java.html[Swagger Java] http://git-wip-us.apache.org/repos/asf/camel/blob/3f617a16/camel-core/src/main/docs/rest-dsl.adoc ---------------------------------------------------------------------- diff --git a/camel-core/src/main/docs/rest-dsl.adoc b/camel-core/src/main/docs/rest-dsl.adoc new file mode 100644 index 0000000..8a7e4c5 --- /dev/null +++ b/camel-core/src/main/docs/rest-dsl.adoc @@ -0,0 +1,714 @@ +== REST API Component + +*Available as of Camel version 2.16* + +Apache Camel offers a REST styled DSL which can be used with Java or +XML. The intention is to allow end users to define REST services using a +REST style with verbs such as get, post, delete etc. + +=== How it works + +The Rest DSL is a facade that builds link:rest.html[Rest] endpoints as +consumers for Camel routes. The actual REST transport is leveraged by +using Camel REST components such +as link:restlet.html[Restlet], link:spark-rest.html[Spark-rest], and +others that has native REST integration. + +=== Components supporting Rest DSL + +The following Camel components supports the Rest DSL. See the bottom of +this page for how to integrate a component with the Rest DSL. + +* camel-coap +* link:netty-http.html[camel-netty-http] (also +supports link:swagger-java.html[Swagger Java]) +* link:netty4-http.html[camel-netty4-http] (also +supports link:swagger-java.html[Swagger Java]) +* link:jetty.html[camel-jetty] (also +supports link:swagger-java.html[Swagger Java]) +* link:restlet.html[camel-restlet] (also +supports link:swagger-java.html[Swagger Java]) +* link:servlet.html[camel-servlet] (also +supports link:swagger-java.html[Swagger Java]) +* link:spark-rest.html[camel-spark-rest] (also +supports link:swagger-java.html[Swagger Java] from Camel 2.17 onwards) +* link:undertow.html[camel-undertow] (also +supports link:swagger-java.html[Swagger Java] from Camel 2.17 onwards) + +=== Rest DSL with Java + +To use the Rest DSL in Java 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 define as follows, where we use rest() to +define the services as shown below: + +[source,java] +---- +protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @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: + +[width="100%",cols="25%,25%,25%,25%",options="header",] +|=== +|Base Path |Uri template |Verb |Consumes + +|/say |/hello |get |_all_ + +|/say |/bye |get |application/json + +|/say |/bye |post |_all_ +|=== + +Notice that in the REST service we route directly to a Camel endpoint +using the to(). This is because the Rest DSL has a short-hand for +routing directly to an endpoint using to(). An alternative is to embed a +Camel route directly using route() - there is such an example further +below. + +=== Rest DSL with XML + +The REST DSL supports the XML DSL also using either Spring or Blueprint. +The example above can be define in XML as shown below: + +[source,xml] +---- +<camelContext xmlns="http://camel.apache.org/schema/spring"> + <rest path="/say"> + <get uri="/hello"> + <to uri="direct:hello"/> + </get> + <get uri="/bye" consumes="application/json"> + <to uri="direct:bye"/> + </get> + <post uri="/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> +---- + +=== Using base path + +The REST DSL allows to define base path to make the DSL a bit more DRY. +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: + +[source,java] +---- +rest("/customers/") + .get("/{id}").to("direct:customerDetail") + .get("/{id}/orders").to("direct:customerOrders") + .post("/neworder").to("direct:customerNewOrder"); +---- + +And using XML DSL it becomes: + +[source,xml] +---- +<rest path="/customers/"> + <get uri="/{id}"> + <to uri="direct:customerDetail"/> + </get> + <get uri="/{id}/orders"> + <to uri="direct:customerOrders"/> + </get> + <post uri="/neworder"> + <to uri="direct:customerNewOrder"/> + </post> +</rest> +---- + +TIP: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 ( / ). But Apache +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 bast path and define the base path and uri template in the verbs +only. The example above can be defined as: + +[source,xml] +---- +<rest> + <get uri="/customers/{id}"> + <to uri="direct:customerDetail"/> + </get> + <get uri="/customers/{id}/orders"> + <to uri="direct:customerOrders"/> + </get> + <post uri="/customers/neworder"> + <to uri="direct:customerNewOrder"/> + </post> +</rest> +---- + +=== Using Dynamic To in Rest DSL + +*Available as of Camel 2.16* + +The link:rest-dsl.html[Rest DSL] supports the new .toD <toD> as dynamic +to in the rest-dsl. For example to do a request/reply +over link:jms.html[JMS] where the queue name is dynamic defined + +[source,java] +---- + public void configure() throws Exception { + rest("/say") + .get("/hello/{language}").toD("jms:queue:hello-${header.language}"); +} +---- + +=== And in XML DSL + +[source,xml] +---- +<rest uri="/say"> + <get uri="/hello//{language}"> + <toD uri="jms:queue:hello-${header.language}"/> + </get> +<rest> +---- + +See more details at link:message-endpoint.html[Message Endpoint] about +the dynamic to, and what syntax it supports. By default it uses +the link:simple.html[Simple] language, but it has more power than so. + +=== Embedding Camel routes + +Each of the rest service becomes a Camel route, so in the first example +we have 2 x get and 1 x post REST service, which each become a Camel +route. And we have 2 regular Camel routes, meaning we have 3 + 2 = 5 +routes in total. + +There are two route modes with the Rest DSL + +* mini using a singular to +* embedding a Camel route using route + +The first example is using the former with a singular to. And that is +why we end up with 3 + 2 = 5 total routes. + +The same example could use embedded Camel routes, which is shown below: + +[source,java] +---- +protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + rest("/say/hello") + .get().route().transform().constant("Hello World"); + rest("/say/bye") + .get().consumes("application/json").route().transform().constant("Bye World").endRest() + .post().to("mock:update"); + }; +} +---- + +In the example above, we are embedding routes directly in the rest +service using .route(). Notice we need to use .endRest() to tell Camel +where the route ends, so we can _go back_ to the Rest DSL and continue +defining REST services. + +#### Configuring route options + +In the embedded route you can configure the route settings such as +routeId, autoStartup and various other options you can set on routes +today. + +[source,java] +---- +.get() + .route().routeId("myRestRoute").autoStartup(false) + .transform().constant("Hello World"); +---- + + +=== Managing Rest services + +Each of the rest service becomes a Camel route, so in the first example +we have 2 x get and 1 x post REST service, which each become a Camel +route. This makes it _the same_ from 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. + +This means you can use JMX to stop/start routes, and also get the JMX +metrics about the routes, such as number of message processed, and their +performance statistics. + +There is also a Rest Registry JMX MBean that contains a registry of all +REST services which has been defined. + +=== Binding to POJOs using + +The Rest DSL supports automatic binding json/xml contents to/from POJOs +using Camels link:data-format.html[Data Format]. 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: + +[width="100%",cols="10%,90%",options="header",] +|=== +|Binding Mode |Description + +|off |Binding is turned off. This is the default option. + +|auto |Binding is enabled and Camel is relaxed and support json, xml or both if +the needed data formats are included in the classpath. Notice that if +for example `camel-jaxb` is not on the classpath, then XML binding is +not enabled. + +|json |Binding to/from json is enabled, and requires a json capabile data +format on the classpath. By default Camel will use `json-jackson` as the +data format. See the INFO box below for more details. + +|xml |Binding to/from xml is enabled, and requires `camel-jaxb` on the +classpath. See the INFO box below for more details. + +|json_xml |Binding to/from json and xml is enabled and requires both data formats to +be on the classpath. See the INFO box below for more details. +|=== + +TIP:From *Camel 2.14.1* onwards 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. + +INFO:From *Camel 2.16.3* onwards 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. + +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 + +[source,java] +---- +restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.auto); +---- + +And in XML DSL + +[source,xml] +---- +<restConfiguration bindingMode="auto" component="restlet" 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. + +[width="100%",cols="25%,25%,25%,25%",options="header",] +|=== +|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. + +For example to map from xml/json to a pojo class `UserPojo` you do this +in Java DSL as shown below: + +[source,java] +---- +// configure to use restlet on localhost with the given port +// and enable auto binding mode +restConfiguration().component("restlet").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: + +[source,java] +---- +// configure to use restlet on localhost with the given port +// and enable auto binding mode +restConfiguration().component("restlet").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"); +---- + +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: + +[source,java] +---- +// configure to use restlet on localhost with the given port +// and enable auto binding mode +restConfiguration().component("restlet").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: + +[source,java] +---- +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 to use JAXB +annotations, so we can add those annotations if we want to support XML +also + +[source,java] +---- +@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. + +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. Some +components has consumer options, which you can also configure from +endpoint level by prefixing the option with "consumer." +* 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 can for +example configure 2 component options, and 3 endpoint options etc. + + + +=== Enabling or disabling Jackson JSON features + +*Available as of Camel 2.15* + +When using JSON binding you may want to turn specific Jackson features +on or off. For example to disable failing on unknown properties (eg json +input has a property which cannot be mapped to a POJO) then configure +this using the dataFormatProperty as shown below: + +[source,java] +---- +restConfiguration().component("jetty").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: + +[source,java] +---- +.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"); +---- + +Likewise you can enable features using the enableFeatures such as: + +[source,java] +---- +restConfiguration().component("jetty").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 + +[source,xml] +---- +<restConfiguration component="jetty" 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> +---- + +=== Default CORS headers + +*Available as of Camel 2.14.1* + +If CORS is enabled then the follow headers is in use by default. You can +configure custom CORS headers which takes precedence over the default +value. + +[width="100%",cols="50%,50%",options="header",] +|=== +|Key |Value + +|Access-Control-Allow-Origin |* + +|Access-Control-Allow-Methods |GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH + +|Access-Control-Allow-Headers |Origin, Accept, X-Requested-With, Content-Type, +Access-Control-Request-Method, Access-Control-Request-Headers + +|Access-Control-Max-Age |3600 +|=== + +=== Defining a custom error message as-is + +If you want to define custom error messages to be sent back to the +client with a HTTP error code (eg such as 400, 404 etc.) then +from *Camel 2.14.1* onwards you just 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: + +[source,java] +---- +restConfiguration().component("restlet").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) + .route() + .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: + +[source,java] +---- +public class UserErrorService { + public void idToLowError(Exchange exchange) { + exchange.getIn().setBody("id value is too low"); + exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain"); + exchange.getIn().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 (eg would otherwise bind to CountryPojo). + +=== Catching JsonParserException and returning a custom error message + +From *Camel 2.14.1* onwards you 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: + +[source,java] +---- +onException(JsonParseException.class) + .handled(true) + .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400)) + .setHeader(Exchange.CONTENT_TYPE, constant("text/plain")) + .setBody().constant("Invalid json data"); +---- + +=== Query Parameter default Values + +You can specify default values for parameters in the rest-dsl, such as +the verbose parameter below: + +[source,java] +---- + 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"); +---- + +From *Camel 2.17* onwards then the default value is automatic set as +header on the incoming Camel `Message`. So if the call +the `/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. + +=== Integrating a Camel component with Rest DSL + +Any Apache Camel component can integrate with the Rest DSL if they can +be used as a REST service (eg as a REST consumer in Camel lingo). To +integrate with the Rest DSL, then the component should implement +the `org.apache.camel.spi.RestConsumerFactory`. The Rest DSL will then +invoke the `createConsumer` method when it setup the Camel routes from +the defined DSL. The component should then implement logic to create a +Camel consumer that exposes the REST services based on the given +parameters, such as path, verb, and other options. For example see the +source code for camel-restlet, camel-spark-rest. + +=== Swagger API + +The Rest DSL supports link:swagger-java.html[Swagger Java] by +the `camel-swagger-java` module. See more details at + link:swagger-java.html[Swagger] and the `camel-swagger-java` example +from the Apache Camel distribution. + +From *Camel 2.16* onwards 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: + +[source,xml] +---- +<!-- this is a rest GET to view an user by the given id --> +<get uri="/{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 + +[source,java] +---- +.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: + +[source,xml] +---- +<!-- 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 + +[source,java] +---- +.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") +---- +