Spring Data Rest Jpa insert #Lob field - spring-data-rest

I have a spring data rest service, that expose a resource like:
#Entity
public class Resource{
private String name;
#Lob
private byte[] data;
private String contentType;
}
How should be a json to insert a resource of this type?

AFAIK, SDR does not handle multipart requests or responses yet, as it can only do JSON.
You can run SDR at the same time as a regular Spring MVC servlet (it's one line of code in your config).
I would suggest using a regular Spring MVC controller for your file upload/download, and SDR for the rest (pun intended).

You don't need JSON.
"name" and "contentType" are part of the http header (respectively "Content-Type" and "Content-Disposition: filename")
"data" is the HTTP body. Its encoding depends of "Content-Encoding"
Maybe you should use "ResourceResolvers" plugged with JPA.

Spring Content was designed for exactly this.
Assuming you are using Spring Boot then you can add LOB handling as follows:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa-boot-starter</artifactId>
<version>0.0.11</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.11</version>
</dependency>
Add a Store:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#StoreRestResource(path="resourceContent")
public interface ResourceContentStore extends ContentStore<Resource,String> {}
}
Associate content with your entity entity:
#Entity
public class Resource {
private String name;
#ContentId
private String contentId;
#ContentLength
private long contentLength = 0L;
#MimeType
private String mimeType = "text/plain";
}
That's all that you should need. When you application starts Spring Content will see the dependencies on the Spring Content JPA/REST modules and it will inject an implementation of the ResourceContentStore store for JPA as well as an implementation of a controller (at /resourceContent) that supports that maps GET, POST, PUT and DELETE requests onto the underlying Store interface. The REST endpoint will be available under.
i.e.
curl -X PUT /resourceContent/{resourceId} will create or update an resource's content
curl -X GET /resourceContent/{resourceId} will fetch the resource's content
curl -X DELETE /resourceContent/{resourceId} will delete the resources content
There are a couple of getting started guides here. They use Spring Content for the Filesystem but the modules are interchangeable. The JPA reference guide is here. And there is a tutorial video here.
HTH

Related

Resharp DI and DNS resolution

I'm upgrading to version 107 restsharp and i'm wondering if both these options below are ok ways of doing dependency injection in dotnet with restsharp.
The documents say don't create a new restsharp for every request (connection pool exhaustion resaons) but if the httpclient is injected via the constructor will I be get the benefit of DNS resolution changes even though rest client is contained in transient scoped object (i think that's what AddHttpClient will do). I know there will be some price to pay for creating a transient object, but for a general purpose business app maybe that's fine?
Recommended way as described in the documentation
services.AddSingleton<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient()
{
_restClient = new RestClient();
}
}
OR: is it ok to do this?
services.AddHttpClient<IMyClient, MyClient>();
public class MyClient : IMyClient
{
protected readonly RestClient _restClient;
public MyClient(HttpClient httpClient)
{
_restClient = new RestClient(httpClient);
}
}
You should AddSingleton, not AddHttpClient as per official documentation:
https://restsharp.dev/v107/#restclient-lifecycle
If you use a dependency-injection container, register your API client as a singleton.
I believe it's becouse RestClient is managing the connection pools and addressing known issues, that AddHttpClient would typically address.
If you would use HttpClient directly, you should follow Microsofts recommendations from below URL, where you have a choice of Singleton or AddHttpClient:
https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
Also, not sure how your 2nd option works at this point.
Also, great video explaining more about what AddHttpClient does (sets HttpClient as Transient etc):
https://www.youtube.com/watch?v=Z6Y2adsMnAA&t=335s

Different JSON (de)serialization configs on different endpoints using Spring WebFlux

My micro service needs to communicate with 2 different services over HTTP. 1 has an API contract with snake_case JSON, while the other uses camelCase. How can I configure WebFlux to deserialize and serialize JSON with a certain Jackson ObjectMapper on a set of functional endpoints, while use another one on different endpoints?
The WebFlux documentation shows how to wire in another ObjectMapper, but this applies to all the endpoints of my API. So right now either all my JSON in snake_case or in camelCase. Cant find any resource to solve this issue, but it must be doable right?
Update: to make it clear I want to configure the web server which receives the requests from other services, not the webclient for sending http requests myself. I know how to do the latter.
you can use the #JsonNaming annotation on the classes you want to serialize/deserialize and specify what type of naming strategy you want.
jackson-advanced-annotations
Okay, so this is not the cleaned up solution, I will use this solution from our library, but the basic gist of my work around looks like this:
#Controller
public class Handler {
private ObjectMapper mapper;
public Handler(#Qualifier("snakeCaseWrapper") ObjectMapper mapper) {
this.mapper = mapper;
}
Mono<ServerResponse> returnUser(final ServerRequest request) {
//REQUEST DESERIALIZATION
var messageReader = new DecoderHttpMessageReader<>(new Jackson2JsonDecoder(mapper));
var configuredRequest = ServerRequest.create(request.exchange(), List.of(messageReader));
//RESPONSE SERIALIZATION
return configuredRequest.bodyToMono(UserDto.class)
.map(userDto -> {
try {
return mapper.writeValueAsString(userDto);
} catch (JsonProcessingException e) {
e.printStackTrace();
//properly handle the error here
return "";
}
})
.flatMap(json -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(json))
);
}
}
This is the only way I could find to programatically choose which kind of ObjectMapper I want to use for a specific endpoint/handler method for request deserialization. For response serialization, the trick was to first use the ObjectMapper to serialize the response body to a String, and put that String into the response with BodyInserters.fromObject(json) .
It works, so I'm happy with it.

