I have the following controller in a springboot application which I use to GET an employee based on given employeeID.
#GetMapping(path = "employee/{empId}", produces = "application/json; charset=UTF-8")
public Mono<ResponseEntity<Object>> getEmployee(#PathVariable(name = "empId") String empID,
#RequestParam(name = "showExtraDetails", defaultValue = "false") boolean showExtraDetails) {
Mono<Employee> employeeFound = employeeService.getEmployee(empID, showExtraDetails);
if (employeeFound != null) {
return Mono.just(new ResponseEntity<>(employeeFound, HttpStatus.OK));
}
}
I can see the status code 200 in postman but I cannot see the details of the employee as the response body. Instead I see the following:
{
"scanAvailable": true
}
Is there a way to display the employee attributes without blocking the stream?
I am not sure, but it might have to do with putting the Mono<Employee> in the ResponseEntity directly. This should work:
return employeeService.getEmployee(empID, showExtraDetails)
.map(employee-> new ResponseEntity<>(employee, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
Related
I'm new to MockMVC. I've successfully written some basic tests, but I got stuck on trying to test an use case with the endpoint that requires a POST request with two parameters - a POJO and an array of MultipartFile. The test is written as such:
#Test
public void vytvorPodnetTest() throws Exception {
var somePojo = new SomePojo();
somePojo.setSomeVariable("test_value");
var roles = List.of("TEST_USER");
var uid = "00000000-0000-0000-0000-000000000001";
MockMultipartFile[] attachments = {new MockMultipartFile("file1.txt", "file1.txt", "text/plain", "file1 content".getBytes()),
new MockMultipartFile("file2.txt", "file2.txt", "text/plain", "file2 content".getBytes())};
MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/some-pojo/create");
builder.with(req - {
req.setMethod("POST");
return req;
});
MvcResult result = mockMvc.perform(builder.file(attachments[0]).file(attachments[1])
.param("SomePojo", new ObjectMapper().writeValueAsString(somePojo))
.file(attachment[0])
.with(TestUtils.generateJWTToken(uid, roles)))
.andExpect(status.isOk())
.andReturn();
}
The controller method is as follows:
#PostMapping(value = "/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public UUID createPojo(
#RequestPart(value = "SomePojo") SomePojo somePojo,
#RequestPart(value = "attachments", required = false) MultipartFile[] attachments) {
return pojoService.create(somePojo, attachments);
}
It stops here, before reaching the service. I've tried adding the files both as a param "attachments" and like shown above, but all I get is "400 Bad Request"
Finally found the way to send the parameters as MockMultipartFile from MockMVC to the controller:
MockMultipartFile pojoJson = new MockMultipartFile("SomePojo", null,
"application/json", JsonUtils.toJSON(podnet).getBytes());
mockMvc.perform(MockMvcRequestBuilders.multipart("/some-pojo/create")
.file(pojoJson)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.with(new TestUtils().generateJWTToken(uid, roles)))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
There is my code.
public Mono<RespDto> function(TransReqDto1 reqDto1, TransReqDto2 reqDto2, String token) {
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("TransReqDto1", reqDto1);
builder.part("TransReqDto2", reqDto2);
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
LinkedMultiValueMap map = new LinkedMultiValueMap();
map.add("TransReqDto1", reqDto1);
map.add("TransReqDto2", reqDto2);
return
client.post()
.uri("/api")
.body(BodyInserters.fromValue(reqDto1))
.headers(h -> h.setBearerAuth(token.split(" ")[1]))
.retrieve()
.bodyToMono(RespDto.class);
}
My probelm is that I need to send both reqDto1 & reqDto2. I've successfully sent reqDto1 with the code above but I can't figure out a way to send two objects.
Tried MultipartBodybuild and MultiValueMap but both are returning error from the target API. Please give me some hints!! Thank you
Here is the API I am trying to call!
#PostMapping("")
#ApiOperation(value = "test", notes = "test")
public Mono<?> transPost(#Valid #RequestBody TransReqDto1 reqDto1,
#Valid #RequestBody TransReqDto2 reqDto2) {
return testService.function(reqDto1, reqDto2);
}
You cannot use two #RequestBody. It can bind to a single object only. The expected way to do that is to create a wrapper DTO containing all the relevant data:
public class TransReqDto {
private TransReqDto1 transReqDto1;
private TransReqDto2 transReqDto2;
//...
}
I have built a Restful-API Java(SpringBoot) and created the needed requests.
The following request is a POST Request to add new Category.
I have tested the POST request by POSTMAN, and it working as expected.
I am building the client-side in ASP.NET 5.x.x.
Now the problem appear when I am calling the post request, it seems the API doesn't receive the data (#RequestBody category) that has been send from the client.
Here is a code simple of how I have created them
Server Side:
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public CategoryDTO create(#RequestBody CategoryDTO category) {
log.info("Adding new Category Name: " + category.getName());
return categoryMapper.asCategoryDTO(categoryService.save(categoryMapper.asCategory(category)));
}
Client Side
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Category category)
{
Category newCategory = new Category();
// Serialize the concrete class into a JSON String
var stringPayload = JsonConvert.SerializeObject(category);
// Wrap the JSON inside a StringContent which then can be used by the HttpClient class
StringContent content = new StringContent(stringPayload);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.PostAsync("http://localhost:8080/category/add", content))
{
string apiResponse = await response.Content.ReadAsStringAsync();
newCategory = JsonConvert.DeserializeObject<Category>(apiResponse);
}
}
return RedirectToAction("Index");
}
I don't know what is wrong there, could anybody help!
EDIT--
Here is the request via postman
EDIT
I have created another POST request but as a RequestParam instead of RequestBody
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/add", produces = MediaType.APPLICATION_JSON_VALUE)
public CategoryDTO addCategory(#RequestParam(name = "categoryName") String categoryName){
return categoryMapper.asCategoryDTO(categoryService.addCategory(categoryName));
}
and created in the client side the request as following
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Category category)
{
Category newCategory = new Category();
var parameters = new Dictionary<string, string> { { "categoryName", category.Name } };
var encodedContent = new FormUrlEncodedContent(parameters);
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.PostAsync("http://localhost:8080/category/add", encodedContent))
{
string apiResponse = await response.Content.ReadAsStringAsync();
newCategory = JsonConvert.DeserializeObject<Category>(apiResponse);
}
}
return RedirectToAction("Index");
}
And It's works fine!
So the problem is how to pass the data via the httpClient, which need to be of type RequestBody (the data in the body not in the header!) also as a application/json.
So how to pass the data?
I suppose that your spring boot application just blocks POST request because you didn't provide instruction how to handle requests. Try to disable csrf protection like it did here: https://stackoverflow.com/a/48935484/13314717
It might be a problem in the naming convention. Starting with capital letters in properties in C#, and starting with lowercase in Java.
If your class looks like this in C#
class Category {
int Id;
string Name;
...
}
And like this in Java
class Category {
int id;
string name;
...
}
It is not going to work so you have to match the property names. So make either both id or both Id.
I am trying to use swagger with java.
Using NSwag studio I am able to generate all my endpoints except one that returns a list of objects.
Here is my action in controller:
#ApiOperation(value = "getAll", nickname = "getAll", responseContainer = "List", response = DiakEntity.class)
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
I am using swagger-annotations 1.5.23, springfox-swagger-ui 2.9.2, springfox-swagger2 2.9.2.
If I test from Postman it works.
Also tried like this:
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class, message = "Gets all diak objects")
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public ResponseEntity<List<DiakEntity>> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return new ResponseEntity<>(request.getObject(), HttpStatus.OK);
}
thnx
Please try with the following annotation for swagger.
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class)
At the end I changed my action as below, and it started to work
#ApiOperation(value = "all", nickname = "all")
#PostMapping("/api/diak/all")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetAll(#RequestBody #Valid RequestDiakByName data) throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
In my controller I am having if condition and two different response type. I will get response in JSON format from "if" condition, but I am getting response from else condition like unexpected '0 , instead I need to get my error message'.
My controller code snippet
#RequestMapping(value = "/saveuser", produces = { "application/json" }, consumes = { "application/json" }, method = RequestMethod.POST)
public ResponseEntity<?> addUser(#RequestBody TestUser user)
throws NotFoundException {
System.out.println(user.getAnswer().size());
if(questioncount == user.getAnswer().size())
{
return new ResponseEntity<TestUser>(service.addUser(user),
HttpStatus.OK);
}else {
String one="one";
String erromessage = "Only" + questioncount +" questions are allowed";
System.out.println(erromessage);
return new ResponseEntity<String>(erromessage,HttpStatus.NOT_ACCEPTABLE);
}
}