How can you selectively export PATCH, PUT and POST for a #RestRepositoryResource? - spring-data-rest

I know that it is possible to use annotations to prevent the export of certain HTTP methods for a given repository, e.g:
#RepositoryRestResource
public interface AccountRepository extends PagingAndSortingRepository<Account, Long> {
#Override
#RestResource(exported = false)
Account save(Account entity);
}
If I understood the documentation correctly save is mapped to POST, PUT and PATCH. Is it possible to selectively prevent the export of these individually? For instance, in this case I want to allow PUT but prevent POST.

It is just a workaround which makes you to override automatically generated CRUD methods (by Spring Data REST) with your own controllers:
public class CrudController {
#RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.PUT})
public String save(Model uiModel) {
// code here
}
}

Related

Spring Data Rest ID conversion using HashIDs

We have a concern exposing internal IDs to the outside world. Therefore I'm thinking about using a hashing mechanism (current choice is hashids) to hash our IDs.
I tried to use a #JsonSerializer and #JsonDeserializer mapping on the Entities ID field. But this only takes effect when including the ID in the body, and has no impact on the IDs in the URL paths.
Is there a possibility to do this, e.g. something like an ID Translation SPI?
The only thing I can think of is to create a request filter that would take the request with encoded ID in URL, then decode the ID and redirect to an URL with decoded ID.
What you need is working "right from the box" in Spring Data REST by customizing item resource URIs:
#Configuration
public class RestConfigurer extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withEntityLookup().forRepository(ModelRepo.class, model -> HashIdUtil.encode(model.getId()), ModelRepo::findByEncodedId);
super.configureRepositoryRestConfiguration(config);
}
}
public interface ModelRepo extends JpaRepository<Model, Long> {
default Model findByEncodedId(String encodedId) {
return getById(HashIdUtil.decode(encodedId));
}
Model getById(Long id);
}
public class HashIdUtil {
private static final Hashids HASHIDS = new Hashids("salt", 8);
public static String encode(Long source) {
return HASHIDS.encode(source);
}
public static Long decode(String source) {
return HASHIDS.decode(source)[0];
}
}
Unfortunately, due to the bug (I suppose), PUT/PATCH-ing entities does not work in Spring Boot 2+, unlike the previous version of SB (1.5+) where it works as expected.
See my demo: sdr-hashids-demo
You could try using a converter.
#Component
#AllArgsConstructor
public class HashIdConverter implements Converter<String, Long> {
private final HashidsUtil hashidsUtil;
#Override
public Long convert(#NonNull String source) {
return hashidsUtil.decodeId(source);
}
}
Using it the way I just showed you is a bit unsafe, but it can do the work quite well if you are careful enough

Spring Data Redis global TTL for all entities

I need to set global TTL to each entity I have and it should be configurable in one place.
There is an opportunity to do this via #RedisHash annotation:
#RedisHash(value = "persons",timeToLive = 100)
public class Person{
...
}
or I can have a field
public class Person{
#TimeToLeave
Long ttl;
}
but in this case I can't change it in one place and it's not really comfortable
to maintain it.
I have a property in applicaiton.properties:
app.redis.ttl=100
and it will be awesome to have an opportunity to change it on property level.
You can configure settings by creating a subclass of KeyspaceConfiguration and configuring #EnableRedisRepositories. There's no property-based configuration for global TTL.
#EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class MyConfig {
}
public class MyKeyspaceConfiguration extends KeyspaceConfiguration {
#Override
public boolean hasSettingsFor(Class<?> type) {
return true;
}
#Override
public KeyspaceSettings getKeyspaceSettings(Class<?> type) {
KeyspaceSettings keyspaceSettings = new KeyspaceSettings(type, "my-keyspace");
keyspaceSettings.setTimeToLive(3600L);
return keyspaceSettings;
}
}
Deriving from KeyspaceConfiguration is intended to provide Iterable<KeyspaceSettings> initialConfiguration() in the first place but since you want to apply that settings to all classes, the in-place creation of KeyspaceSettings makes more sense.
You also might want to cache the KeyspaceSettings to not create instances all over so Java 8's Map.computeIfAbsent(…) would be a good fit.