How to implement customized authentication in Spring Boot Application

I am building a web app with Spring Boot. Post requests can be made by a phone app to upload data in form of xml to the cloud. The phones that are allowed to push data are required to be registered company phones. The way to authenticate the APIs calls is to look up the android ID of the phone in a corporate database. It will accept the data only if the Android ID exists. The idea is to embed the android ID in the header of requests. Since it is not a typical way for authentication, how do I implement it with Spring Security? Or we don't even need Spring Security. Just extract the Android ID from the header and look it up in database. Reject the request if it is not a valid ID. Any advice would help.
Nothing prevents you from using Authorization header in a creative way, i.e., by embedding the Android ID into it. Then, in order to add authentication to your endpoints, you can use an AOP interceptor:
Protected operation marker interface:
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ProtectedOperation {
}
Interceptor:
#Aspect
#Component
public class SecurityAspect {
private CorporateService corpService; // this is your custom service to check Android IDs
#Autowired
public SecurityAspect(CorporateService corpService) {
this.corpService = corpService;
}
#Around("#annotation(operation)")
public Object protectedOperationPermissionCheck(final ProceedingJoinPoint pjp, final ProtectedOperation operation) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
String header = requestAttributes.getRequest().getHeader("Authorization");
String androidId = // get the ID from header - try not to use existing authorization header formats like Bearer, Negotiate etc. to avoid collision with other authentication systems
if (corpService.isAuthorized(androidId)) {
return pjp.proceed();
}
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.flushBuffer();
return null;
}
}
Make sure to add the spring-boot-starter-aop dependency to your pom.xml, for #Aspect support
EDIT: to protect an endpoint, annotate the endpoint method in your controller with #ProtectedOperation, and add #EnableAspectJAutoProxy to your Spring Boot application

Spring data rest. How to take repository class through request URI

Can anyone help me please.
I have a few entities and repositories inside my app which is based on spring data rest.
Right now I wrote my own HandlerInterceptor implementation to do preHandling each request and I have to know which repository will use for each http request.
Ofc I can make separate Interceptor for each repository but this solution is not flexible Any ideas?
Thx for advance
Repositories are resolved in spring data-rest using the Repositories class.
The repositories class exposes a helper method (getRepositoryFor(Class<?>)) for finding the repository for a given class.
You can use the following snippet for finding a repository for a given class in your interceptor:
Repositories repositories = new Repositories(appContext);
repositories.getRepositoryFor(entityObject.getClass());
A more elegant solution would be to take advantage of the built-in spring-data-rest repository lookup implementation with a custom controller(RootResourceInformationHandlerMethodArgumentResolver)
For this you just need to add a RootResourceInformation parameter to a RepositoryRestController endpoint method.
#RepositoryRestController
#RequestMapping("/customName")
public class RepositoryExportController {
#Autowired
private ApplicationContext appContext;
#RequestMapping(method = RequestMethod.GET, value = "{repository}",
produces = MediaTypes.HAL_JSON_VALUE)
#ResponseBody
public Resources<Resource<?>> export(RootResourceInformation resourceInformation, ...) {
Repositories repositories = new Repositories(appContext);
CrudRepository repo=(CrudRepository)repositories.getRepositoryFor(resourceInformation.getDomainType());
repo.findAll();
...other logic....
}

Content-Type case sensitivity in RESTEasy 3

I am developing a RestEasy client to connect to a 3rd party REST service which has defined its own custom media types. A made up example is
application/vnd.abc.thirdPartyThing-v1+json
Note the uppercase P in thirdParty.
I am using RESTEasy 3.0.11 for my client implementation. At the point where I make a POST call to the service my code looks like
Response response = target.request()
.post(Entity.<ThirdPartyThing>entity(
thing,
"application/vnd.abc.thirdPartyThing-v1+json"));
but RESTEasy sends to the server
Content-Type: application/vnd.abc.thirdpartything-v1+json
This is due to RESTEasy's MediaTypeHeaderDelegate class's toString() method, which lowercases the type and subtype MediaTypeHeaderDelegate. This should be correct, or at least unimportant, as RFC-1341 states that Content-Type values are case-insensitive - RFC-1341
Unfortunately the 3rd party service is checking the Content-Type in a case sensitive manner and so returning a 415 UNSUPPORTED MEDIA TYPE error. I've tested using curl which doesn't alter the content-type value and confirmed that it's a case issue. application/vnd.abc.thirdPartyThing-v1+json works, application/vnd.abc.thirdpartything-v1+json does not.
I'm in the process of raising a ticket, but in the meantime is there any way to override RESTEasy's default behaviour and send Content-Type headers without lowercasing the value?
Thanks for reading.
I could reproduce this behavior with RESTeasy 3.0.6.Final and would not expect it. Maybe you could check their JIRA if this has already been discussed or open an issue. I once had problems on the server side because a 2.x version of RESTeasy was checking the charset attribute of the Content-Type header case-sensitive. This was also changed.
You could solve this problem by a really ugly workaround: Overwrite the header again in a ClientRequestFilter.
public class ContentTypeFilter implements ClientRequestFilter {
private Map<String, String> contentTypes;
public ContentTypeFilter() {
contentTypes = new HashMap<>();
contentTypes.put("text/foo", "text/Foo");
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
String contentType = requestContext.getHeaderString("Content-Type");
if (contentTypes.containsKey(contentType)) {
requestContext.getHeaders().putSingle("Content-Type", contentTypes.get(contentType));
}
}
}
Don't forget to register this Filter:
Client client = ClientBuilder.newClient().register(ContentTypeFilter.class);