How can I send a header with void response? - spring

I'm working on a Spring project.
I'm currently doing this.
public ResponseEntity<?> create(#RequestBody final Some entity) {
// persist the entity here
final URI location = uriComponentsBuilder.path("{id}").buildAndExpand(entity.getId()).toUri();
return ResponseEntity.created(location).build();
}
And I found the #ResponseStatus.
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody #NotNull final BaseType entity) {
// persist the entity here
// Location header to where?
}
Is there any way to send Location header with this way?

You can return response entity like below:
return new ResponseEntity<>(location, HttpStatus.CREATED);
or
HttpHeaders headers = new HttpHeaders();
headers.add(location);
return new ResponseEntity<>(headers, HttpStatus.CREATED);

Try this. It returns your preferred header and status, without body.
#ResponseStatus(HttpStatus.I_AM_A_TEAPOT)
#RequestMapping("/teapot")
public HttpHeaders dummyMethod() {
HttpHeaders h = new HttpHeaders();
h.add("MyHeader", "MyValue");
return h;
}

Related

How to pass multipart request parameter in rest template spring boot?

I have following controller code in one microservice :
#PostMapping("/posts/{postId}/images")
#RolesAllowed({Roles.USER, Roles.ADMIN})
public ResponseEntity<UploadImageResponse> uploadFile(#RequestParam("image") MultipartFile file, #AuthenticationPrincipal String username, #PathVariable(name = "postId") String postId) {
ImageMetadataEntity metadata = imageService.upload(file, username, postId);
UploadImageResponse uploadImageResponse = new UploadImageResponse(metadata.getFilename(), metadata.getUri(), metadata.getFileType(), metadata.getPostId());
return new ResponseEntity<>(uploadImageResponse, HttpStatus.CREATED);
}
I am calling this API from other microservice using rest template like below:
#Override
public UploadImageResponse uploadFile(UploadImageRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add(HttpHeaders.AUTHORIZATION, Constants.BEARER + " " + TokenContext.get());
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("image", request.getFile().getBytes());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<UploadImageResponse> response = restTemplate
.postForEntity(String.format(IMAGE_UPLOAD_URL, MEDIA_SERVICE_HOST, request.getPostId()), requestEntity, UploadImageResponse.class);
return response.getBody();
}
But somehow this is not working. I am getting the below error :
2022-11-27 18:52:56.829 WARN 11120 --- [nio-8000-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'image' is not present]
But in when debugged HttpServletRequest.multipartParameterNames has the 'image' field i am sending.
Can someone tell me what is wrong ?
The issue is that the file is sent in request as request.getFile().getBytes(). When request comes to controller, spring checks for the file name and if its present then its added in multiPartFiles else they will be added in multiPartParameterNames in HttpServletRequest. When sent as getBytes(), the file name is null. I have fixed it by adding the below class
public class MultipartInputStreamFileResource extends InputStreamResource {
private final String filename;
public MultipartInputStreamFileResource(InputStream inputStream, String filename) {
super(inputStream);
this.filename = filename;
}
#Override
public String getFilename() {
return this.filename;
}
#Override
public long contentLength() throws IOException {
return -1;
}
}
and the request now is changed as this :
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("image", new MultipartInputStreamFileResource(request.getFile().getInputStream(), request.getFile().getOriginalFilename()));

How to get token from a REST service with Spring

The service provider supplies me with the header data: grant_type, Content-Type. And the body data: grant_type, username and password. Which I use in Postman where it generates OK token. But in the Spring application it generates an error HttpClientErrorException $ BadRequest: 400 Bad Request.
I have the class to set the body data:
public class BodyToken {
private String grant_type = "password";//set body data
private String username = "User";//set body data
private String password = "123";//set body data
private String access_token;
#JsonGetter("access_token")
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
#JsonGetter("grant_type")
public String getGrant_type() {
return grant_type;
}
#JsonGetter("username")
public String getUsername() {
return username;
}
#JsonGetter("password")
public String getPassword() {
return password;
}
}
This is the controller where the header data is set:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
HttpEntity request = new HttpEntity(headers);
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}
OK was solved with using the Class MultiValueMap and LinkedMultiValueMap. The credentials are added to this new object and it is sent together with the request:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();//line solution
body.add("grant_type", "password");//line solution
body.add("username", "user");//line solution
body.add("password", "123");//line solution
HttpEntity request = new HttpEntity(body, headers);//and I add this body to HttpEntity
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}

