405 MethodNotAllowed is returned instead of what is specified in ResponseStatusException() - spring

I have a very simple endpoint
#PostMapping("/exception")
public String exception() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
in 2 different machines. On the first machine this code is in a very simple spring boot app and it works as it is supposed to be working - when invoked, it returns 400 BAD_REQUEST. On the second machine, I have real spring boot project, with a lot of stuff. There, instead of having BAD_REQUEST returned, i get 405 MethodNotAllowed.
I don't even know what can be causing this behavior. Do you have any idea what is the case?
I am attaching a screenshot of the postman request that I use.
Postman screenshot
The whole controller:
package com.xxx.service.max.web.controller;
import com.xxx.service.max.model.context.UserContext;
import com.xxx.service.max.services.cas.CustomerAccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import static com.xxx.service.max.constant.Constants.MY_ACCOUNT_X_REST;
#RestController
#RequestMapping(MY_ACCOUNT_X_REST)
public class ChangeLocaleController {
private static final Logger LOG = LoggerFactory.getLogger(ChangeLocaleController.class);
private UserContext userContext;
private CustomerAccountService customerAccountService;
#PostMapping("/exception")
public String exception() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
#Autowired
public void setUserContext(UserContext userContext) {
this.userContext = userContext;
}
#Autowired
public void setCustomerAccountService(CustomerAccountService customerAccountService) {
this.customerAccountService = customerAccountService;
}
}

Make sure you are sending a POST request.
The 405 Method Not Allowed error occurs when the web server is configured in a way that does not allow you to perform a specific action for a particular URL. It's an HTTP response status code that indicates that the request method is known by the server but is not supported by the target resource.
Source
If you are simply entering the URL in your browser that is a GET request and you would get a 405.

Related

Spring Boot Tries to Access A Post Request URL but shows GET not supported

I just started to learn Spring Boot today, and I wanted to create a GET/POST request for my Spring Boot Project. When I tried to access the URL that has the post request it shows 405 error saying that "Request method 'GET' not supported".
I think it is something wrong about my code for the POST request, but I don't know where I did wrong. I tried to search for the a tutorial that teaches how to write a proper GET/POST request, so I couldn't find anything good.
If you guys have any good website that teaches basic HTTP requests in Spring Boot, that will be great. I tried to find answers at StackOverflow, but I didn't find anything answers.
The Spring Boot project I have been using is the one from the official Spring.io website: https://spring.io/guides/gs/rest-service/
I wanted to call the POST request for my project so I have a better understanding of the HTTP.
Here is the source code for the controller:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.*;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
// GET Request
#RequestMapping(value="/greeting", method = GET)
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(), name);
}
#RequestMapping(value = "/testpost", method = RequestMethod.POST)
public String testpost() {
return "ok";
}
}
Here is the source code for the Application:
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And here is the source code for the Greeting Object
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;
}
}
I can get the GET request working by using the "/greeting" URL.
I tried to access the "/testpost" url but it shows 405 error that the GET method is not supported.
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
If you try to open the http://localhost:8080/testpost by directly opening in browser, it won't work because opening in browser makes a GET request.
I am not sure how you are trying to do a post request, I tried to do the same post request from postman and able to get the response. Below is the screenshot.
It looks like you are trying to make post request directly from web browser which will not work.
When you hit a URL directly from web browser address bar, it is considered as GET request. Since in your case, there is no GET API as /testpost , it is giving error.
Try to use rest client such as Postman or curl command to make post request.
I tried your post end-point with postman and it is working properly. PFA snapshot for your reference.
Hope this helps.
From where you are trying POST request. If from browser windows you calling POST call, then it will not work, browser will send only GET request. Have you tried from postman or from UI side. It will work.

Connect to spring backend error

I want try do a Post request from my frontend (Angular 2) to my backend (Spring). But I can't.
The error:
GET http://localhost:8080/loginPost 405 ()
Failed to load http://localhost:8080/loginPost: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.0.190:4200' is therefore not allowed access. The response had HTTP status code 405.
My Angular Service:
import {Injectable} from "#angular/core";
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Login } from'../data-login/models/login.model';
#Injectable()
export class LoginService{
private loginUrl = 'http://localhost:8080/loginPost'; // URL to web API
private headers = new Headers({'Content-Type': 'application/json'});
constructor(private http: Http){}
loginQuery(login: Login){
console.log(login.id);
return this.http.request(this.loginUrl,JSON.stringify(login));
}
}
My Spring Backend Code:
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
#RestController
public class LoginProvider {
#CrossOrigin(origins = "http://192.168.0.190:4200")
#PostMapping(value="/loginPost", consumes = { MediaType.APPLICATION_JSON_UTF8_VALUE })
public ResponseEntity<?> verifyLogin(#RequestBody String maoe){
System.out.println(maoe);
return new ResponseEntity<>(HttpStatus.OK);
}
}
I need to read the Json sent by my frontend, checking and responding with OK, no problems. But I can not read Json. I'm trying to store it in the variable "maoe"
You are trying to do send a GET request to a resource that accepts only POST requests. That is why you are getting a 405 response. Change either your rest service or angular http service to have both matching request types.

