Prevents HTTP method in spring-data-rest - java

I'm using spring-data-rest.
Given following repository :
#RepositoryRestResource
public interface MyRepository extends PagingAndSortingRepository<MyEntity, Long> {}
The annotation #RestResource(exported = false) on the save() method makes the framework return a 405 Method Not Allowed error when using methods POST, PUT and PATCH.
My question : How can I just return a 405 error on PUT method while POST and PATCH are still allowed for this repository ?
Thanks !

#SWiggels
Thanks for your response :)
Your solution didn't work for me... PUT is always allowed.
For others I found this one that worked :
#BasePathAwareController
public class MyEntityController {
#RequestMapping(value = "/myentity/{id}", method = RequestMethod.PUT)
public ResponseEntity<?> preventsPut() {
return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED);
}
}

You can add your allowed methods in the response of the options-probe.
#RequestMapping(method = RequestMethod.OPTIONS)
ResponseEntity<Void> getProposalsOptions() {
HttpHeaders headers = new HttpHeaders();
headers.setAllow(new HashSet<>(Arrays.asList(OPTIONS, PATCH, POST)));
return new ResponseEntity<>(headers, HttpStatus.NO_CONTENT);
}
This allows only Options, Patch, Post as request-methods. For every other tried method you get a HTTP-405-Error.

Related

How can I override a Spring Data REST method without disabling default implementations

I finally found a way to override methods of Spring Data REST with a custom implementation. Unfortunately this disables the default handling.
My Repository should contain findAll and findById exposed over the GET: /games and GET: /games/{id} respectively and save should not be exported because it is overriden by the controller.
#RepositoryRestResource(path = "games", exported = true)
public interface GameRepository extends Repository<Game, UUID> {
Collection<Game> findAll();
Game findById(UUID id);
#RestResource(exported = false)
Game save(Game game);
}
My controller should handle POST: /games, generate the game on the server and return the saved Game.
#RepositoryRestController
#ExposesResourceFor(Game.class)
#RequestMapping("games")
public class CustomGameController {
private final GameService gameService;
public CustomGameController(GameService gameService) {
this.gameService = gameService;
}
#ResponseBody
#RequestMapping(value = "", method = RequestMethod.POST, produces = "application/hal+json")
public PersistentEntityResource generateNewGame(#RequestBody CreateGameDTO createGameDTO, PersistentEntityResourceAssembler assembler) {
Game game = gameService.generateNewGame(createGameDTO);
return assembler.toFullResource(game);
}
}
However when I try to GET: /games it returns 405: Method Not Allowed but POST: /games works as intended. When I change the value of the generateNewGame mapping to "new" all three requests work. But POST: /games/new is no RESTful URL Layout and I would rather avoid it. I don't understand why I get this behaviour and how I may solve it. Does anybody have a clue?
Use #BasePathAwareControllerannotation above your controller to preserve default spring data rest paths and add new custom path base on your need. Although overwrite default spring data rest path.
#BasePathAwareController
public class CustomGameController {
private final GameService gameService;
public CustomGameController(GameService gameService) {
this.gameService = gameService;
}
#ResponseBody
#RequestMapping(value = "", method = RequestMethod.POST, produces =
"application/hal+json")
public PersistentEntityResource generateNewGame(#RequestBody CreateGameDTO
createGameDTO, PersistentEntityResourceAssembler assembler) {
Game game = gameService.generateNewGame(createGameDTO);
return assembler.toFullResource(game);
}
}
Maybe you can do something we usually do in Linux. Set a fake path and link to it.
POST /games ==> [filter] request.uri.euqal("/games") && request.method==POST
==> Redirect /new/games
What you see also is /games.
Don't use /games/new, it may be conflict with things inner Spring.

HTTP Status 415 – Unsupported Media Type when doing POST request on Spring MVC

