How can I receive multiple files with respective metadata with my spring controller in one request? - spring

I am developing a java spring webApp with a typescript frontend and want to send several files with some additional informations which were added by the user in the frontend to the backend (with one request). So far I managed to implement the functionality such that I can send a single file and additional information in one request which then gets mapped into a respective object in the backend.
In the code below I would like to implement functionality such that all elements of the attachments array are sent in one go. Preferably the controller in the backend should directly map them to an array of the respective QuoteAttachment object.
My frontend DTO:
export type QuoteAttachment = {
file: File,
uploadedOn: Date,
};
My frontend post request:
async createAttachment(attachments: QuoteAttachment[], quoteReference: string) {
let formData: FormData = new FormData();
formData.append('file', attachments[0].file);
formData.append('uploadedOn', attachments[0].uploadedOn.toLocaleString());
formData.append('relatedQuote', quoteReference);
await this.http.performRequest('/attachment', {
method: HttpRequestMethod.Post,
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
My backend DTO:
#Getter
#Setter
public class QuoteAttachment {
private MultipartFile file;
private String uploadedOn;
private String relatedQuote;
}
My backend controller:
#Controller
#RequiredArgsConstructor
#RequestMapping("/attachment")
public class AttachmentController {
private final AttachmentService attachmentService;
private final AttachmentValidationService attachmentValidationService;
#ResponseBody
#PostMapping
public void uploadFileWithInfos(#ModelAttribute final QuoteAttachment quoteAttachment) {
System.out.println("Test");
}
}
I really played around a lot and tried many different variations but didn't arrive at a good solution. I hope someone has a good idea for an implementation solving the problem.

I think there is a lot of solutions you can do for this:-
solution:
you can send the file as file in angular and as a Multipart in Spring boot, beside the QuoteAttachment DTO but as a String of JSON (NOT normal JSON). Example answer mentioned here in this URL then in java after receiving a String not a QuoteAttachment model you can map that string to a model with this simple code
ObjectMapper objectMapper = new ObjectMapper();
QuoteAttachment quoteAttachment = objectMapper.readValue(quoteAttachmentAsString, QuoteAttachment.class);
Note: ObjecMapper imported from com.fasterxml.jackson.databind library.
solution:
Also you can add all your QuoteAttachment DTO properties to formData in angular and receive them as #RequestParam in java. Example answer here
My preferred solution is number 1.

Related

Implementing JSR-303 validation with crnk JSON API

I am fairly familiar with the JSR-303's #Valid annotation and have used it a couple of times in my #Controller classes. For example:
#PostMapping("/users")
ResponseEntity<String> addUser(#Valid #RequestBody User user) {
// persisting the user
return ResponseEntity.ok("User is valid");
}
Where User object is a typical class with annotations like #NotBlank or #NotNull on the fields.
However, I am trying to build a REST API based on JSON API using the crnk library and am trying to do the same validation, example:
#Override
public Subscription save(#Valid Subscription subscription) {
// code goes here
}
Unfortunately the validation doesn't work and I have tried both #Valid and #Validation.
Can anyone kindly show what is wrong with this code?
Thanks

How to update image using image url

I have method which is taking multipart image file,if i want to update same image then obviously i have to take image url as a input but i cant able to take the input as url since it is taking file format
my method:
MediaType.APPLICATION_OCTET_STREAM_VALUE}, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ApiResponse> updatePersonalDataForUser(
#RequestHeader("accessToken") #NotEmpty(message = "accessToken is mandatory") String bearer,
#RequestHeader("mappingId") #NotEmpty(message = "mappingId is mandatory") String mappingId,
#RequestPart("personalInfoObj") String personalInfoObj,
#RequestPart(value = "profileImage") MultipartFile profileImage)
throws IOException {
jobPostController.userRoleAuthorization(mappingId);
userController.oAuthByRedisAccessToken(bearer, mappingId);
ObjectMapper objectMapper = new ObjectMapper();
PersonalInfoResponse personalInfoConv = objectMapper.readValue(personalInfoObj, PersonalInfoResponse.class);
return userController.updatePersonalData(mappingId, personalInfoConv, profileImage, Contants.UserRoleName);
}```
You should take a look at the Spring community project called Spring Content.
This project makes it easy to build contentful applications and services. It has the same programming model as Spring Data. Meaning it can supply implementations for the file storage and REST controllers on top of that file storage, therefore you don't need to concern yourself with creating these yourself. It is to content (or unstructured data) what Spring Data is to structured data.
This might look something like the following:-
pom.xml (for Spring Web MVC. Spring Boot also supported)
<!-- Spring Web MVC dependencies -->
...
<!-- Java API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-fs</artifactId>
<version>1.0.0.M5</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>1.0.0.M5</version>
</dependency>
StoreConfig.java
#Configuration
#EnableFilesystemStores
#Import(RestConfiguration.class)
public class EnableFilesystemStoresConfig {
#Bean
File filesystemRoot() {
try {
return new File("/path/to/your/uploaded/files");
} catch (IOException ioe) {}
return null;
}
#Bean
FileSystemResourceLoader fileSystemResourceLoader() {
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
}
ImageStore.java
#StoreRestResource(path="images")
public interface ImageStore extends Store<String> {
}
This is all you need to do to get REST Endpoints that will allow you to store and retrieve files. As mentioned how this actually works is very much like Spring Data. When your application starts Spring Content will see the spring-content-fs dependency, know that you want to store content on your filesystem and inject a filesystem implementation of the ImageStore interface into the application context. It will also see the spring-content-rest and inject a controller (i.e. REST endpoints) that talk to the ImageStore interface. Therefore, you don't have to do any of this yourself.
So, for example:
curl -X POST /images/myimage.jpg -F "file=#/path/to/myimage.jpg"
will store the image on the filesystem at /path/to/your/uploaded/files/myimage.jpg
And:
curl /images/myimage.jpg
will fetch it again and so on...these endpoints support full CRUD and the GET & PUT endpoints also support video streaming (or byte range-requests).
You could also decide to store the contents elsewhere like in the database with your entities, or in S3 by swapping the spring-content-fs dependency for the appropriate Spring Content Storage module. Examples for every type of storage are here.
In addition, in case it is helpful, often content is associated with Spring Data Entities. So, it is also possible to have the ImageStore interface implement ContentStore, like this:
> FileStore.java
#StoreRestResource(path="images")
public interface ImageStore extends ContentStore<PersonalInfo, String> {
}
And to add Spring Content-annotated fields to your Spring Data entities, like this:
> PersonalInfo.java
#Entity
public class PersonalInfo {
#Id
#GeneratedValue
private long id;
...other existing fields...
#ContentId
private String contentId;
#ContentLength
private long contentLength = 0L;
#MimeType
private String mimeType = "text/plain";
...
}
This approach changes the REST endpoints as the content is now addressable via the Spring Data URL. So:
POST /personalInfos/{personalInfoId} -F "image=#/some/path/to/myimage.jpg"
will upload myimage.jpg to /path/to/your/uploaded/files/myimage.jpg. As it did before but it will also update the fields on the PersonalInfo entity with id personalInfoId.
GET /personalInfos/{personalInfoId}
will get it again.
HTH

Spring's RestTemplate: complex object to query params

I have a complex object like this:
public class ComplexObject {
private String a;
private String b;
...
private String z;
//getters and setters
}
I want to call a web service that receives all the complex object fields: http://localhost:8080/api/some_service?a=something&b=something&...&z=something
Is there any way to pass a ComplexObject to RestTemplate and have the work done automatically or I have to do the manual mapping by myself?
Thanks!
YES! there is a way to pass complete complex object to make the service call and then for sure it can be achieved automatically.
And for this you have to alter the way you send this complexObject and have to use HTTP POST (highly recommended ), as:
public HttpStatus send()
{
ComplexObject complexObj = getYourFilledObject();
ResponseEntity<HttpStatus> response = restTemplate.postForEntity(ROOT_URI, complexObj, HttpStatus.class);
return response;
}
And if not and GET is the only option then unfortunately you have to send as you’re. Because at the end of the day either you use rest templates ‘s function which intake params map or you create your own URI with params, it is the same HTTP GET and you have to achieve programmatically.
For examples & illustration you can visit here and best reference will be spring resttemplate doc

How to auto generate response fields that do not have POJO

We have a service that simply returns the json document on a GET request. Since we do not have the POJO for the response "model", it appears we won't be able to use the auto response fields generation "goodness".
One option for us is to create the Pojos (quite large, about 50 attributes) and a corresponding controller that uses the pojos. This is awkward as we now have to maintain the model and corresponding controller just so we can auto generate the model.
Any ideas on how we can still leverage some auto generation of the response fields would be greatly appreciated.
Here's the controller I'm referring to:
#RestController
#RequestMapping("/api")
public class ProductController {
#Autowired
ProductService productService;
#RequestMapping(value = { "/products/{ids}" }, method = { RequestMethod.GET },
produces = "application/json", headers={"accept=application/json"})
#Timed
#ExceptionMetered
#LogExecutionTime
public String getProductDetails(#PathVariable("id") String id) {
return productService.getProductDetails(id);
}
At the moment I see no way of leveraging the auto generation without putting additional effort into it. Spring Auto REST Docs works by inspecting POJOs with a Jackson visitor (static introspection without runtime information) and there is currently no way of deriving the JSON fields from a string (would be dynamic at runtime). Thus, I only see two options:
The approach that you already described: Creating the corresponding POJO and using it.
Using Spring REST Docs for the corresponding test and manually document each field in the test. Might be the better option here if you do not want to alter the production code.

From request object to the database

I have an app with an AngularJS front-end and a Spring MVC back-end. I'm having some trouble with converting/mapping request objects to domain/dto objects.
On one page you can add a new order to the system, the POST payload would look something like this:
{
memo: "This is some extra info for order",
orderLines: [{productId:3, quantity:4}, {productId:2, quantity:5}, {productId:1, quantity:4}],
shippingDate: "2014-10-08T19:16:19.947Z",
warehouseId: 2
}
The Spring MVC controller method looks like this:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ResponseEntity<Void> addOrder(#RequestBody #Valid OrderRequest orderRequest, UriComponentsBuilder b) throws Exception {
// the magic
}
Where OrderRequest is filled with the values of the POST request, the OrderRequest and OrderLineRequest look like this:
public class OrderRequest {
private Long id;
private Date shippingDate;
private String memo;
private List<OrderLineRequest> orderLines;
private Long warehouseId;
public OrderRequest() {
}
// getters and setters ommitted
}
public class OrderLineRequest {
private Long id;
private String productCode;
private int quantity;
public OrderLineRequest() {
}
}
My question now is, in order to save an Order object with orderService.add(order) I need to construct the Order object based on the values that were sent in the request. Where/how do I do this?
OPTION 1
The OrderRequest class could have a makeOrder() method with just returns an Order object like so:
public Order makeOrder() {
Order order = new Order();
order.setMemo(this.memo);
order.setShippingDate(this.shippingDate);
...
}
Then I'd have to map the OrderLineRequest which could have their own makeOrderLine method:
public OrderLine makeOrderLine() {
OrderLine orderLine = new OrderLine();
orderLine.setQuantity = this.quantity;
...what to do with only the productId?
}
As you can see I can set the quantity but in the request I only received the productId, but in the database I save the productCode, productName as well, so I need that info from the database, but I don't want to make a database call from the Request object...I also don't want to half of the mapping in the request object and the rest of the mapping in the controller where I do have access to the services.
OPTION 2
I can use Dozer to do the mapping for me, but that would mean injecting the services into the Dozer custom converters which seem equally unclean to me...
OPTION 3
I pass the OrderRequest object to the service layer and let the service layer handle it, but my question would remain, how exactly would the service layer convert it, say you have the method addOrder like this:
public void addOrder(OrderRequest orderRequest) {
}
Would you call another service to convert from one to the other as I don't really want this conversion in a business logic method?
Any help would be appreciated
use the #RequestBody to map your jsonObject that is send with the request , to a DTO .
please refer to the following tutorial .
hope that helps .
and please ask if there is something not clear .

Resources