Can someone please suggest and provide example on how to use the #Valid annotation for #RequestHeader Map<String, String> fields in spring boot
You can write class like below:
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.util.Map;
#Validated
public class MyHeaders {
public Map<String, String> getHeaders(#Valid #RequestHeader Map<String, String> headers) {
return headers;
}
}
below is my controller class:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
private final MyHeaders myHeaders;
public MyController(MyHeaders myHeaders) {
this.myHeaders = myHeaders;
}
#GetMapping("/")
public String hello() {
Map<String, String> headers = myHeaders.getHeaders();
// Do something with the headers
return "Hello";
}
}
Yes, we can pass the value and name from list of message property.
First you can define the message properties in "application.properties" file.
header.authorization.name=Authorization
header.authorization.value=<your_value>
header.authorization.dataType=string
And next you can use #Value annotation in your controller class like below:
#RestController
public class HelloWorld{
#Value("${header.authorization.name}")
private String authorizationHeaderName;
#Value("${header.authorization.value}")
private String authorizationHeaderValue;
#Value("${header.authorization.dataType}")
private String authorizationHeaderdataType;
#PostMapping(value = "/getmessage")
#ApiImplicitParams({#ApiImplicitParam(name = ${header.authorization.name}", value = "${header.authorization.value}", dataType =
${header.authorization.dataType}", paramType = "header")
})
public responseEntity<?> getMessage(){
return ResponseEntity.ok("HelloWorld");
}
}
Related
I have a requirement to create an auto-configuration for service call on spring-boot startup.
i.e., During spring-boot startup, the below service has to be called.
#PostMapping(path = "/addProduct", produces = "application/json", consumes = "application/json")
public #ResponseBody String addProduct(#RequestBody String productStr) {
..<My code>..
}
The add product requires an input like:
{
"product":"test",
"price":"10"
}
This will internally call a database service.
During startup, the json input provided in the console should be fed to this service.
I have no idea on how to achieve this. Verified a couple of Spring documentation. But those does'nt suit the requirement.
Kindly help in explaining a way or providing a right documentation to achieve this.
One way to do this is by implementing ApplicationRunner like this :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
#Component
public class ApplicationInitializer implements ApplicationRunner {
private ProductController productController;
public ApplicationInitializer(ProductController productController) {
this.productController = productController;
}
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
String response = productController.add(product);
System.out.println(response);
}
}
The run method will be invoked at startup with arguments passed in the command line like this : java -jar yourApp.jar --product="{\"name\":\"test\", \"price\":\"15\"}".
And you need a class to map the json to an object like this :
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
You can also call your Controller using the RestTemplate (or WebClient) if needed :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:8080/products", product, String.class);
System.out.println(response);
}
}
Such requirement can be achieved by using an init() method annotated with #PostConstruct in a bean.
e.g.
#Component
public class Foo {
#PostConstruct
public void init() {
//Call your service
}
}
I have the following yaml file
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
I'm trying to bind the actionKeyMap/gateway property into a Java map and it doesn't works.
I've tried the following code
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
ConfigurationProperties or Value annotation doesn't works.
Adding code as discussed in comments
Application.yml
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
Spring boot Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
//#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MessageResolver.java
package hello;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
#Value("${proj.sms.actionKeyMap.OTP_GENERATOR}")
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public Map<String, String> getActionKeyMap() {
return actionKeyMap;
}
public void setActionKeyMap(Map<String, String> actionKeyMap) {
this.actionKeyMap = actionKeyMap;
}
}
GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
MessageResolver messageResolver;
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
System.out.println(messageResolver.getTest());
System.out.println(messageResolver.getActionKeyMap());
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Greeting.java (in case you try to build complete project)
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
URL http://localhost:8080/greeting
Console Output from GreetingController (sop on line 22 & 23)
Value1
{OTP_GENERATOR=Value1, CREATE_USER=Value2}
You can get information with this link on how to solve your problem: How to inject a Map using the #Value Spring Annotation?
it will look something like:
actionKeyMap: '{
OTP_GENERATOR: "value1",
CREATE_USER: "value2"
}'
Try :(colon) intead of .(dot)
#Component
#ConfigurationProperties(prefix="proj:sms")
public class MessageResolver {
#Value("${proj:sms:actionKeyMap}")
private Map<String, String> actionKeyMap;
You just need to declare Gateway and ActionKeyMap class to match the ymlproperty. Or you can read the Spring Boot reference here, link.
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
private Gateway gateway;
private ActionKeyMap actionKeyMap;
//getter/setter
}
``
public class Gateway {
private String username;
private String password;
private String signature;
private String url;
//getter/setter
}
``
public class ActionKeyMap {
private String OTP_GENERATOR;
private String CREATE_USER;
//getter/setter
}
I am trying to design a rest api, and below is my controller code.
when i invoke http://localhost:8080/ the response is fine, but if i hit http://localhost:8080/api/ca it thorws javax.servlet.ServletException: No adapter for handler [...CaDetailController#48224381]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
#RestController("/api")
public class CaDetailController {
private static final Logger logger = LoggerFactory.getLogger(GetClassLoader.class.getClass());
#Autowired
CaService caService;
#RequestMapping(path = "/ca", method = RequestMethod.GET)
public #ResponseBody List<CaDetail> getCorporateActions() {
logger.info("CaDetailController.findAllCaDetails()");
return caService.findAllCaDetails();
}
#RequestMapping(path = "/ca/{caId}", method = RequestMethod.GET)
public #ResponseBody List<CaDetail> getCorporateActions(#PathParam("caId") long caId) {
logger.info("CaDetailController.getCorporateActions() : caId : " + caId);
return caService.findAllCaDetails();
}
}
Updated controller.
#RestController
#RequestMapping("/api/ca")
public class CaDetailController {
private static final Logger logger = LoggerFactory.getLogger(GetClassLoader.class.getClass());
#Autowired
CaService caService;
#GetMapping(path = "/")
public #ResponseBody List<CaDetail> getCorporateActions() {
logger.info("CaDetailController.findAllCaDetails()");
return caService.findAllCaDetails();
}
#GetMapping(path = "/{caId}")
public #ResponseBody List<CaDetail> getCorporateActions(#PathParam("caId") Long caId) {
logger.info("CaDetailController.getCorporateActions() : caId : " + caId);
return caService.findAllCaDetails();
}
}
For clarity, fix is:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/ca")
public class CaDetailController {
#GetMapping
public String healthcheck1() {
return "ok!";
}
#GetMapping(path = "health")
public String healthcheck2() {
return "ok again!";
}
}
You can call this endpoints using URL:
http://localhost:8080/api/ca and
http://localhost:8080/api/ca/health
(Assuming default Spring Boot Tomcat configuration).
Don't add ("/api") value to #RestController Annotation,
add it to #RequestMapping
#RestController
#RequestMapping("api/")
...
Try this
#RestController
#RequestMapping("/api")
public class CaDetailController {
instead of
#RestController("/api")
public class CaDetailController {
Does anyone have a full spring boot REST CRUD example? The spring.io site just has a RequestMapping for GET. I'm able to get POST and DELETE working but not PUT.
I suspect it's how I'm trying to get the params where the disconnect is, but I have not seen an example where someone is performing an update.
I'm currently using the SO iPhone app so I can't paste my current code. Any working examples would be great!
As you can see I have implemented two way to update.
The first one will receive a json, and the second one will receive the cusotmerId in the URL and json also.
#RestController
#RequestMapping("/customer")
public class CustomerController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Customer greetings(#PathVariable("id") Long id) {
Customer customer = new Customer();
customer.setName("Eddu");
customer.setLastname("Melendez");
return customer;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public List<Customer> list() {
return Collections.emptyList();
}
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody Customer customer) {
}
#RequestMapping(method = RequestMethod.PUT)
public void update(#RequestBody Customer customer) {
}
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public void updateById(#PathVariable("id") Long id, #RequestBody Customer customer) {
}
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public void delete() {
}
class Customer implements Serializable {
private String name;
private String lastname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getLastname() {
return lastname;
}
}
}
An alternative update returns ResponseEntity object.
#RestController
#RequestMapping("/fruits")
public class FruitController {
private final Logger LOG = LoggerFactory.getLogger(FruitController.class);
#Autowired
private FruitService fruitService;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Fruit>> getAll(#RequestParam(value = "offset", defaultValue = "0") int index,
#RequestParam(value = "numberOfRecord", defaultValue = "10") int numberOfRecord) {
LOG.info("Getting all fruits with index: {}, and count: {}", index, numberOfRecord);
List<Fruit> fruits = fruitService.getAll(index, numberOfRecord);
if (fruits == null || fruits.isEmpty()) {
return new ResponseEntity<List<Fruit>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<Fruit>>(fruits, HttpStatus.OK);
}
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public ResponseEntity<Fruit> get(#PathVariable("id") int id) {
LOG.info("Getting fruit with id: {}", id);
Fruit fruit = fruitService.findById(id);
if (fruit == null) {
return new ResponseEntity<Fruit>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<Fruit>(fruit, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> create(#RequestBody Fruit fruit, UriComponentsBuilder ucBuilder) {
LOG.info("Creating fruit: {}", fruit);
if (fruitService.exists(fruit)) {
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
fruitService.create(fruit);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/fruit/{id}").buildAndExpand(fruit.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
#RequestMapping(value = "{id}", method = RequestMethod.PUT)
public ResponseEntity<Fruit> update(#PathVariable int id, #RequestBody Fruit fruit) {
LOG.info("Updating fruit: {}", fruit);
Fruit currentFruit = fruitService.findById(id);
if (currentFruit == null) {
return new ResponseEntity<Fruit>(HttpStatus.NOT_FOUND);
}
currentFruit.setId(fruit.getId());
currentFruit.setName(fruit.getName());
fruitService.update(fruit);
return new ResponseEntity<Fruit>(currentFruit, HttpStatus.OK);
}
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public ResponseEntity<Void> delete(#PathVariable("id") int id) {
LOG.info("Deleting fruit with id: {}", id);
Fruit fruit = fruitService.findById(id);
if (fruit == null) {
return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
}
fruitService.delete(id);
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
From Spring MVC RESTFul Web Service CRUD Example
I have prepared a set of tutorials on Spring Boot CRUD Operations. Following are the content of tutorials:
How to create Spring Boot Project using Spring Tool Suite
How to implement GET & POST method in spring boot restful web service
How to implement PUT & DELETE method in spring boot restful web service
Integrate PostgreSQL database using spring boot JPA with spring boot restful web service
Use of CURL commands
Youtube Tutorials:
Spring Boot Restful Web Service Tutorial | Tutorial 1 -
Introduction
Spring Boot Restful Web Services CRUD Example GET & POST | Tutorial - 2
Spring boot CRUD Operations example PUT & DELETE | Tutorial - 3
Spring Boot JPA | In 5 Simple Steps Integrate PostgreSQL Database | Tuorial - 4
Visit Blog for more details.
You can get my full RESTful server and client app using SpringBoot at Spring RESTFul Examples at github
package com.controllers;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.cats.hcm.bussinessObjects.Address;
import com.cats.hcm.bussinessObjects.Employee;
import com.cats.hcm.bussinessObjects.EmployeeBO;
import com.cats.hcm.bussinessObjects.MaritalStatus;
import com.cats.hcm.bussinessObjects.State;
import com.cats.hcm.repository.EmployeeRepositoryImpl;
import com.cats.hcm.repository.MaritalStatusRepositoryImpl;
import com.cats.hcm.repository.Repository;
import com.cats.hcm.repository.StateRepositoryImpl;
import com.cats.hcm.services.EmployeeServiceImpl;
import com.google.gson.Gson;
#Controller
#RequestMapping("/newemployee")
public class NewEmployeeController {
private static final Logger logger = LoggerFactory.getLogger(Repository.class);
#Autowired
Repository repository;
#Autowired
StateRepositoryImpl stateRepositoryImpl;
#Autowired
MaritalStatusRepositoryImpl maritalStatusRepositoryImpl;
#Autowired
EmployeeRepositoryImpl employeeRepositoryImpl;
#Autowired
EmployeeServiceImpl employeeServiceImpl;
#RequestMapping(value="/save", method=RequestMethod.POST)
public #ResponseBody String addEmployee(#RequestBody EmployeeBO employeeBO, BindingResult bindingResult) throws SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException, IOException {
logger.info("==========="+new Gson().toJson(employeeBO));
logger.info("========Employee ID========"+employeeBO.getEmployeeId());
repository.addToDataBase(employeeBO);
String msg="Success";
return msg;
}
#RequestMapping(value="/update", method=RequestMethod.POST)
public #ResponseBody String updateEmployee(#RequestBody EmployeeBO employeeBO, BindingResult bindingResult) throws SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException, IOException {
logger.info("==========="+new Gson().toJson(employeeBO));
logger.info("========Employee ID========"+employeeBO.getEmployeeId());
//Deleting Existing Employee
Boolean isDeleted = employeeServiceImpl.deleteEmployeeDetails(employeeBO.getEmployeeId());
if(isDeleted) {
repository.addToDataBase(employeeBO);
}
String msg="Success";
return msg;
}
#RequestMapping("/employeeData")
public #ResponseBody List<Employee> getEmployeeDataTablePage() {
logger.info("======getEmployeeDataTablePage======");
List<Employee> employeeList = employeeRepositoryImpl.readAll();
logger.info("========EmployeeList========="+new Gson().toJson(employeeList));
return employeeList;
}
#RequestMapping("/modifyPage")
public #ResponseBody List<Employee> getEmployeeModifyPage(#RequestParam("employeeId") String employeeId) {
logger.info("========getEmployeeModifyPage====EmployeeID:===="+employeeId);
//List<State> stateList = stateRepositoryImpl.readAll();
//List<MaritalStatus> maritalStatusList = maritalStatusRepositoryImpl.readAll();
//model.addAttribute("stateList", stateList);
//model.addAttribute("maritalStatusList", maritalStatusList);
List<Employee> employeeList = employeeRepositoryImpl.readAll();
logger.info("========employeeList:===="+employeeList);
EmployeeBO employeeBO = employeeServiceImpl.getEmployeeBO(employeeId);
logger.info("========getEmployeeModifyPage====EmployeeBO:===="+employeeBO);
return employeeList;
//return new ModelAndView("apps-mod-employee", "employee", employeeBO);
}
#RequestMapping("/delete")
public #ResponseBody String deleteEmployee(#RequestParam("employeeId") String employeeId) {
logger.info("========deleteEmployee===EmployeeID:===="+employeeId);
//employeeRepositoryImpl.delete(employeeServiceImpl.getEmployeeBO(employeeId));
Boolean isDeleted = employeeServiceImpl.deleteEmployeeDetails(employeeId);
if(isDeleted) {
logger.info("========Employee Record Deleted===EmployeeID:===="+employeeId);
}
return "redirect:/employee/employeeDataTable";
}
/*#RequestMapping("/employeeDataByEmpId")
public String getEmployeeAddPage(Map<String, Object> model) {
public ModelAndView getEmployeeDataByEmpId(ModelMap model,HttpServletRequest request, HttpServletResponse response) {
logger.info("======getEmployeeDataByEmpId======");
String EmployeeID=request.getParameter("empId");
Employee employee = employeeRepositoryImpl.read(EmployeeID);
logger.info("========Employee========="+new Gson().toJson(employee));
model.addAttribute(new EmployeeBO());
model.addAttribute("employeeByEmpId", employee);
//return "apps-add-employee";
//return new ModelAndView("apps-add-employee");
return new ModelAndView("apps-employee", "employee", new EmployeeBO());
}*/
}
I have the following request handler for saving autos. I have verified that this works when I use e.g. cURL. Now I want to unit test the method with Spring MVC Test. I have tried to use the fileUploader, but I am not managing to get it working. Nor do I manage to add the JSON part.
How would I unit test this method with Spring MVC Test? I am not able to find any examples on this.
#RequestMapping(value = "autos", method = RequestMethod.POST)
public ResponseEntity saveAuto(
#RequestPart(value = "data") autoResource,
#RequestParam(value = "files[]", required = false) List<MultipartFile> files) {
// ...
}
I want to uplod a JSON representation for my auto + one or more files.
I will add 100 in bounty to the correct answer!
Since MockMvcRequestBuilders#fileUpload is deprecated, you'll want to use MockMvcRequestBuilders#multipart(String, Object...) which returns a MockMultipartHttpServletRequestBuilder. Then chain a bunch of file(MockMultipartFile) calls.
Here's a working example. Given a #Controller
#Controller
public class NewController {
#RequestMapping(value = "/upload", method = RequestMethod.POST)
#ResponseBody
public String saveAuto(
#RequestPart(value = "json") JsonPojo pojo,
#RequestParam(value = "some-random") String random,
#RequestParam(value = "data", required = false) List<MultipartFile> files) {
System.out.println(random);
System.out.println(pojo.getJson());
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
return "success";
}
static class JsonPojo {
private String json;
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
}
}
and a unit test
#WebAppConfiguration
#ContextConfiguration(classes = WebConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class Example {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void test() throws Exception {
MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some xml".getBytes());
MockMultipartFile secondFile = new MockMultipartFile("data", "other-file-name.data", "text/plain", "some other type".getBytes());
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", "{\"json\": \"someValue\"}".getBytes());
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders.multipart("/upload")
.file(firstFile)
.file(secondFile)
.file(jsonFile)
.param("some-random", "4"))
.andExpect(status().is(200))
.andExpect(content().string("success"));
}
}
And the #Configuration class
#Configuration
#ComponentScan({ "test.controllers" })
#EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
The test should pass and give you output of
4 // from param
someValue // from json file
filename.txt // from first file
other-file-name.data // from second file
The thing to note is that you are sending the JSON just like any other multipart file, except with a different content type.
The method MockMvcRequestBuilders.fileUpload is deprecated use MockMvcRequestBuilders.multipart instead.
This is an example:
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;
/**
* Unit test New Controller.
*
*/
#RunWith(SpringRunner.class)
#WebMvcTest(NewController.class)
public class NewControllerTest {
private MockMvc mockMvc;
#Autowired
WebApplicationContext wContext;
#MockBean
private NewController newController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wContext)
.alwaysDo(MockMvcResultHandlers.print())
.build();
}
#Test
public void test() throws Exception {
// Mock Request
MockMultipartFile jsonFile = new MockMultipartFile("test.json", "", "application/json", "{\"key1\": \"value1\"}".getBytes());
// Mock Response
NewControllerResponseDto response = new NewControllerDto();
Mockito.when(newController.postV1(Mockito.any(Integer.class), Mockito.any(MultipartFile.class))).thenReturn(response);
mockMvc.perform(MockMvcRequestBuilders.multipart("/fileUpload")
.file("file", jsonFile.getBytes())
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
}
Have a look at this example taken from the spring MVC showcase, this is the link to the source code:
#RunWith(SpringJUnit4ClassRunner.class)
public class FileUploadControllerTests extends AbstractContextControllerTests {
#Test
public void readString() throws Exception {
MockMultipartFile file = new MockMultipartFile("file", "orig", null, "bar".getBytes());
webAppContextSetup(this.wac).build()
.perform(fileUpload("/fileupload").file(file))
.andExpect(model().attribute("message", "File 'orig' uploaded successfully"));
}
}
Here's what worked for me, here I'm attaching a file to my EmailController under test. Also take a look at the postman screenshot on how I'm posting the data.
#WebAppConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = EmailControllerBootApplication.class
)
public class SendEmailTest {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void testSend() throws Exception{
String jsonStr = "{\"to\": [\"email.address#domain.com\"],\"subject\": "
+ "\"CDM - Spring Boot email service with attachment\","
+ "\"body\": \"Email body will contain test results, with screenshot\"}";
Resource fileResource = new ClassPathResource(
"screen-shots/HomePage-attachment.png");
assertNotNull(fileResource);
MockMultipartFile firstFile = new MockMultipartFile(
"attachments",fileResource.getFilename(),
MediaType.MULTIPART_FORM_DATA_VALUE,
fileResource.getInputStream());
assertNotNull(firstFile);
MockMvc mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders
.multipart("/api/v1/email/send")
.file(firstFile)
.param("data", jsonStr))
.andExpect(status().is(200));
}
}
If you are using Spring4/SpringBoot 1.x, then it's worth mentioning that you can add "text" (json) parts as well . This can be done via MockMvcRequestBuilders.fileUpload().file(MockMultipartFile file) (which is needed as method .multipart() is not available in this version):
#Test
public void test() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.fileUpload("/files")
// file-part
.file(makeMultipartFile( "file-part" "some/path/to/file.bin", "application/octet-stream"))
// text part
.file(makeMultipartTextPart("json-part", "{ \"foo\" : \"bar\" }", "application/json"))
.andExpect(status().isOk())));
}
private MockMultipartFile(String requestPartName, String filename,
String contentType, String pathOnClassPath) {
return new MockMultipartFile(requestPartName, filename,
contentType, readResourceFile(pathOnClasspath);
}
// make text-part using MockMultipartFile
private MockMultipartFile makeMultipartTextPart(String requestPartName,
String value, String contentType) throws Exception {
return new MockMultipartFile(requestPartName, "", contentType,
value.getBytes(Charset.forName("UTF-8")));
}
private byte[] readResourceFile(String pathOnClassPath) throws Exception {
return Files.readAllBytes(Paths.get(Thread.currentThread().getContextClassLoader()
.getResource(pathOnClassPath).toUri()));
}
}