I am trying to send a post request on a simple Spring MVC web app and use RequestBody in my controller to convert the JSON into a Java Object but for whatever reason, I keep getting HTTP Status 415 – Unsupported Media Type. I have spent a lot of time trying to find a solution to this but nothing seems to be working.
The get method in my Controller seems to be working fine. This is my original code
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(value = "/users", method = RequestMethod.POST)
public class MyControllerAgain {
#RequestMapping(method = RequestMethod.POST, consumes = "application/json")
public void handleJson(#RequestBody Contact c) {
System.out.println(c);
}
#RequestMapping(method = RequestMethod.GET, consumes = "application/json")
public void handleGet() {
System.out.println("a");
}
}
This is my Contact
public class Contact {
int id;
public String name;
public int number;
public Contact(){}
// Getters and setters
}
I am sending a request with Postman and this is what it looks like
POST /users HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Accept: application/json
Cache-Control: no-cache
Postman-Token: 511c9e03-4178-380d-58f8-db24a3245b9e
{
"id":1,
"name":"something",
"number":1
}
I have also tried including Jackson dependencies in my pom.xml .
I have tried altering consume value in #RequestMapping annotation and I have tried all combinations of headers accept and Content type in my request.
Also, If I use #ModelAttribute instead of #RequestBody, then everything works fine except all the fields in Contact class are null.
Here is the github link - https://github.com/Sanil2108/test_springmvc
To me it looks like that the jpa anotations are messing up the json deserialization.
The error returned from the spring server could be misleading.
Try using plain object with setters and getters and see if this changes anything.
You should search for some exceptions in the logs.
RequestMapping annotation not only has the consumes but also produces.
But to avoid all these settings for HTTP REST, you can use RestController annotation and GetMapping, PostMapping etc..
You can find an example in my github
Add a mapping to the handleGet method, for example:
#RequestMapping(value = "/get", method = RequestMethod.GET, consumes = "application/json")
public void handleGet() {
System.out.println("a");
}
--UPDATE--
Remove the consumes = "application/json" part from you GET call. It sees that both requests that listen to "/users" can consume json data, but one is a GET and the other one is a POST.
--2nd UPDATE--
This will definitely work. Tested.
#RestController
#RequestMapping(value = "/users", method = RequestMethod.POST)
public class ContactController
{
#RequestMapping(method = RequestMethod.POST, consumes = "application/json")
public void handleJson(#RequestBody Contact c)
{
System.out.println(c);
}
}
Tried everything but couldn't it to work. Maybe I was making a silly mistake somewhere or there was something seriously wrong with my configuration. Anyway, I tried to make it work with Spring boot and it worked fine. For anyone who is interested, Here is the github link - https://github.com/Sanil2108/spring_hibernate/tree/master/spring_boot1
Also, thanks to everyone who tried to help!

Testing Spring MVC POST with IntelliJ REST client causes 415

I'm developing an application in Java/Spring MVC and have no problem with testing my GET methods. The problem occur then I try to test the POST using #RequestBody.
The error:
HTTP 415 The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
I created a simple test to show my problem:
#RestController
#RequestMapping("/test")
public class ConcreteTestController implements TestController {
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
#Override
public void add(#RequestBody Dummy dummy) {
System.out.println(dummy);
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public Dummy get() {
Dummy dummy = new Dummy();
dummy.setName("apa");
return dummy;
}
}
The Dummy class is very simple:
public class Dummy {
private String name;
public Dummy() {}
// Omitted setters and getters.
}
The jsonresponse from the GET looks like this:
{"name":"apa"}
I'm starting the IntelliJ REST client and using the json above as request body. I've tried using both application/json and / under Accept in the header with no difference in result.
Any idea what could cause this? I'm stuck and would appreciate help.
By default you have to add Content-Type manually in the REST client in IntelliJ. I had forgotten to do so and to set it to application/json. After having done so it is working fine.

Spring Rest RequestMethod.GET returns 400 Bad Request for #RequestParam required=true when missing

I am new to Spring and Rest Endpoints.
I have a controller, which accepts #RequestParam and returns a JSON Response.
By default the #RequestParam required = "true", which is how I need it.
I am using Spring 3.1.3
This is my Get Method in the controller:
#Controller
#RequestMapping("/path")
public class MyController{
#RequestMapping(value = "/search/again.do", method = RequestMethod.GET, produces = {
"application/json"
})
public ResponseEntity<?> find(#RequestParam(value = "test", required = true) final String test) {
return new ResponseEntity<String>("Success ", HttpStatus.OK);
}
}
When I send a get with the request param it hits the endpoint , which is how I expect.
Example : path/search/again.do?test=yes
Everything is perfect.
This is where I am having issue:
When I send a Get with that value missing:
Example: path/search/again.do
I get a 400 Bad Request. May be this is correct.
But what I want to achieve is. When the required value is missing in the GET request.
I can send a JSON response as that #RequestParam Value test is missing.
Can anybody guide me how to achieve this.
I am not sure what I am missing.
Thanks in advance.
If you look closely at your code, you'll see that the answer is staring right at you. Just change required to false and you should be good to go. When the user doesn't provide a value for GET parameter test, then you can return a special message.
#Controller
#RequestMapping("/path")
public class MyController {
#RequestMapping(value = "/search/again.do", method = RequestMethod.GET, produces = {
"application/json"
})
public ResponseEntity<?> find(#RequestParam(value = "test", required = false) final String test) {
if (test == null) {
return new ResponseEntity<String>("test parameter is missing", HttpStatus.OK);
}
else {
return new ResponseEntity<String>("Success ", HttpStatus.OK);
}
}
}
Solution 1: You can use custom #ExceptionHandler in your controller, e.g
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<?> paramterValidationHandler(HttpServletResquest request){
//validate the request here and return an ResponseEntity Object
}
Solution 2: Would be custom spring ErrorController which I never have tried myself but it possible to override it.
Solution 3: You can write an ControllerAdvice for a global controller exception handling.
Well if you set the parameter test is required. U just can't send the request without that param. Try to change the param required= false and handle the missing param in the method. You can us something likeif(test==null) throw new Exception("Param test missing")

