Multipart Request bind Form(#ModelAttribute) in Spring Boot Rest API - rest

In Spring Boot2 REST, can a Multipart Request be bound to a Form?
After executing the code below, the Multipart variable of Form is null.
Form Class:
public class UploadFrom implements Serializable {
#Data
private MultipartFile uploadFile;
}
Rest Controller:
#RestController
public class UploadController {
#PostMapping("/upload")
public void uploadFile(#ModelAttribute UploadForm form){
System.out.println(form.getUploadFile()); // --> null!!
}
}

You can do it in a different way, by using two RequestPart(MultiPart) one for UploadForm and second for File. Like this :
#PostMapping("/upload")
public void uploadFile(#RequestPart("form") UploadForm form,
#RequestPart("file") MultipartFile file,){
System.out.println(file);
}

Related

Spring Boot o.s.web.servlet.pagenotfound

I am trying to call spring-boot rest controller but it throws o.s.web.servlet.pagenotfound spring boot, I have seen too many answers here but none of these helped me.
controller class is as below
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private UsersRepository usersRepository;
#GetMapping("/users")
public List<Users> getAllUsers() {
return usersRepository.findAll();
}
}
And application.yml is as below
spring.datasource.url = jdbc:mysql://localhost:3306/mydb?useSSL=false
spring.datasource.username=mine
spring.datasource.password=mine
kindly let me know if any further information required
I had the same problem, there should be a problem with your URL. You must be hitting the wrong URL.
I'm assuming you are using Postman for testing the GET Request.
Check if you are hitting through GET Request, and the format is JSON.
And try the below:
#RestController
#RequestMapping(value = "/")
public class UsersController {
#Autowired
private UsersRepository usersRepository;
#GetMapping("/users")
public List<Users> getAllUsers() {
return usersRepository.findAll();
}
}

Implementing RequestMethod.PATCH in Spring RestController

I am creating a Rest API for a MongoDB database using MongoRepository. I want to create an endpoint that uses "RequestMethod.PATCH" and implements the "PATCH" functionality: delta update with fields provided in the #RequestBody.
The functionality that I want already exists in "Spring Data Rest" by using the "#RepositoryRestResource" annotation on my Repository class as described here https://spring.io/guides/gs/accessing-data-rest/
But I don't want to expose my Repository class like that. I like the classic Controller->Service->Repository lineage. My controller looks like this:
#RestController
public class ActivitiesController {
#Autowired
ActivitiesService activitiesService;
#RequestMapping(value="activities", method=RequestMethod.PATCH)
public ActivityModel updateActivity(
#RequestBody ActivityModel activityModel
){
//Input ActivityModel will only have subset of fields that have been changed, aka the delta
return activitiesService.update(activityModel);
}
#RequestMapping(value="activities", method=RequestMethod.PUT)
public ActivityModel updateActivity(
#RequestBody ActivityModel activityModel
){
//Input ActivityModel will have all fields populated
return activitiesService.save(activityModel);
}
}
And my repository is here:
#Repository
public interface ActivitiesRepo extends MongoRepository<ActivityModel, String> {
//out of the box implementation
}
My problem is that, from what I can tell, MongoRepository does not provide delta updates out of the box the way that Spring Data Rest does. How can I implement that functionality in the Service layer here?:
#Service
public class ActivitiesService {
#Autowired
ActivitiesRepo activitiesRepo;
public ActivityModel update(ActivityModel activityModel){
//delta update implementation, aka PATCH implementation
}
//method that should only be used with RequestMethod.PUT
public ActivityModel save(ActivityModel activityModel){
return activitiesRepo.save(activityModel);
}
}
What do you think of this solution for a manual "PATCH" implementation:
public class ModelUtil {
public static <T> Object update(Object origModel, Object dirtyModel, Class<T> clazz){
ObjectMapper m = new ObjectMapper();
HashMap<String, Object> origModelAsMap = m.convertValue(origModel, new TypeReference<Map<String, Object>>() {});
HashMap<String, Object> dirtyModelAsMap = m.convertValue(dirtyModel, new TypeReference<Map<String, Object>>() {});
dirtyModelAsMap.forEach((k, v)-> {
origModelAsMap.put(k, v);
});
return m.convertValue(origModelAsMap, clazz);
}
}

Problems when using EntityFilteringFeature and SelectableEntityFilteringFeature with Jersey 2