Implement Best-Practice Error Message in Spring REST Controller

I am writing a server-side REST application for a mobile app. I have been trying to setup an exception handler which follows the explanation here, where instead of showing some HTTP error page, the client receives a JSON object similar to this one:
{
"status": 404,
"code": 40483,
"message": "Oops! It looks like that file does not exist.",
"developerMessage": "File resource for path /uploads/foobar.txt does not exist. Please wait 10 minutes until the upload batch completes before checking again.",
"moreInfo": "http://www.mycompany.com/errors/40483"
}
I have modeled my exception on those detailed in the guide, and they seem to be working well (the custom errors are being shown in the console). But I got stuck at this point, because I don't know where I'm supposed to put the bean configuration.
Given that I have all my exception handlers, resolvers, etc., I thought I'd try go around it differently. At this point I would still get Spring's Whitelabel error page when I entered an invalid HTTP request, but this time with my custom error messages from my exceptions. So I figured if I tried to implement my own ErrorHandler as explained here, I might be able to construct the JSON objects using Gson or something, instead of the way the previous article went about it.
I tried to get a bare minimum ErrorHandler working:
package com.myapp.controllers;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
#RestController
public class ErrorMessageController implements ErrorController {
private static final String ERROR_PATH = "/error";
#Override
public String getErrorPath(){
return ERROR_PATH;
}
#RequestMapping(value = ERROR_PATH)
public String renderErrorPage(HttpServletRequest request){
String errorPage = (String) request.getAttribute("javax.servlet.error.status_code");
return errorPage;
}
}
So I expected to get something like a solitary 404 appearing on the webpage. But instead I'm getting a Tomcat error page:
Why is this? I'd appreciate any help.
This happens because request.getAttribute("javax.servlet.error.status_code") should be an Integer and you're casting it as a String. This causes an error during the error handling, which pops up the default Tomcat error handler.
If you cast it as an int, it will work:
#RequestMapping(value = ERROR_PATH)
public int renderErrorPage(HttpServletRequest request){
int errorPage = (int) request.getAttribute("javax.servlet.error.status_code");
return errorPage;
}
Alternatively, if you just want to return certain JSON structure, you could use #ExceptionHandler methods in stead of implementing an ErrorController.
For example, let's say you have the following controller:
#GetMapping
public String getFoo() throws FileNotFoundException {
throw new FileNotFoundException("File resource for path /uploads/foobar.txt does not exist");
}
If you want to handle all FileNotFoundExceptions in a particular way, you could write a method with the #ExceptionHandler annotation:
#ExceptionHandler(FileNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse notFound(FileNotFoundException ex) {
return new ErrorResponse(HttpStatus.NOT_FOUND.value(), 40483, "Oops! It looks like that file does not exist.", ex.getMessage(), "http://www.mycompany.com/errors/40483");
}
In this case, ErrorResponse is a POJO containing the fields you want. If you want to re-use this for all your controllers, you can put this in a #ControllerAdvice.

Consuming MULTIPART_FORM_DATA and application/x-www-form-urlencoded Media Types in a method of jersey servlet

