how to get list of objects via requestbody in spring boot api - spring

To get list of objects via #RequestBody in controller and process each object in a list to do a business logic.
I have tried this but not working
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody List<BarberServiceType> serviceTypes,final HttpServletResponse response){
also tried following:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceType[] serviceTypes,final HttpServletResponse response){

Below works for me
#RequestMapping(value = "/payments", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List<Payment> batchCreate(#RequestBody List<Payment> payments) {
return paymentService.create(payments);
}
You will need Jackson in the class path
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
Json in put is
[{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]

You should use a wrapper class for your required list,
in another word, create a new class and name it "BarverServiceTypeRequest"
this class should contain your List:
public class BarberServiceTypeRequest{
private List<BarberServiceType> serviceTypes;
public List<BarberServiceType> getserviceTypes() {
return serviceTypes;
}
public void setServiceTypes(List<BarberServiceType>serviceTypes) {
this.date = date;
}
then your controller will be like:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceTypeRequest request, final HttpServletResponse response){
Your JSON object will be like:
{
"serviceTypes": [{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]
}
sure you can access your list with:
request.getserviceTypes();

Related

Define a bean from external library

I have a poblem.
I am working with api binance and I add this library to use all functionalities.
<dependency>
<groupId>io.github.binance</groupId>
<artifactId>binance-connector-java</artifactId>
<version>1.3.0</version>
</dependency>
This is the code of external library
public class Market {
private final String baseUrl;
private final RequestHandler requestHandler;
private final boolean showLimitUsage;
public Market(String baseUrl, String apiKey, boolean showLimitUsage) {
this.baseUrl = baseUrl;
this.requestHandler = new RequestHandler(apiKey);
this.showLimitUsage = showLimitUsage;
}
When I try to inject this classe I always receive the following error on SpringBoot
Consider defining a bean of type 'com.binance.connector.client.impl.spot.Market' in your configuration.
I have an controller and I use like this.
#RestController
public class ExampleController {
#Autowired
private Market market;
#RequestMapping(value = "/ping", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> ping() {
return ResponseEntity.ok(market.ping());
}
}
I try to add the class with ComponentScan.
The code of external library is only for read.

Spring #RestController produces XML without namespaces

I have a #RestController which should return a result from a SOAP web service. The web service client classes are generated with maven-jaxb2-plugin and therefore using JAXB annotations.
#RestController
public class ZemisPersonSearchController {
#Autowired(required = true)
private SoapClient soapClient;
#RequestMapping(path = "/api/persons/{no}", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_XML_VALUE })
#PreAuthorize("hasRole('ROLE_GET_PERSON_DETAILS')")
public ResponseEntity<Object> getPersonDetails(HttpServletRequest httpReq, #PathVariable String no) {
Result result = soapClient.getPersonDetails(UUID.randomUUID().toString(), no);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"responseHeader",
"getPersonDetailsResponse",
"searchPersonResponse",
"systemException"
})
#XmlRootElement(name = "result")
public class Result {
#XmlElement(name = "ResponseHeader")
protected ResponseHeaderType responseHeader;
#XmlElement(name = "GetPersonDetailsResponse")
protected PersonType getPersonDetailsResponse;
#XmlElement(name = "SearchPersonResponse")
protected SearchPersonResponseType searchPersonResponse;
#XmlElement(name = "SystemException")
protected FaultInfoType systemException;
...
As long as all works as expected, the result looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:result
xmlns:ns2="http://mynamespace/personsearchservice/v1">
<ns2:ResponseHeader>
...
But if there goes something wrong (i.e. soap endpoint isn't available) and an excpetion is thrown, the REST controller returns an 406 http status since the automatically generated response cannot be transformed to XML.
I've tried to extend my application with Jackson XML and registered the module to process JAXB annotations as suggested in documentations and blogs I found.
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
#Bean
public Module jaxbModule() {
return new JaxbAnnotationModule();
}
But if I do so, the error for exceptions can now be generated as XML and I got the correct http status 500, but also the response when no error occurs contains no longer namespaces and it is important to keep the namespaces since it is a big and complex xml:
<result>
<ResponseHeader>
Does anybody have an idea what I have to do to get either the namespaces with jackson or the error transformed to xml with JAXB?
I found out, that spring creates a LinkedHashMap with error details automatically. When this map should be converted to html without jackson-xml on classpath, the http status 406 is given back due to a missing converter for the LinkedHashMap to html. So my solution is to add a simple AbstractHttpMessageConverter to my application which converts the map with error details to html. Therefore I don't need jackson-xml on the classpath and my XML is generated from JAXB with namespaces included.
The converter:
public class HttpXmlExceptionConverter extends AbstractHttpMessageConverter<Map<String, Object>> {
public HttpXmlExceptionConverter() {
super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml"));
}
#Override
protected boolean supports(Class<?> clazz) {
return Map.class.isAssignableFrom(clazz);
}
#Override
protected Map readInternal(Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
throw new NotImplementedException("readFromSource is not supported!");
}
#Override
protected void writeInternal(Map<String, Object> map, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
OutputStream s = outputMessage.getBody();
StringBuilder sb = new StringBuilder();
sb.append("<map>");
for (Entry<String, Object> entry : map.entrySet()) {
sb.append("<").append(entry.getKey()).append(">");
if (entry.getValue() != null)
sb.append(StringEscapeUtils.escapeXml(entry.getValue().toString()));
sb.append("</").append(entry.getKey()).append(">");
}
sb.append("</map>");
s.write(sb.toString().getBytes(Charset.defaultCharset()));
}
}
And register the converter:
#Configuration
//#EnableWebMvc
// -> EnableWebMvc is required if you don't want the spring boot auto configuration should be extended
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new HttpXmlExceptionConverter());
}
}

Swagger configured in Spring Boot shows only methods with POST and GET mapping

Swagger configured in Spring Boot shows only one method with POST mapping and one method with GET mapping from every controller. Swagger ignores another methods with GET and POST mapping and ignores all methods with PUT and DELETE mappings. My configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.project.controllers"))
.paths(PathSelectors.ant("/api/*"))
.build();
}
}
Dependency in pom.xml:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
My controllers code:
#RestController
#RequestMapping(value = "/api/users", produces = "application/json; charset=UTF-8")
public class UserController {
#Autowired
private UserService userService;
protected UserService getService() {
return userService;
}
#RequestMapping(method = GET)
public Page<User> query(#RequestParam Map<String, Object> parameters, Pageable pageable) {
return getService().query(parameters, pageable);
}
#ResponseStatus(CREATED)
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<User> create(#RequestBody User entity) {
return ResponseEntity.status(HttpStatus.CREATED).body(getService().create(entity));
}
#RequestMapping(value = "/{id:[0-9]+}", method = RequestMethod.PUT)
public ResponseEntity<User> update(#PathVariable Long id, #RequestBody User entity) {
return ResponseEntity.ok(getService().update(id, entity));
}
#RequestMapping("/current")
public ResponseEntity current() {
return ResponseEntity.ok(userService.getUser());
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/enable", method = RequestMethod.POST)
public void enable(#PathVariable("id") final long id) {
userService.enable(id);
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/disable", method = RequestMethod.POST)
public void disable(#PathVariable("id") final long id) {
userService.disable(id);
}
#RequestMapping(value = "/histories", method = RequestMethod.GET)
public List<UserHistory> histories() {
return userService.histories();
}
}
May be i need add some more configuration or add something else?
Based on your controller, I think you should add one more star in the path matcher in your swagger config:
.paths(PathSelectors.ant("/api/**"))
e.g /api/users/current would not be matched by the /api/* but by /api/**, and this why you are getting only the base path endpoints documented.

jersey #NotNull doesn't check on input

I have a jersey REST service, and I use #NotNull to check on the #pathparam, but it seems not working.I've include jersey-bean-validation in pom.xml
my code is here:
#Path("/resource")
public class MyResource extends AbstractResource {
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
public abstract class AbstractResource {
..
protected void validateOrganization(#NotNull(message = "{org.string.null}") #Valid String organizationId) throws Exception {
...
}
Ensure you are using the jersey-bean-validation dependency:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.22.2</version>
</dependency>
According to the documentation, having the above mentioned dependency on the classpath will activate the Bean Validation feature on Jersey.
The question is super-old, so you have probably already found an answer...
But you are missing the #Valid annotation on the parameter.
Try this
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(#javax.validation.Valid MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
}

Spring Framework TEST RESTful Web Service (Controller) Offline i.e. No Server, No Database

I have a very simple RESTful Controller that consumes and produces JSON. I need to test this controller offline i.e. no server running, no database running. And I am going nuts for not being able to find a solution. My intial test cases will include:
Test REST URIs i.e. GET, POST, PUT, DELETE - I must be able to Assert data returned against data sent.
Assert will test JSON data
I have the following URIs:
/pcusers - Returns all users
/pcusers/{id} - Return a specific user
/pcusers/create/{pcuser} - Add user to db
/pcusers/update/{pcuser} - Update user
/pcusers/delete/{id} - Delete User
NOTE: This is NOT a typical MVC application. I DO NOT have Views. I have a pure REST controller that spits out JSON and consumes data in JSON format.
If someone could guide me in the right direction would be really appreciated.
Just to be clear how my code looks like:
#Controller
#RequestMapping("/pcusers")
public class PcUserController {
protected static Logger logger = Logger.getLogger(PcUserController.class);
#Resource(name = "pcUserService")
private PcUserService pcUserService;
#RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public List<PcUser> readAll() {
logger.debug("Delegating to service to return all PcUsers");
return pcUserService.readAll();
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET, consumes = "application/json", produces = "application/json")
#ResponseBody
public PcUser read(#PathVariable String id) {
logger.debug("Delegating to service to return PcUser " + id);
return pcUserService.read(id);
}
#RequestMapping(value = "/create/{pcUser}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
#ResponseBody
public boolean create(#PathVariable PcUser pcUser) {
logger.debug("Delegating to service to create new PcUser");
return pcUserService.create(pcUser);
}
#RequestMapping(value = "/update/{pcUser}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
#ResponseBody
public boolean update(#PathVariable PcUser pcUser) {
logger.debug("Delegating to service to update existing PcUser");
return pcUserService.update(pcUser);
}
#RequestMapping(value = "/delete/{id}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
#ResponseBody
public boolean delete(#PathVariable String id) {
logger.debug("Delegating to service to delete existing PcUser");
return pcUserService.delete(id);
}
}
UPDATE (2/5/2012):
After some research, I came across a Spring framework called spring-test-mvc. It looks very promising and I have managed to get a good start on this. But now I have a new problem. When I submit a GET request to "/pcusers/{id}", the control is passed to read method which is responsible for handling that mapping. Inside that method I have a pcUserService that does a read. Now, the problem is when I run this test, the pcUserService instance inside real controller is NULL; and therefore it ends up crashing as read cannot be called on a NULL object.
Here's PcUserControllerTest code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/applicationContextTest.xml")
public class PcUserControllerTest {
#Autowired
PcUserService pcUserService;
#Autowired
PcUserController pcUserController;
PcUser pcUser;
#Before
public void setUp() throws Exception {
pcUser = new PcUser("John", "Li", "Weasley", "john", "john", new DateTime());
pcUserService.create(pcUser);
}
public void tearDown() throws Exception {
pcUserService.delete(pcUser.getId());
}
#Test
public void shouldGetPcUser() throws Exception {
standaloneSetup(pcUserController)
.build()
.perform(get("/pcusers/" + pcUser.getId()).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
Here is one suggestion that should give you some ideas. I assume that you are familiar with the SpringJUnit4ClassRunner and the #ContextConfiguration. Start by creating an test application context that contains PcUserController and a mocked PcUserService. In the example PcUserControllerTest class below, Jackson is used to convert JSON messages and Mockito is used for mocking.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(/* Insert test application context here */)
public class PcUserControllerTest {
MockHttpServletRequest requestMock;
MockHttpServletResponse responseMock;
AnnotationMethodHandlerAdapter handlerAdapter;
ObjectMapper mapper;
PcUser pcUser;
#Autowired
PcUserController pcUserController;
#Autowired
PcUserService pcUserServiceMock;
#Before
public void setUp() {
requestMock = new MockHttpServletRequest();
requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
requestMock.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
responseMock = new MockHttpServletResponse();
handlerAdapter = new AnnotationMethodHandlerAdapter();
HttpMessageConverter[] messageConverters = {new MappingJacksonHttpMessageConverter()};
handlerAdapter.setMessageConverters(messageConverters);
mapper = new ObjectMapper();
pcUser = new PcUser(...);
reset(pcUserServiceMock);
}
}
Now, we have all the code needed to create the tests:
#Test
public void shouldGetUser() throws Exception {
requestMock.setMethod("GET");
requestMock.setRequestURI("/pcusers/1");
when(pcUserServiceMock.read(1)).thenReturn(pcUser);
handlerAdapter.handle(requestMock, responseMock, pcUserController);
assertThat(responseMock.getStatus(), is(HttpStatus.SC_OK));
PcUser actualPcUser = mapper.readValue(responseMock.getContentAsString(), PcUser.class);
assertThat(actualPcUser, is(pcUser));
}
#Test
public void shouldCreateUser() throws Exception {
requestMock.setMethod("POST");
requestMock.setRequestURI("/pcusers/create/1");
String jsonPcUser = mapper.writeValueAsString(pcUser);
requestMock.setContent(jsonPcUser.getBytes());
handlerAdapter.handle(requestMock, responseMock, pcUserController);
verify(pcUserServiceMock).create(pcUser);
}

Resources