I'm new to Jersey 2 and JAX-RS, so probably I'm missing something.
What I'm trying to do is a test program to define a coding style in rest services developing.
The test was written in JAVA and uses JERSEY 2.22.2, JDK 1.8.31, MOXY AS JSON Provider.
I defined a Resource with GET methods to support LIST/DETAIL. Due to the size of my POJO, I used some filters and everything was fine.
// 1) First of all I defined the annotation.
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#EntityFiltering
public #interface MyDetailView {
public static class Factory extends AnnotationLiteral<MyDetailView>
implements MyDetailView {
private Factory() {
}
public static MyDetailView get() {
return new Factory();
}
}
// 2) Once defined the annotation, I used to
// programmaticaly exclude the list of subItems in the response...
#XmlRootElement
public class MyPojo {
...
//*** THIS SHOULD BE FILTERED IF THE ANNOTATION IS NOT SPECIFIED IN THE RESPONSE ***
#MyDetailView
private List<SubItem> subItems = new ArrayList<SubItem>();
public List<SubItem> getSubItems() {
return subItems;
}
public void setSubItems(List<SubItem> subItems) {
this.subItems = subItems;
}
}
// 3) I registered the EntityFilteringFeature
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
}
// 4) Finally, I wrote the code to include/exclude the subItems
/*
The Resource class has getCollection() and getItem() methods...
getCollection() adds the annotation only if filterStyle="detail"
getItem() always add the annotation
*/
#Path(....)
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource extends SecuredResource {
//filterStyle -> "detail" means MyDetailAnnotation
#GET
public Response getCollection(
#QueryParam("filterStyle") String filterStyle,
#Context UriInfo uriInfo) {
//THIS CODE AFFECTS THE RESPONSE
boolean detailedResponse = "detail".equals(filterStyle);
Annotation[] responseAnnotations = detailedResponse
? new Annotation[0]
: new Annotation[]{MyDetailView.Factory.get()};
//pojo collection...
MyPagedCollection myCollection = new MyPagedCollection();
//.....
ResponseBuilder builder = Response.ok();
return builder.entity(myCollection, responseAnnotations).build();
}
#GET
#Path("/{id}")
public Response getItem(#PathParam("{id}") String idS, #Context UriInfo uriInfo) {
MyPOJO pojo = ...
Annotation[] responseAnnotations = new Annotation[]{MyDetailView.Factory.get()};
return Response.ok().entity(pojo, responseAnnotations).build();
}
}
After the first test, I tried to use the SelectableEntityFilteringFeature to allow the client to ask for specific fields in the detail, so I changed the ApplicationConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
register(SelectableEntityFilteringFeature.class);
property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
and I've add the "fields" QueryParam to the Resource getItem() method...
#GET
#Path("/{id}")
public Response getDetail(#PathParam({id}) String id,
#QueryParam("fields") String fields,
#Context UriInfo uriInfo) {
....
But as long as I registered the SelectableEntityFilteringFeature class, the EntityFilteringFeature class stopped working. I tried to add "fields" parameter to one of the Resource methods, it worked perfectly. But the MyDetailAnnotation was completely useless.
I tried to register it using a DynamicFeature
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if ("MyResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getItem".equals(resourceInfo.getResourceMethod().getName())) {
//*** IS THE CORRECT WAY TO BIND A FEATURE TO A METHOD? ***
//
context.register(SelectableEntityFilteringFeature.class);
context.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
}
Now the questions:
1) Why registering both the SelectableEntityFilteringFeature feature breaks the EntityFilteringFeature?
2) What is the correct way to bind a feature to a method with the DynamicFeature interface?
Thanks in advance.
This is my first post to Stack Overflow, I hope it was written complaining the rules.
Short answer: you can't. It appears to be a bug as of 2.25.1 and up to 2.26(that I tested with). https://github.com/jersey/jersey/issues/3523
SelectableEntityFilteringFeature implictily registers EntityFilteringFeature (As mentioned here). So I don't see a need to add this.
Since you need Annotation based filtering, you can exclude registering SelectableEntityFilteringFeature.
You can just do,
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {MyDetailView.Factory.get()})
// Register the EntityFilteringFeature.
.register(EntityFilteringFeature.class)
// Further configuration of ResourceConfig.
You can refer to this example for usage and this example for registering the filter.
So you can remove SelectableEntityFilteringFeature and try just the above mentioned way to register it.

Spring Java config message convertor priority

I have defined two convertors like this using Spring Java config. I always get a XML response unless I specified the 'Accept=applicaiton/json' in the HTTP header. Is there a way to set the default convertor to be JSON instead of XML convertor.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"foo.bar"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Bean
public MappingJackson2HttpMessageConverter jsonConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Bean
public MappingJackson2XmlHttpMessageConverter xmlConverter() {
MappingJackson2XmlHttpMessageConverter xmlConverter = new MappingJackson2XmlHttpMessageConverter();
return xmlConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jsonConverter());
converters.add(xmlConverter());
super.configureMessageConverters(converters);
}
Here is my controller.
#RequestMapping(value = "/product")
public
#ResponseBody
BSONObject getProducts(#RequestParam String ids,
#RequestParam(required = false) String types) {
List<BSONObject> products = commonDataService.getData(ids, types);
return products;
}
Try the following configuration, it sets up the default Content negotiation strategy(based on article here):
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Another option will be to provide other ways of specifying the content format, if Accept header is not feasible, an option could be to specify an extension /myuri/sample.json which would be returned as a json.

POST doesn't send data

I'm using Angularjs and spring mvc 3. I have in my controller class:
#Controller
#RequestMapping(value = "/elprocesses")
public class ELProcessController {
...
#RequestMapping(value = "/elprocess", method = RequestMethod.POST)
public #ResponseBody void save(#RequestBody final Entity01 entity01,
#RequestBody final Entity02 entity02
) {
...
}
ELProcessController.js :
$scope.saveForm = function(selectedname01) {
$http.post('elprocesses/elprocess', {entity01:selectedname01, entity02:selectedname02});
...
}
it doesn't enter in my spring controller method, but when I send only one data with $http.post('elprocesses/elprocess', selectedname01);
and changing my controller class with:
#RequestMapping(value = "/elprocess", method = RequestMethod.POST)
public #ResponseBody void save(#RequestBody final Entity01 entity01)
this works fine,
What am I doing wrong to send entity01 and entity02?
In your javascript, is selectedname02 defined anywhere?
If it is, then open up your network tab and you'll see whether or not it's sending data. The POST request has the header Content-Type: application/json by default though so make sure you're trying to get json data and not form encoded data or something. I'm not familiar with spring mvc at all so check their docs.