Spring Boot RestTemplate: Bad request when directly copying from postman

So I have an API request where I am copying the details directly from postman where it works. I am however getting a bad request error.
#Service
public class GraphApiService {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
RestTemplate restTemplate;
#Autowired
Constants constants;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
JSONObject body = new JSONObject();
body.put("grant_type", "refresh_token");
body.put("client_id", "clientid");
body.put("scope","User.Read offline_access Files.Read Mail.Read Sites.Read.All");
body.put("redirect_uri", "http://localhost");
body.put("client_secret","secret");
body.put("refresh_token", "token");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(body.toString(), headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request,String.class);
return response;
}
catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}
I would appreciate any help. The bad request error message from microsoft graph isn't a descriptive one that will help
You're sending JSON payload with FORM_URLENCODED header.
Either you need to check if API accepts json payload, if so you need to change content-type to application/json or you can post form data as follows.
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
MultiValueMap<String, String> multiValueMap= new LinkedMultiValueMap<String, String>();
multiValueMap.add("grant_type", "refresh_token");
multiValueMap.add("client_id", "clientid");
//.....
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(multiValueMap, headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request, String.class);
return response;
}catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}

Spring RESTful endpoint not asynchonous

I have an issue with an endpoint that is not behaving as I would expect. Basically, it follows the flow the way I would expect and it hits the return statement BEFORE it fires the callback methods. So, all seems good.
However, when I test this in Postman, the method still hangs. It blocks and does not return a response until the callback onSuccess is executed. Is this how this should behave or am I missing something?
#RequestMapping(value = "/async", method = RequestMethod.POST)
#ResponseBody
public DeferredResult<String> createAsync(#RequestBody Input input) throws CombineException {
AsyncRestTemplate restTemplate = new AsyncRestTemplate();
DeferredResult<String> result = new DeferredResult();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<?> entity = new HttpEntity<>(input , headers);
ListenableFuture<ResponseEntity<Input>> future = restTemplate.exchange("http://localhost:8080/myLongRunningEndpointIDontWantToWaitFor", HttpMethod.POST, entity, Input.class);
future.addCallback(new ListenableFutureCallback<ResponseEntity<Input>>() {
#Override
public void onSuccess(ResponseEntity<Input> response) {
System.out.println("Success");
result.setResult(response.getBody().toString());
}
#Override
public void onFailure(Throwable t) {
System.out.println("FAILED");
result.setErrorResult(t.getMessage());
}
});
System.out.println("RETURNING...");
return result;
}

Rest client with Spring RestTemplate and custom Object input parameter

This is my rest controller (server):
#RestController
public class RemoteController {
#RequestMapping(value="/test", method=RequestMethod.GET)
public Return serverTest(HttpServletRequest req, SearchFilter search) throws Exception{
//...
return new OutputTest();
}
}
I want to write the corresponding client for this GET controller with SearchFilter object as input.
public void clientTest(){
SearchFilter input=new SearchFilter();
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = input;// how to store SearchFilter input ??????
ResponseEntity<OutputTest> response=restTemplate.exchange("http://localhost:8080/test", HttpMethod.GET, entity, OutputTest.class);
OutputTest out=response.getBody();
}
How can I send a single object to restTemplate?
You should tell Spring how to bind the request parameters to SearchFilter. There are multiple approachs to achieve that, The simplest solution is to use ModelAttribute annotation:
#RequestMapping(value="/test", method=RequestMethod.GET)
public Return serverTest(HttpServletRequest req, #ModelAttribute SearchFilter search) throws Exception{
//...
return new OutputTest();
}
Supposing your SearchFilter looks like this:
public class SearchFilter {
private String query;
// other filters and getters and setters
}
If you fire a request to /test?query=something, the SearchFilter will be populated with the sent query parameter. In order to send this request with RestTemplate:
RestTemplate template = new RestTemplate();
// prepare headers
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// request without body, just headers
HttpEntity<Object> request = new HttpEntity<>(headers);
ResponseEntity<OutputTest> response = template.exchange("http://localhost:8080/test?query=something",
HttpMethod.GET,
request,
OutputTest.class);
The other approach i can think of, is to implement a HandlerMethodArgumentResolver for resolving SearchFilter arguments. Also, you can break the SearchFilter apart and use multiple RequestParams.

Resources