how to validate header params using Bean Validation and jax-rs - jax-rs

I have jax-rs resources and each have a same header. what is the best way to validate that the header is present using Bean Validation. I know about #HeaderParam but I don't want to change all my methods in all resources to include the header param.

It's as simple as implementing javax.ws.rs.container.ContainerRequestFilter. For example:
#Provider
public class ContentTypeValidatorFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext reqContext) {
String contentType = getHeader(reqContext, "Content-Type");
// Content-type validation, but you can valid as many headers as you want.
if (Objects.isNull(contentType)) {
throw new InvalidRequestException("Content-Type header is missing");
}
}
private String getHeader(ContainerRequestContext requestContext, String header) {
return requestContext.getHeaders().getFirst(header);
}
}
Later, to handle this exception gracefully just implement ExceptionMapper for this InvalidRequestException.
The above filter will be applied globally. But if you want to exclude some endpoints, then make use of #NameBinding to annotate your custom annotation and apply it only to specific endpoints.

Related

How to get path template in JAX-RS ContainerRequestFilter?

From within a JAX-RS ContainerRequestFilter, I'd like to obtain the unresolved path URI of the request; i.e. something like "todos/{id}". Via requestContext.getUriInfo().getPath() I only get the path with parameters resolved, e.g. "todos/1". Is there any way for getting the path with parameters instead?
You may need to just build the template. It's not that difficult. You can get access to the resource class and method (from an injected ResourceInfo) and just use the UriBuilder methods. It has path() methods that accept Class and Method and it will build the Uri for you based on the #Path annotations of those objects. Then just get the template with UriBuilder#toTemplate() For example
public class UriTestingFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext request) throws IOException {
UriInfo uriInfo = request.getUriInfo();
Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
UriBuilder partialUriBuilder = UriBuilder.fromResource(resourceClass)
.path(resourceMethod);
String partialUri = partialUriBuilder.toTemplate();
URI baseUri = uriInfo.getBaseUri();
UriBuilder fullUriBuilder = UriBuilder.fromUri(baseUri)
.path(resourceClass)
.path(resourceMethod);
String fullUri = fullUriBuilder.toTemplate();
}
}
Using ResourceInfo doesn't really work very well with sub-resources.
It's possible to get it in Jersey from the ContainerRequestContext when using using this:
List<UriTemplate> matchedTemplates = new ArrayList<>(ctx.getMatchedTemplates());
Collections.reverse(matchedTemplates);
String path = matchedTemplates.stream()
.map(UriTemplate::getTemplate)
.filter(s -> !s.equals("/"))
.collect(joining());

Injecting IOptions<> into ApiKeyAuthorizeAttribute

I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.
However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.
Here is how my controller looks like now:
[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
//some endpoints here
}
ApiKeyAuthorize Class:
public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
//////This...
private readonly IOptions<MyConfig> _config;
public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
{
_config = config;
}
/////////...is what I am trying to accomplish
public void OnAuthorization(AuthorizationFilterContext context)
{
var request = context.HttpContext.Request;
var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);
if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
{
context.Result = new UnauthorizedResult();
}
}
}
My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.
Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:
[ServiceFilter(typeof(ApiAuthorizeAttribute))]
This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:
[ApiAuthorizeAttribute(Roles = "Admin")]
It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.

Add query param at runtime when using CXF proxy client

So I'm using CXF-RS proxy feature to create reusable REST client that I will use in multiple applications. So I have an interface, something like that :
#Path("/hello")
public interface HelloService {
#GET
#Path("sayHello")
String sayHello(#QueryParam("name") String name);
}
And I'm creating the client with :
JAXRSClientFactory.create(address, HelloService.class, Collections.singletonList(JacksonJsonProvider.class), true)
But now I need depending on the configuration of the application to send an additional query parameter to the request. I would like not to change the interface HelloService and instead use some kind of filter to handle this. I saw the ClientRequestFilter but I don't know if it's the right tool and how I should add it to the proxy (all the tutorials I saw use ClientBuilder.newClient() and not a proxy).
Thank you in advance.
Sure you can use a ClientRequestFilter for this. Say you wanted to add a query param. You could do something like
public class MyClientFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext request) throws IOException {
request.setUri(UriBuilder.fromUri(request.getUri())
.queryParam("foo", "bar")
.build());
}
}
To register it, you just add it to the list you pass as the third argument to JAXRSClientFactory.create. Look at the docs for JAXRSClientFactory. You can see the overloaded create methods that accepts a list of providers. The ClientRequestFilter is a kind of provider.

Spring data REST content negotiation

I have an annotated RepositoryRestResource for domain objects whose fields also contain binary data (images.) The Spring Rest machinery will create nice RESTful links for these fields, but I would also like to introduce a handler that returns the bare binary when browsers send an "image/*" accept header.
I could overlay a controller on the same paths, but it's brittle and I'd rather write a strategy class for this.
Possible? Any idea where to plug it in the Spring plumbing?
TIA,
Edoardo
Using the #RepositoryRestController annotation "properly", you should be able to restrict the controller override to the "image/*" only.
Say you have a simple domain object model (getters/setters and a some annotations omitted...)
public class Item {
#Id
private String id;
private String name;
}
Let's override the controller for image/* only
#RepositoryRestController
#ResponseBody
public class ItemRepositoryRestController {
#Autowired
private ItemRepository repository;
#RequestMapping(value = "/items/{id}", method = RequestMethod.GET,
produces = "image/*")
public Item getItem(#PathVariable(value = "id") String id)
{
Item item = repository.findOne(id);
/* do some magic with your item */
return item;
}
Obviously, no image/* data is returned here - you'll actually get a 400 error - but you do only get to this controller if asking if accepting image/*, while going through the automagic Spring Rest Controller when not asking for image/* if and only if you use #RequestMapping at the method level.
I haven't been to the point where I return raw binary data, you may have to use the HttpServletResponse directly as shown here. Or perhaps you already have an answer for this since in your comment you mention having added another resource path already (in which case I'm interested in how you return the raw data).

How to make Jersey / Moxy more restrictive when unmarshaling JSON?

I'm currently using Jersey and Moxy in Glassfish 4. Is there a way to tell Jersey/Moxy to refuse a HTTP request if its JSON content is not valid (i.e. it contains more objects than it should when binding JSON to a POJO) ?
I would create my own subclass of MOXyJsonProvider (see: http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html). Then in that subclass I would override the preReadFrom method. In that method I would set an Unmarshaller.Listener.
#Override
protected void preReadFrom(Class<Object> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
Unmarshaller unmarshaller) throws JAXBException {
Unmarshaller.Listener ul = new YourUnmarshallerListener();
unmarshaller.setListener(ul);
}
Then Unmarshaller.Listener would then count each time an object was unmarshalled and error out if too many are read.