I have a method in jersey servlet which consumes both MULTIPART_FORM_DATA and application/x-www-form-urlencoded Media Types, In my request I am sending some parameters along with a file in the file input stream.
Here is my method
#POST
#Path("/upload")
#Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_FORM_URLENCODED})
#Produces(MediaType.TEXT_PLAIN)
public String uploadFile(MultivaluedMap<String,String> requestParamsPost,#FormDataParam("file") InputStream fis,
#FormDataParam("file") FormDataContentDisposition fdcd){
//some code goes here
}
But my problem is when I start my server after making the mapping of the servlet in web.xml, I get some severe exceptions
SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 0
SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 1
SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 2
Is it somehow possible to consume two Media Types in one method at single endpoint?
Sending a file Parameter is necessary in every request?
The reason for the error is the MultivaluedMap parameter. Jersey doesn't know what to do with it. You can only have one entity type per method. In your method you are trying to accept two different body types in the request. You can't do that. I don't even know how you plan on sending that from the client.
The application/x-www-form-urlencoded data needs to be part of the multipart body. So you can do
#Consumes({MediaType.MULTIPART_FORM_DATA})
public String uploadFile(#FormDataParam("form-data") MultivaluedMap<String,String> form,
#FormDataParam("file") InputStream fis,
#FormDataParam("file") FormDataContentDisposition fdcd){
That would work. The only thing is, you need to make sure the client set the Content-Type of the form-data part to application/x-www-form-urlencoded. If they don't, then the default Content-Type for that part will be text/plain and Jersey will not be able to parse it to a MultivaluedMap.
What you can do instead is just use FormDataBodyPart as a method parameter, then explicitly set the media type. Then you can extract it to a MultivaluedMap. This way the client doesn't need to be expected to set the Content-Type for that part. Some clients don't even allow for setting individual part types.
Here's an example using Jersey Test Framework
import java.util.logging.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
public class MultipartTest extends JerseyTest {
#Path("test")
public static class MultiPartResource {
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response post(#FormDataParam("form-data") FormDataBodyPart bodyPart,
#FormDataParam("data") String data) {
bodyPart.setMediaType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
MultivaluedMap<String, String> formData = bodyPart.getEntityAs(MultivaluedMap.class);
StringBuilder sb = new StringBuilder();
sb.append(data).append(";").append(formData.getFirst("key"));
return Response.ok(sb.toString()).build();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(MultiPartResource.class)
.register(MultiPartFeature.class)
.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
#Override
public void configureClient(ClientConfig config) {
config.register(MultiPartFeature.class);
}
#Test
public void doit() {
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.field("data", "hello");
multiPart.field("form-data", "key=world");
final Response response = target("test")
.request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA));
assertEquals("hello;world", response.readEntity(String.class));
}
}
If you look at the logging, you will see the request as
--Boundary_1_323823279_1458137333706
Content-Type: text/plain
Content-Disposition: form-data; name="data"
hello
--Boundary_1_323823279_1458137333706
Content-Type: text/plain
Content-Disposition: form-data; name="form-data"
key=world
--Boundary_1_323823279_1458137333706--
You can see the Content-Type for the form-data body part is text/plain, which is the default, but in the server side, we explicitly set it before Jersey parses it
public Response post(#FormDataParam("form-data") FormDataBodyPart bodyPart,
#FormDataParam("data") String data) {
bodyPart.setMediaType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
MultivaluedMap<String, String> formData = bodyPart.getEntityAs(MultivaluedMap.class);

Login in Extjs using java spring Bean

I am trying to authenticate a user's username and password using an Ajax call.The parameters are passed to java bean and it should return a success or a failure message string to the caller but it shows a Servlet service error.The code is as follows:
package com.eits.portal.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import com.eits.portal.service.OrgTreeService;
public class UserLoginController extends MultiActionController {
OrgTreeService orgTreeService;
ModelAndView mv;
public String getLoginStatus(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
throws Exception{
//paramHttpServletResponse.setContentType("application/json");
String userId = paramHttpServletRequest.getParameter("user");
String password = paramHttpServletRequest.getParameter("password");
String s="";
System.out.println("---node"+userId);
System.out.println("---node"+password);
String us="admin";
String pass="admin";
if(userId.equals(us)&& password.equals(pass)){
s="success";
System.out.println("success ");
}else{
s="failure";
System.out.println("unsuccessful");
}
return s;
}
public void setOrgTreeService(OrgTreeService paramOrgTreeService)
{
this.orgTreeService = paramOrgTreeService;
}
}
The code is pretty simple and it does print success on passing the right username and password in the IDE console , but the same string is not passing to the caller function for the front end.Here is a snapshot of the error I am getting :-
http://gyazo.com/12b386a9e586fc0ee5514800c9822071
Is this a right way to pass the success to the caller method , or should i pass an object instead of the string?
Firstly, if you are getting a 404 (Not Found) then you should check that you are calling the correct URL and that your servlet request mapping configuration is specified correctly, e.g. via #RequestMapping annotation if using Spring.
Then, assuming you are making a request similar to below, you can return a Json string from your controller method that will be returned in the response and can be decoded into an object, assuming you may want to pass back more details than simply "success" or "failure".
Ext.Ajax.request({
url: '/someUrl',
params: {
user: 'username',
password: 'password'
},
callback: function(options, success, response) {
if (success) {
var obj = Ext.decode(response.responseText);
....
}
}
});

Resources