Simple Injector Property Injection

How do you perform property injection with Simple Injector.
The with Ninject you do is as per bellow:
[Inject]
public IUnitOfWork UnitOfWork { get; set; }
How can I do the equivalent to this with Simple Injector. I tried finding a solution online but had no luck.
Why do I want to use Property Injection?
I want to use property injection to set up unit of work in my base controller so that it will create a new unit of work OnActionExecuting and commit the changes OnResultExecuted. It also means I don't have to pass in the UoW with each new controller I create through the constructor.
Another option is to use the RegisterInitializer method:
container.RegisterInitializer<BaseControllerType>(controller =>
{
controller.UnitOfWork = container.GetInstance<IUnitOfWork>();
}
It keeps all configuration in your composition root and does not pollute your code base with all kinds of attributes.
Update: (as promised)
While this is a direct answer to your question I have to provide you with a better option, because the usage of a base class for this is a IMO not the correct design, for multiple reasons.
Abstract classes can become real PITA classes as they tend to grow towards a god class which has all kinds of cross cutting concerns
An abstract class, especially when used with property injection, hides the needed dependencies.
With focus on point 2. When you want to unit test a controller which inherits from the base controller, you have no way of knowing that this controller is dependent on IUnitOfWork. This you could solve by using constructor injection instead of property injection:
protected abstract class BaseController : Controller
{
protected readonly IUnitOfWork uoW;
protected BaseController (IUnitOfWork uoW)
{
this.uoW = uoW;
}
}
public class SomeController : BaseController
{
public SomeController(IUnitOfWork uoW) : base(uoW) { }
}
While this solves point 2, point 1 is still lurking. The main reason you're wanting this, as you say, is because you do not want to commit your changes in every Action method. Changes must just be saved by the context when the request is done. And thinking about design in this way is a good thing, because Saving changes is, or can be seen as a cross cutting concern and the way you're implementing this is more or less known as AOP.
If it's comes to AOP, especially if you're working with atomic actions in the action methods of your controllers, there is a far better, more SOLID and more flexible design possible which deals with this very nicely.
I'm referring to the Command/Handler pattern which is described in great detail here (also read this for the query part of your application).
With this patterns you don't inject a generic IUnitOfWork abstraction, but inject the specific needed ICommandHandler<TCommand> abstractions.
The action methods would fire the responsible commandhandler for this specific action. All commandhandlers can simple be decorated by a single open-generic SaveChangesCommandHandlerDecorator, 'ValidationDecorator', 'CheckPermissionsDecorator', etc...
A quick example:
public class MoveCustomerCommand
{
public int CustomerId;
public Address NewAddress;
}
public class MoveCustomerCommandHandler : ICommandHandler<MoveCustomerCommand>
{
public void Handle(MoveCustomerCommand command)
{
// retrieve customer from database
// change address
}
}
public class SaveChangesCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
private readonly DbContext db;
public SaveChangesCommandHandlerDecorator(
ICommandHandler<TCommand> decoratee, DbContext db)
{
this.decoratee = decoratee;
this.db = db;
}
public void Handle(TCommand command)
{
this.decoratee.Handle(command);
this.db.SaveChanges();
}
}
// Register as
container.Register(typeof(ICommandHandler<>), new []{Assembly.GetExecutingAssembly() });
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(SaveChangesCommandHandlerDecorator<>));
// And use in controller as
public ActionResult MoveCustomer(int customerId, Address address)
{
var command = new MoveCustomerCommand
{ CustomerId = customerId, Address = address };
this.commandHandler.Handle(command);
return View(new ResultModel());
}
This keeps your controllers clean and let it do what it must do, namely be the layer between the business logic (the commandhandler implementation in this case) and the view.
Need to create the following:
First create the attribute class
[System.AttributeUsage(System.AttributeTargets.Property]
public class Inject : Attribute
{
}
Then create a custom property behavior
class PropertySelectionBehavior<TAttribute> : IPropertySelectionBehavior
where TAttribute : Attribute
{
public bool SelectProperty(Type type, PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(TAttribute)).Any();
}
}
Finally tell the container to use custom behavior
container.Options.PropertySelectionBehavior = new PropertySelectionBehavior<Inject>();
All that is left to do is decorate the property with the attribute
[Inject]
public IUnitOfWork UnitOfWork { get; set; }