How use PUT method in Springboot Restcontroller?

Am developing an application using Spring boot.I tried with all representations verbs like GET, POST , DELETE all are working fine too. By using PUT method, it's not supporting in spring boot. Whether I need to add any new configuration.
Put method works only the request not have any parameters. If i add any query parameter or form data it doesnt work. Kindly any expertize will help me to solve this issue.
#RequestMapping("/student/info")
#RequestMapping(method = RequestMethod.PUT)
public #ResponseBody String updateStudent(#RequestParam(value = "stdName")String stdName){
LOG.info(stdName);
return "ok";
}
Request method 'PUT' not supported
This code will work fine. You must specify request mapping in class level or in function
level.
#RequestMapping(value = "/student/info", method = RequestMethod.PUT)
public #ResponseBody String updateStudent(#RequestBody Student student){
LOG.info(student.toString());
return "ok";
}
Have you tried the following Request Mapping:
#RequestMapping(value = "/student/info", method = RequestMethod.PUT)
There's no need to separate the value and the Request Method for the URI.
Since Spring 4.3 you can use #PutMapping("url") : https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/PutMapping.html
In this case it will be:
#PutMapping("/student/info")
public #ResponseBody String updateStudent(#RequestParam(value = "stdName")String stdName){
LOG.info(stdName);
return "ok";
}
I meet the same issue with spring boot 1.5.*,I fixed it by follow:
#RequestMapping(value = "/nick", method = RequestMethod.PUT)
public Result updateNick(String nick) {
return resultOk();
}
Add this bean
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory(){
#Override
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
connector.setParseBodyMethods("POST,PUT,DELETE");
}
};
}
see also
https://stackoverflow.com/a/25383378/4639921
https://stackoverflow.com/a/47300174/4639921
you can add #RestController annotation before your class.
#RestController
#RequestMapping(value = "/v1/range")
public class RangeRestController {
}

Categories

Resources