Springboot 405 Method Not Allowed in Pivotal Web Services/Cloud Foundry but working in local docker - spring-boot

I have created REST API application in SpringBoot which has GET and POST method pointing to the same URL. I have created springboot application and deployed in docker container in my local machine. When I try to access this application, I am able to get response for both GET and POST methods. I have deployed the same application into Pivotal Web Services (Pivotal Cloud Foundry). When I try to access GET method in PWS, it works fine but when I try to access POST method, I am getting error.
`"error": "Method Not Allowed",
"message": "Request method 'POST' not supported",`
If I look at the error in PWS log, I could see the following.
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
I have tried to change the springboot code to include different options in the method but none of them seem to work in PWS.
I would like to see 200 OK for POST method.
#RestController
#RequestMapping("/skus")
public class SKUController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
//==============================================================
#Autowired
SKUService skuService; //Service which will do all data retrieval/manipulation work
// -------------------Retrieve All SKUS---------------------------------------------
#RequestMapping(value = "/", method = RequestMethod.GET)
public ResponseEntity<List<SKU>> listAllSKUS() {
List<SKU> skus = skuService.listAllSKUS();
if (skus.isEmpty()) {
return new ResponseEntity(HttpStatus.NO_CONTENT);
// You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<SKU>>(skus, HttpStatus.OK);
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity<?> addSKU(#RequestBody SKU sku) {
try {
skuService.addSKU(sku);
return new ResponseEntity<SKU>(sku, HttpStatus.OK);
}catch(Exception e) {
return new ResponseEntity("SKU cannot be created", HttpStatus.BAD_REQUEST);
}
}
}
````

Related

Testing a Post multipart/form-data request on REST Controller

I've written a typical spring boot application, now I want to add integration tests to that application.
I've got the following controller and test:
Controller:
#RestController
public class PictureController {
#RequestMapping(value = "/uploadpicture", method = RequestMethod.POST)
public ResponseEntity<VehicleRegistrationData> uploadPicturePost(#RequestPart("userId") String userId, #RequestPart("file") MultipartFile file) {
try {
return ResponseEntity.ok(sPicture.saveAndParsePicture(userId, file));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
Test:
#Test
public void authorizedGetRequest() throws Exception {
File data = ResourceUtils.getFile(testImageResource);
byte[] bytes = FileUtils.readFileToByteArray(data);
ObjectMapper objectMapper = new ObjectMapper();
MockMultipartFile file = new MockMultipartFile("file", "test.jpg", MediaType.IMAGE_JPEG_VALUE, bytes);
MockMultipartFile userId =
new MockMultipartFile("userId",
"userId",
MediaType.MULTIPART_FORM_DATA_VALUE,
objectMapper.writeValueAsString("123456").getBytes()
);
this.mockMvc.perform(multipart("/uploadPicture")
.file(userId)
.file(file)
.header(API_KEY_HEADER, API_KEY)).andExpect(status().isOk());
}
Testing the controller with the OkHttp3 client on android works seamlessly, but I can't figure out how to make that request work on the MockMvc
I expect 200 as a status code, but get 404 since, I guess, the format is not the correct one for that controller
What am I doing wrong?
It must be a typo.
In your controller, you claim the request URL to be /uploadpicture, but you visit /uploadPicture for unit test.

Spring POST method not supported

My spring application has GET Method working. Any try of creating POST method ends with the error below:
org.springframework.web.servlet.PageNotFound.handleHttpRequestMethodNotSupported Request method 'POST' not supported.
Now I'm trying to create request as simple as it can.
#RequestMapping(value="/post/", method = RequestMethod.POST)
public ResponseEntity<String> newReport(#RequestBody String aa) {
System.out.println(aa);
return new ResponseEntity<String>("User created", HttpStatus.CREATED);
}
my controller
#CrossOrigin("*")
#RestController
#RequestMapping({"/api"})
public class ReportsController
I've checked many threads of this problem, but none solves it.
You must delete '/' from the end of the service name:
#RequestMapping(value="/post", method = RequestMethod.POST)
public ResponseEntity<String> newReport(#RequestBody String aa) {
System.out.println(aa);
return new ResponseEntity<String>("User created", HttpStatus.CREATED);
}

Spring Feign client call enters exception block when it should stay in try block

Need some small help about Spring Feign client. So here is the situation,
I have 2 Spring boot services. Let’s say Service A and Service B. I have configured my Service A with Feign client through which I call the Service B method.
So here is the code for my Service A,
My FeignCleint config interface,
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello")
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
And my rest controller that uses above feign config to call the Service B method,
#RestController
public class ApiController {
#Autowired
private FeignApi feignApi;
#RequestMapping(value = "/callServiceB")
public ResponseEntity<?> companyInfo() {
ResponseEntity<?> response = new ResponseEntity("OK Response", HttpStatus.OK);
try {
response = feignApi.hello();
// Code for some other things related to application.
return response;
} catch (Exception ex) {
System.out.println("Service A Exception block reached.");
return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}
below is my controller for Service B,
#RestController
public class MyController {
#GetMapping("/hello")
public String hello() throws Exception {
if (true) {
throw new Exception("Service B Exception...");
}
return "Hello World";
}
}
And my Controller advice to handle the exception that I am manually throwing,
#ControllerAdvice
public class MyControllerAdvice {
#ExceptionHandler
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<?> handleException(Exception exception, Model model) {
return new ResponseEntity<>("Caused due to : " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Now my flow is like below,
As you can see, I am calling service B from service A using feign client. My service B is throwing an exception manually which I am catching using the controller advice and sending the exception details as an ResponseEntity back to the calling service A. So that Service A can process the details and move forward based on that.
The problem is when I hit the call from Service A using
http://localhost:8080/feign/callServiceB
The service B fails as expected. Now what I expect is that the Service A should receive the response back in form of the ResponseEntity. But what really happens is that the flow enters the exception block instead of staying in the try block. I can see this line printed,
"Service A Exception block reached."
This is what I don't understand. If I have managed the service B exception using controller advice and sent back the response to service A in form of ResponseEntity, then how come the flow of service A enters catch block. I expect it to stay in try block only as I want to process further based on the data.
Any idea, how can I get around this thing? Or is this how it will behave even when I am using controller advice to manage exceptions? What should be the expected behavior in this case?
Please advice.
By default Feign throws FeignException for any error situation.
Make use of fallback mechanism to handle failures.
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello", fallback= FeignApiFallback.class)
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
#Component
class FeignApiFallback implements FeignApi {
#Override
public ResponseEntity<?> hello() {
//do more logic here
return ResponseEntity.ok().build();
}
}
make sure you add below property to wrap methods in hystrix commands in recent releases
feign.hystrix.enabled=true
Any status other than 200, feign client will consider it as an exception and you are setting HttpStatus.INTERNAL_SERVER_ERROR in your controller advice.
You can use custom ErrorDecoder
refer https://github.com/OpenFeign/feign/wiki/Custom-error-handling

Why this externa web service call go into error only when the call is performed using Spring RestTemplate?

I am working on a Spring project implementing a simple console application that have to call an external REST web service passing to it a parameter and obtaining a response from it.
The call to this webservice is:
http://5.249.148.180:8280/GLIS_Registration/6
where 6 is the specified ID. If you open this address in the browser (or by cURL tool) you will obtain the expected error message:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<sampleid>IRGC 100000</sampleid>
<genus>Oryza</genus>
<error>PGRFA sampleid [IRGC 100000], genus [Oryza] already registered for this owner</error>
</response>
This error message is the expected response for this request and I correctly obtain it also using cURL tool to perform the request.
So I have to perform this GET request from my Spring application.
To do it I create this getResponse() method into a RestClient class:
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RestClient {
RestTemplate restTemplate;
String uriResourceRegistrationApi;
public RestClient() {
super();
restTemplate = new RestTemplate();
uriResourceRegistrationApi = "http://5.249.148.180:8280/GLIS_Registration/7";
}
public ResponseEntity<String> getResponse() {
ResponseEntity<String> response = restTemplate.getForEntity(uriResourceRegistrationApi, String.class);
return response;
}
}
Then I call this method from this test method:
#Test
public void singleResourceRestTest() {
System.out.println("singleResourceRestTest() START");
ResponseEntity<String> result = restClient.getResponse();
System.out.println("singleResourceRestTest() END");
}
But I am experiencing a very strange behavior, what it happens is:
1)The call to my external web service seems that happens (I saw it from the web services log).
2) The web service retrieve the parameter having value 7 but then it seems that can't use it as done without problem performing the request from the browser or by the shell statment:
curl -v http://5.249.148.180:8280/GLIS_Registration/7
But now, calling in this way, my webservice (I can't post the code because it is a WSO2 ESB flow) give me this error message:
<200 OK,<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>Location information not correct</error>
<error>At least one between <genus> and <cropname> is required</error>
<error>Sample ID is required</error>
<error>Date is required</error>
<error>Creation method is required</error>
</response>,{Vary=[Accept-Encoding], Content-Type=[text/html; charset=UTF-8], Date=[Fri, 05 May 2017 14:07:09 GMT], Transfer-Encoding=[chunked], Connection=[keep-alive]}>
Looking the web service log it seems that performing the call using RestTemplate it have some problem to use the retrieved ID=7 to perform a database query.
I know it looks terribly strange and you can see: "The problem is of your web service and not of the Spring RestTemplate". This is only partially true because I implemented this custom method that perform a low level Http GET call, this callWsOldStyle() (putted into the previous RestClient class):
public void callWsOldStyle() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL restAPIUrl = new URL("http://5.249.148.180:8280/GLIS_Registration/7");
connection = (HttpURLConnection) restAPIUrl.openConnection();
connection.setRequestMethod("GET");
// Read the response
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder jsonData = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonData.append(line);
}
System.out.println(jsonData.toString());
}catch(Exception e) {
e.printStackTrace();
}
finally {
// Clean up
IOUtils.closeQuietly(reader);
if(connection != null)
connection.disconnect();
}
}
Using this method instead the RestTemplate one it works fine and this line:
System.out.println(jsonData.toString());
print the expected result:
<?xml version="1.0" encoding="UTF-8"?><response><sampleid>IRGC 100005</sampleid><genus>Oryza</genus><error>PGRFA sampleid [IRGC 100005], genus [Oryza] already registered for this owner</error></response>
To summarize:
Calling my WS from the browser it works.
Calling my WS using cURL it works.
Calling my WS using my callWsOldStyle() method it works.
Calling my WS using the method that use RestTemplate it go into error when my WS receive and try to handle the request.
So, what can be the cause of this issue? What am I missing? Maybe can depend by some wrong header or something like this?
As Pete said you are receiving an internal server error (status code 500) so you should check the server side of this rest service.
In any case you can do the following for the resttemplate
create an org.springframework.web.client.RequestCallback object if
you need to do something in the request
create an org.springframework.web.client.ResponseExtractor<String>
object in order to extract your data
use the resttemplate
org.springframework.web.client.RequestCallback
public class SampleRequestCallBack implements RequestCallback
{
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException
{
}
}
org.springframework.web.client.ResponseExtractor
public class CustomResponseExtractor implements ResponseExtractor<String>
{
private static final Logger logger = LoggerFactory.getLogger(CustomResponseExtractor.class.getName());
#Override
public String extractData(ClientHttpResponse response) throws IOException
{
try
{
String result = org.apache.commons.io.IOUtils.toString(response.getBody(), Charset.forName("UTF8"));
if( logger.isInfoEnabled() )
{
logger.info("Response received.\nStatus code: {}\n Result: {}",response.getStatusCode().value(), result);
}
return result;
}
catch (Exception e)
{
throw new IOException(e);
}
}
}
REST TEMPLATE CALL
#Test
public void testStack()
{
try
{
String url = "http://5.249.148.180:8280/GLIS_Registration/6";
String response = restTemplate.execute(url, HttpMethod.GET, new SampleRequestCallBack(), new CustomResponseExtractor());;
logger.info(response);
}
catch (Exception e)
{
logger.error("Errore", e);
}
}
Angelo

get error HTTP Status 405 for rest web service POST method

I load at browser as
localhost:8080/picking/addPick get error HTTP Status 405 - Request method 'GET' not supported.
What wrong?Hope advice thanks
#Controller
#RequestMapping("/picking")
public class PickerController {
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody ArrayList getAllPickingItems()throws SQLException, ClassNotFoundException{
//....
}
#RequestMapping(value="/addPick",method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public Boolean add(#RequestBody PickingInfo pickingInfo,HttpServletResponse response){
try{
Boolean success = pickerMethod.addPickingInfo(pickingInfo);
response.setHeader("addPickingInfo", success+"");
return true;
}catch(Exception ex){
System.out.println(ex.getMessage());
}
return false;
}
}
You limited URI/picking/addPick to POST requests :
#RequestMapping(value="/addPick",method=RequestMethod.POST)
When you try to open that URI from your browser you're sending a GET request, not a POST. If you want to be able to access /picking/addPick from your browser you must either :
remove the restriction method=RequestMethod.POST
allow explicitely GET requests : method = { RequestMethod.POST, RequestMethod.GET }
If you just want to test a POST method, use SoapUI. Simply create a "New REST Project", paste your service URI, and send any type of HTTP Request you want.
You have mapped /addPick to the add method only for POST requests. Therefor GET is not mapped to anything (and in this case there is no point in mapping get to the method since you are also using #RequestBody)

Resources