Spring & Reslet : is it possible to map a URL path component to a method argument?

I'm new to Restlet, but I've followed the tutorial on Restlet's own website and got a basic application up and running. What I'm doing right now is that I'm setting up a basic ServerResource and expose a #Get method.
What I'd like is to be able to invoke /user/{userId} and get the user representation back. Is it possible, somehow, to hand over the mapping of {userId} to Restlet, which in turn would invoke getUser(String userId) in my ServerResource?
Such feature (binding path variables into annotated method parameters) isn't natively supported in the framework. Such mapping in the annotated method signatures is only supported with input representation.
To get the path variables of a request, you can get them from the request object (method getAttribute), as described below:
public class UserServerResource extends ServerResource {
#Get
public User getUser() {
String userId = getAttribute("userId");
User user = (...)
(...)
return user;
}
}
If you want to share this path variable across several methods, you can define it as a instance variable (notice that a new instance of the server resource is created for each request unlike to Spring REST where each controller is a singleton and such variable must be defined in method signatures). We can leverage the method doInit of the server resource, as described below:
public class UserServerResource extends ServerResource {
private String userId;
private User user;
#Override
protected void doInit() throws ResourceException {
super.doInit();
userId = getAttribute("userId");
// for example
user = loadUser(userId);
// throws a status 404 if user can't be found
setExisting(user != null);
}
#Get
public User getUser() {
return user;
}
#Put
public User saveUser(User user) {
saveUser(user);
return user;
}
#Delete
public void deleteUser() {
deleteUser(user);
}
}
If you really want to use a mapping from request elements (like path variables, ...) to method parameters, you should use JAXRS. Restlet provides a support of this specification. Implementing a similar server resource as above but with JAXRS is described below:
#Path("/users/{userId}")
public class UserResource {
#GET
#Produces("text/xml")
public String getUser(#PathParam("userId") String userId) {
(...)
}
}
For more details, you can have a look at the corresponding documentation: http://restlet.com/technical-resources/restlet-framework/guide/2.2/extensions/jaxrs.
Hop it helps,
Thierry

Play framework: How to require login for some actions, but not all

Adding #With(Secure.class) to a controller blocks all unauthenticated access. Is there a way to enabled it only for certain actions, or to except certain actions after it's enabled on a controller?
You can't do that with the secure module. As Niels said the secure module is more an example than a solution. You can build your own security system with the #Before annotation. Here is an example:
public class Admin extends Controller {
#Before(unless={"login", "authenticate", "logout", "otherMethod"})
void checkAccess() {
// check the cookie
}
public void login() {
render();
}
public void authenticate(String email, String password) {
// check the params and set a value in the cookie
}
public void logout() {
// delete cookie
}
I recommend you to read the source code of the secure module.
I've since found my earlier #Public solution somewhat limiting since it can't address inherited actions. I've instead gone to a class-level annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface AllowGuest {
String[] value();
}
and added this code to the beginning of the Secure.checkAccess() method:
AllowGuest guest = getControllerInheritedAnnotation(AllowGuest.class);
if (guest != null) {
for (String action : guest.value()) {
if (action.equals(request.actionMethod))
return;
}
}
which can be used like this: #AllowGuest({"list","view"})
This makes it easy to allow access to local and inherited actions, and to see which actions in a controller are unsecured.
Remove #With(Secure.class) annotation to the controller and add this piece of code inside the controller.
#Before(unless={"show"})
static void checkAccess() throws Throwable {
Secure.checkAccess();
}
where show is the action you need to make publicly available.
In order to get what I was looking for, I copied the Check annotation and created a Public annotation.
package controllers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface Public {
}
then I added these two lines to the beginning of the Secure.checkAccess:
if (getActionAnnotation(Public.class) != null)
return;
Now actions in controllers using With(Secure.class) can be made accessible without logging in by adding a #Public annotation to them.
You can set at the #Before-Tag of the Secure Controller the value unless or only. The Secure-Module is more an example than a solution.