I am writing a Spring Boot app (RESTful webservice) that uses Jackson for serialization. I have the following data models that will be sent back and forth between the service and its HTTP clients (hence these will be serialized/deserialized to/from JSON):
public abstract class BaseEntity {
#JsonIgnore
private Long id;
private UUID refId;
// Getters, setters, ctors, etc.
}
public abstract class BaseLookup extends BaseEntity {
private String name;
private String label;
private String description;
// Getters, setters, ctors, etc.
}
public class State extends BaseLookup {
private String abbrev; // "VT", "FL", etc.
// Getters, setters, ctors, etc.
}
public class Contact extends BaseEntity {
private String givenName;
private String surname;
private State state;
// Getters, setters, ctors, etc.
}
public class Account extends BaseEntity {
private Contact contact;
private String code;
// lots of other fields that will be generated server-side
// Getters, setters, ctors, etc.
}
Thus there will be some endpoints for CRUDding Accounts, others for CRUDding Contacts, etc. For instance, the AccountController will expose endpoints for CRUDding Account instances:
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#RequestMapping(method = RequestMethod.POST)
public void createAccount(#RequestBody Account account) {
// Do stuff and persist the account to the DB
}
}
I want to simplify the JSON that HTTP clients must craft in order to create new Account, Contact, etc. instances. At the same time there are fields on those data models that I do not want exposed to the client-side. Things like the BaseEntity#id (which is the PK of the entity in the DB). Or for instance, in the case of State, I just want the client-side to know about (and use) the abbrev field, etc. I don't want them to ever see the other BaseLookup fields or even know about them.
Hence, my end goal is to allow the client to POST the following JSON, and have a custom Jackson deserializer convert that JSON into an Account instance:
{
"contact" : {
"givenName" : "Him",
"surname" : "Himself",
"state" : "NY"
},
"code" : "12345"
}
So you see, like I stated above, this JSON accomplishes several things:
The client-side doesn't provide a BaseEntity#id or BaseEntity#refId when POSTing to create a new instance
For the contact.state field, which is a BaseLookup with several fields (id, refId, name, label, description, abbrev), the user only has to provide the abbrev field, and the deserializer is expected to figure out which State the client is referring to
The Account class actually has many other fields that are inferred/generated server-side; the client doesn't need to know about them in order to create an Account instance
The JSON above is a simplified form of what we would get if we serialized an Account with Jackson's default behavior; this is to make things easier on the client-side and even more secure on the server-side (not exposing PKs, etc.)
The important thing to note here is that the JSON sent to this controller for the contact field is identical to the JSON that will be POSTed to a ContactController for creating new Contact instances.
Here's the problem:
public class AccountDeserializer extends StdDeserializer<Account> {
public AccountDeserializer() {
this(null);
}
public AccountDeserializer(Class<Account> accClazz) {
super(accClazz);
}
#Override
public Account deserialize(JsonParser jsonParser, DeserializationContext dCtx)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.codec.readTree(jsonParser)
Contact contact = ??? // TODO: How to invoke ContactDeserializer here?
String accountCode = node.get("code").asText();
// Generate lots of other Account field values here...
Account account = new Account(contact, accountCode, /* other fields here */);
return account;
}
}
Since I will also have a ContactController (for CRUDding Contact instances irrespective of an associated Account), and because I have similar desires to hide Contact fields from the client-side as well as to simplify the JSON coming into this ContactController#createContact endpoint, I will also need a ContactDeserializer in addition to this AccountDeserializer...
public class ContactDeserializer extends StdDeserializer<Contact> {
// ...etc.
}
This ContactDeserializer will be responsible for converting JSON into Contact instances. But since Account instances also contain Contact instances, and because the "contact JSON" inside the outer "account JSON" will be the same as any JSON that the client sends to any of the "contact endpoints", I'd like to invoke the ContactDeserializer from inside the AccountDeserializer somehow.
That way, when the ContactController receives "contact JSON" to create a new Contact instance, the ContactDeserializer is engaged to get the job done. And, if the AccountController receives "account JSON" to create a new Account instance, then the AccountDeserializer is engaged to get that job done...and it uses the ContactDeserialzer to handle the deserialization of the account JSON's internal contact field as well.
Can this be done?! Can one Jackson deserializer reuse other deserializers inside of it? If so, how? If not, then what's the solution here?!
You can invoke ContactDeserializer by calling the treeToValue method of ObjectCodec. Jackson will automatically pick up the ContactDeserializer for you if you've registered it on your ObjectMapper.
public class AccountDeserializer extends JsonDeserializer<Account> {
#Override
public Account deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
JsonNode node = p.readValueAsTree();
JsonNode contactNode = node.get("contact");
Contact contact = null;
if (contactNode != null) {
contact = p.getCodec().treeToValue(contactNode, Contact.class);
}
return new Account(contact, /* account properties */);
}
}
Edit
If you want to add your deserializers to existing mapper which is created by Spring Boot, you can autowire it in one of your configuration classes and customize as you like.
#Configuration
public class ObjectMapperConfiguration {
#Autowired
public void configureObjectMapper(ObjectMapper mapper) {
SimpleModule module = new SimpleModule()
.addDeserializer(Account.class, new AccountDeserializer())
.addDeserializer(Contact.class, new ContactDeserializer());
mapper.registerModule(module);
}
}
Related
When getting a request in any action of any controller, I look at the jwt know which user is requesting and lookup the user in the database to get some user-data that I want to use throughout the application. E.g. which departments the user belongs to or the users preferred language.
Now I could create a object which wraps these information and send it down the layers and pass it to every method that likes to use some of this data. But I like the data to be available to every method throughout the application without passing it in every method. Like e.g. dependency injection (Seems to late at that point) or something else I can get access to that data quickly.
Any advice of how to handle it?
Try it with the Items property on the HttpContext. By using it you can store data during a single request. The only downside with this approach is that every service needs to have access to the HttpContext to read the values. Values can be added to the Items Dictionary as shown below
public class IndexModel : PageModel
{
//
public void OnGet()
{
HttpContext.Items.Add("Key", new RequestInfo { Key = "RequestKey" });
}
}
class RequestInfo
{
public string Key { get; set; }
}
You can then access the value by registering the IHttpContextAccessor to the IServiceCollection and then using Constructor injection to use the HttpContext in your service so that you can work with the Items Dictionary.
public class Service
{
private IHttpContextAccessor _htp;
public Service(IHttpContextAccessor http)
{
_htp = http;
}
public void Log()
{
Console.WriteLine(((RequestInfo)_htp.HttpContext.Items["Key"]).Key);
}
}
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
My Startup.cs contains 40 repositories:
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IXRepository, XRepository>();
... 40 more lines ...
I'm trying to find a way to have this mess in a single line, but I'm failing miserably to put my head around this, I have several methods that are not available in the interface. Can someone provide some help? I understand why it is not working, it does not have a concrete repository, but I'm no closer to making this work.
InvalidOperationException: Unable to resolve service for type...
// IRepository.cs
public interface IRepository
{
}
// IUserRepository.cs
public interface IUserRepository : IRepository
{
User ReadToken(string email, string password);
}
// BaseRepository.cs
public class BaseRepository : IDisposable
{
protected IDbConnection PostgreSQL;
public BaseRepository(IDbConnection postgreSQL)
{
PostgreSQL = postgreSQL;
}
public void Dispose()
{
}
}
// UserRepository.cs
public class UserRepository : BaseRepository, IUserRepository
{
public UserRepository(IDbConnection postgreSQL) : base(postgreSQL)
{
}
public User ReadToken(string email, string password)
{
object parameters;
string sql;
parameters = new
{
email,
password
};
sql =
#"
SELECT
user_id AS id,
token
FROM users
WHERE
email = #email AND
password = CRYPT(#password, password) AND
active = TRUE;
";
var user = base.PostgreSQL.Query<User>(sql, parameters).SingleOrDefault();
if (user == null)
throw new UnauthorizedException("User", "User not found.");
return user;
}
}
// UsersController.cs
public class UsersController : ControllerBase
{
protected IUserRepository UserRepository;
public UsersController(IUserRepository userRepository)
{
UserRepository = userRepository;
}
}
// Startup.cs
services.AddTransient<IRepository, BaseRepository>();
You can do that with Scrutor
It offers assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
All those repositories can be summed up to something like this:
services.Scan(x => x.FromAssemblyOf<IAnAssemblyRegistrationMarker>()
.AddClasses(filter => filter.AssignableTo(typeof(IRepository)))
.AsImplementedInterfaces()
.WithScopedLifetime());
IAnAssemblyRegistrationMarker is an empty interface to point at the assembly (project) you want to scan
AddClasses Adds all public, non-abstract classes from the selected assemblies that matches the requirements specified in the
AsImplementedInterfaces Registers each matching concrete type as all of its implemented interfaces
WithScopedLifetime Registers each matching concrete type with Scoped Lifetime (You also have WithSingletonLifetime and WithTransientLifetime)
The only requirement in the code example above is that the repositories implement IRepository in order for you to target only the necessary items.
Disclaimer: I am not associated with Scrutor in any way. I just like the thing.
What you are looking for is called convention based registration. This gives you ability register all types which for example ends with Repository as the interfaces they implement. However the built-in ASP.NET Core IOC is very lightweight and doesn't provide such functionality. So you can either wrap it up with your code which scans all referenced assemblies, look for types by a pattern and then add them to ServiceCollection or you can use different IOC implementation that provides this functionality and supports .Net Core e.g. Autofac, StructureMap etc.
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
I am trying to implement a very simple example of FormsAuthentication. It is not real life but it has thrown up a problem. The AuthenticationService, which is intended to be an Application level singleton, appears to be instantiated twice.
Here is the code:
public class User : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
public interface IAuthenticationService
{
Guid GetIdentifier(string username, string password);
}
public class AuthenticationService : IUserMapper, IAuthenticationService
{
public readonly Guid Identifier = Guid.NewGuid();
private readonly string Username = "admin";
private readonly string Password = "x";
public Guid GetIdentifier(string username, string password)
{
return (username == Username && password == Password) ? Identifier : Guid.Empty;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
return (identifier == Identifier) ? new User { UserName = "admin" } : null;
}
}
public class MyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<IAuthenticationService, AuthenticationService>().AsSingleton();
}
}
The code above is being used by my LoginModule as follows. Please note that I am injecting the application-level singleton instance of the AuthenticationService via the module's constructor.
public LoginModule(IAuthenticationService authenticationService)
{
Post["/login"] = _ =>
{
var identifier = authenticationService.GetIdentifier(
(string) Form.Username,
(string) Form.Password);
if (identifier.IsEmpty())
{
return Context.GetRedirect("~/login?error=true");
}
return this.LoginAndRedirect(identifier);
};
}
What should happen is that when the user POSTs their username and password, these are checked by the AuthenticationService via the GetIdentifier(..) method. If the credentials match then the single GUID identifier is returned. This GUID will always be the same because it is created as a readonly field and thus set once when the singleton AuthenticationService is first instantiated at application startup.
However this is not the case. Instead two distinct instances of the AuthenticationService are created, one that is injected into the LoginModule constructor and used to call the GetIdentifier(..) method and another instance which Nancy uses to call the IUserIdentity.GetUserFromIdentifier(..) method.
These two instances have different GUID identifiers and so the GetUserFromIdentifier(..) method always return null.
I have tested a standard singleton service that does not implement IUserMapper and it works as expected, only one instance is created.
So it seems that Nancy is instantiating the IUserMapper singleton twice, once for its own internal use during FormsAuthentication, and once to inject into my LoginModule constructor!
Can you spot my mistake?
Thanks
It's probably because you're using a different interface so you have one singleton for things requesting IUsernameMapper and another for things requesting IAuthenticationService.
You can either:
Register both with an instance of your authentication service
Split out the username mapper and take a dependency on that in your service (so your Application Service has a dependency on the IUsernameMapper - that will be the same one Nancy is using)
Register one of them using a factory that resolves using the other interface (container.Register((c,p) => c.Resolve
My I ask why you're doing any of this though rather than just using the built in forms auth?