announcement - icon

Flakiness in REST requests is a common issue. A request can get a 200 OK in one scenario and a 409 next time. Sometimes a request can even succeed and fail intermittently on the same exact request. In short, working over HTTP can be a bit of a mess without solid tooling.

Also, while it’s easy enough to debug these issues locally when developing the application, we’re talking about production here - we can’t afford the downtime while you’re stepping in and out of code. Uptime is kind of the whole point.

With Lightrun, you can get the same level of access you get with a local debugger or profiler - no downtime required. You can add logs, metrics, and snapshots (think breakpoints, but without stopping the running service), in a safe and read-only manner - without redeploying, restarting, or even stopping the running service. Performance and security are maintained throughout the process.

Learn how to debug a live REST API (built with Spring, of course), using Lightrun, in this 5-minute tutorial:

>> Debugging REST Requests in Spring-Based applications using the Lightrun Platform

1. Introduction

Jersey is a popular Java framework for creating RESTful web services.

In this tutorial, we'll explore how to read different request parameter types via a simple Jersey project.

2. Project Setup

Using Maven archetypes, we'll be able to generate a working project for our article:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2
  -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false
  -DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example
  -DarchetypeVersion=2.28

The generated Jersey project will run on top of a Grizzly container.

Now, by default, the endpoint for our app will be http://localhost:8080/myapp.

Let's add an items resource, which we'll use for our experiments:

@Path("items")
public class ItemsController {
    // our endpoints are defined here
}

Note, by the way, that Jersey also works great with Spring controllers.

3. Annotated Parameters Types

So, before we actually read any request parameters, let's clarify a few rules. The allowed types of parameters are:

  • Primitive types, like float and char
  • Types that have a constructor with a single String argument
  • Types that have either a fromString or valueOf static method; for those, a single String argument is mandatory
  • Collections – like List, Set, and SortedSet – of the types described above

Also, we can register an implementation of the ParamConverterProvider JAX-RS extension SPI. The return type must be a ParamConverter instance capable of a conversion from a String to a type.

4. Cookies

We can resolve cookie values in our Jersey methods using the @CookieParam annotation:

@GET
public String jsessionid(@CookieParam("JSESSIONId") String jsessionId) {
    return "Cookie parameter value is [" + jsessionId+ "]";
}
If we start up our container, we can cURL this endpoint to see the response:
> curl --cookie "JSESSIONID=5BDA743FEBD1BAEFED12ECE124330923" http://localhost:8080/myapp/items
Cookie parameter value is [5BDA743FEBD1BAEFED12ECE124330923]

5. Headers

Or, we can resolve HTTP headers with the @HeaderParam annotation:

@GET
public String contentType(@HeaderParam("Content-Type") String contentType) {
    return "Header parameter value is [" + contentType+ "]";
}

Let's test again:

> curl --header "Content-Type: text/html" http://localhost:8080/myapp/items
Header parameter value is [text/html]

6. Path Parameters

Especially with RESTful APIs, it's common to include information in the path.

We can extract path elements with @PathParam:

@GET
@Path("/{id}")
public String itemId(@PathParam("id") Integer id) {
    return "Path parameter value is [" + id + "]";
}

Let's send another curl command with the value 3:

> curl http://localhost:8080/myapp/items/3
Path parameter value is [3]

7. Query Parameters

We commonly use query parameters in RESTful APIs for optional information.

To read such values we can use the @QueryParam annotation:

@GET
public String itemName(@QueryParam("name") String name) {
    return "Query parameter value is [" + name + "]";
}

So, now we can test with curl, like before:

> curl http://localhost:8080/myapp/items?name=Toaster
Query parameter value if [Toaster]

8. Form Parameters

For reading parameters from a form submission, we’ll use the @FormParam annotation:
@POST
public String itemShipment(@FormParam("deliveryAddress") String deliveryAddress, 
  @FormParam("quantity") Long quantity) {
    return "Form parameters are [deliveryAddress=" + deliveryAddress+ ", quantity=" + quantity + "]";
}

We also need to set the proper Content-Type to mimic the form submission action. Let's set the form parameters using the -d flag:

> curl -X POST -H 'Content-Type:application/x-www-form-urlencoded' \
  -d 'deliveryAddress=Washington nr 4&quantity=5' \
  http://localhost:8080/myapp/items
Form parameters are [deliveryAddress=Washington nr 4, quantity=5]

9. Matrix Parameters

A matrix parameter is a more flexible query parameter as they can be added anywhere in the URL.

For example, in http://localhost:8080/myapp;name=value/items, the matrix parameter is name.

To read such values, we can use the available @MatrixParam annotation:

@GET
public String itemColors(@MatrixParam("colors") List<String> colors) {
    return "Matrix parameter values are " + Arrays.toString(colors.toArray());
}

And now we'll test again our the endpoint:

> curl http://localhost:8080/myapp/items;colors=blue,red
Matrix parameter values are [blue,red]

10. Bean Parameters

Finally, we'll check how to combine request parameters using bean parameters. To clarify, a bean parameter is actually an object that combines different types of request parameters.

We'll use a header parameter, a path and a form one in here:

public class ItemOrder {
    @HeaderParam("coupon")
    private String coupon;

    @PathParam("itemId")
    private Long itemId;

    @FormParam("total")
    private Double total;

    //getter and setter

    @Override
    public String toString() {
        return "ItemOrder {coupon=" + coupon + ", itemId=" + itemId + ", total=" + total + '}';
    }
}

Also, to get such a combination of parameters, we'll use the @BeanParam annotation:

@POST
@Path("/{itemId}")
public String itemOrder(@BeanParam ItemOrder itemOrder) {
    return itemOrder.toString();
}

In the curl command, we've added those three types of parameters and we'll end up with a single ItemOrder object:

> curl -X POST -H 'Content-Type:application/x-www-form-urlencoded' \
  --header 'coupon:FREE10p' \
  -d total=70 \
  http://localhost:8080/myapp/items/28711
ItemOrder {coupon=FREE10p, itemId=28711, total=70}

11. Conclusion

To sum it up, we've created a simple setup for a Jersey project to help us explore how to read different parameters from a request using Jersey.

The implementation of all these snippets is available over on Github.

REST bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course :

>> CHECK OUT THE COURSE
REST footer banner
Comments are closed on this article!