Spring MVC GET/redirect/POST - redirect

Say I have 2 Spring MVC services:
#RequestMapping(value = "/firstMethod/{param}", method = RequestMethod.GET)
public String firstMethod(#PathVariable String param) {
// ...
// somehow add a POST param
return "redirect:/secondMethod";
}
#RequestMapping(value = "/secondMethod", method = RequestMethod.POST)
public String secondMethod(#RequestParam String param) {
// ...
return "mypage";
}
Could redirect the first method call to second(POST) method?
Using second method as GET or using session is undesirable.
Thanks for your responses!

You should not redirect a HTTP GET to a HTTP POST. HTTP GET and HTTP POST are two different things. They are expected to behave very differently (GET is safe, idempotent and cacheable. POST is idempotent). For more see for example HTTP GET and POST semantics and limitations or http://www.w3schools.com/tags/ref_httpmethods.asp.
What you can do is this: annotate secondMethod also with RequestMethod.GET. Then you should be able to make the desired redirect.
#RequestMapping(value = "/secondMethod", method = {RequestMethod.GET, RequestMethod.POST})
public String secondMethod(#RequestParam String param) {
...
}
But be aware that secondMethod can then be called through HTTP GET requests.

Related

Spring mvc test case with string in request header and multipart file as a request parameter for a post request

#Controller
#PostMapping("/hello/{studentName}")
public ResponseEntity<Void> method1(
#RequestMapping(value = "/upload/{studentName}", method = RequestMethod.POST)
#ResponseBody
public String saveAuto(
#PathVariable(value = "name") String name,` `
#RequestParam("file") MultipartFile myFile) {
}
}
Hi, I am new to unit test. can anyone please help me for writing test case using mockmvcbuilderrequest..
I tried this but getting 404
mockMvc.perform(MockMvcRequestBuilders.multipart("/hello/{zoneName}","com.example")
.file(file).accept(MediaType.MULTIPART_FORM_DATA_VALUE))
You have 2 options.
Change the rest path and put: "/hello/{studentName}", in this way the test will work as you have explained.
Leave the rest path "/upload/{studentName}" and change the uri in the test from "/hello/{studentName}" to "/upload/{studentName}".
I leave the way to execute the test, with the correction.
mockMvc.perform(
MockMvcRequestBuilders.multipart("/upload/{studentName}","Anu Shree")
.file(file)
.accept(MediaType.MULTIPART_FORM_DATA_VALUE)
)
I hope it helps

Correct HTTP verb to use for a business rules validation endpoint in Web API

I have the below Web API controller. Its sole responsibility is to validate the incoming document against a set of business rules and return a result. Which is the correct HTTP verb to use for this controller action?
//[Http<???>]
public IActionResult ValidateBusinessRules([FromBody BusinessDocument document)
{
var result = ValidateBusinessRules(document);
return Ok(result);
}
[FromBody] explicitly tells the model binder to check the request body for data to bind. And since only certain request allow a body then it means that it works with POST or PUT.
POST would be the default verb to use in this scenario. Taking model state into consideration the action can look like this
[HttpPost]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document) {
if(ModelState.IsValid) {
var result = ValidateBusinessRules(document);
if(result.IsValid) { //assuming result has a flag
return Ok(result);
}
return BadRequest(result);//assuming result provides relevant details.
}
return BadRequest(ModelState);
}
That way the status of the response can provide some relevant feedback about the request made.
One could argument that POST should only be used for creating a new entity but as GET is not designed to send data through the request body and the other verbs (PUT = update entity, DELETE = remove entity) don't give you a better option I would say it's OK to use POST for scenarios where you need to get some data from the server and need to send data in the request body.
I would therefore recommend you to use POST here
[HttpPost]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document)
{
var result = ValidateBusinessRules(document);
return Ok(result);
}
If you use this endpoint to validate data from form, and then you want to save them through another endpoint, I think, that the best solution would be something like this:
[HttpPost]
[Route("documents")]
public IActionResult ValidateBusinessRules([FromBody] BusinessDocument document)
{
var result = ValidateBusinessRules(document);
if (!result.IsValid)
{
return BadRequest(result);
}
var document = _documentService.Save(document);
return Ok(document);
}
For me it is odd to use POST if you don't want to create new resource.

Spring Boot REST API Endpoint Mapping best practice

I am using below endPoint URL Mapping with HTTP Methods like ( POST, DELETE, GET, PUT)
POST for Create a new Trade -
#PostMapping("/trade")
DELETE for Delete a Trade with specific id -
#DeleteMapping("/trade/{id}")
GET for Get details of specific Trade -
#GetMapping("/trade/{id}")
PUT for Update Trade details -
#PutMapping(“/trade/{id}”)
GET for Retrieve all Trade list of the collection -
#GetMapping("/trades")
Spring currently supports five types of inbuilt annotations for handling different types of incoming HTTP request methods which are GET, POST, PUT, DELETE and PATCH. These annotations are:
#GetMapping
#PostMapping
#PutMapping
#DeleteMapping
#PatchMapping
From the naming convention, we can see that each annotation is meant to handle the respective incoming request method types, i.e. #GetMapping is used to handle GET type of request method, #PostMapping is used to handle POST type of request method, etc.
if I am missing anything here Please suggest
Add API version like
#RestController
#RequestMapping("/API/V1")
public class TestController {
#RequestMapping("/greeting")
public String greeting( {
return "welcome";
}
}
For versioning there are several approaches you can use:
URI path:
Include the version number in the URL path of the endpoint.
For example v1 in /api/v1/trade:
public class TradeController {
#GetMapping("v1/trade")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping("v2/trade")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Query parameters:
Pass the version number as a query parameter with a specified name.
For example: ?version=1 in /api/trade?version=1:
public class TradeController {
#GetMapping(name = "v1/trade", params = "version=1")
public Trade tradeVersionOne() {
return new Trade("123","Trade Result");
}
#GetMapping(name = "v2/trade", params = "version=2")
public Trade tradeVersionTwo() {
return new Trade(new RealTimeTrade("123", "Real Time Trade Result"));
}
}
Custom HTTP headers:
Define a new header that contains the version number in the request.
Content negotiation:
Include the version number in the Accept header along with the accepted content type.

ReSTful service getting contradict due to path parameter value has forward slash

I have API like this-
/objectname/name
/objectname/collection/id
Both API's are indirectly related.
Problem occurs when calling first API with name value as "A/B Type". So rest controller actually calling second API rather first (/objectname/A/B Type) because forward slash. How to deal with this situation.
As a side note I am encoding the parameters values.
I developed the restful services using SpringBoot and RestTemplate.
The conflict comes by specifying the name directly in the resource path and passed to the function as a #PathVariable.
Your code looks something like this:
#RequestMapping(value = "objectname/{name}", method = RequestMethod.GET)
public String yourMethodName(#PathVariable String name){
return name;
}
What I would recommend in order to avoid this kind of conflict is (if you're allowed to modify the #RestController or #RepositoryRestResource layers) to pass the value of the object in a #RequestParam
For instance:
#RequestMapping(value = "/objectname", method = RequestMethod.GET)
public String yourMethodName(#RequestParam(name = "name", required = true) String name){
return name;
}
That said, When you are constructing your the request using RestTemplate then you should url encode your name (A%2FB%20Testing) and construct the following url:
http://localhost:8080/objectname?name=A%2FB%20Testing
I tested this locally and worked alright for me.

Async REST calls with JAX-RS

I need to create a RESTful service which should support async calls in follwing way. When user calls some method he got the http '202' code and url to poll where he can see the status of his request. Currently I use JAX-RS and its annoations:
#Path("")
public interface MyService {
#POST
#Path("/myService/{name}")
#Consumes({APPLICATION_XML, APPLICATION_JSON})
void postSomething(#PathParam("name") String name, MyObject data);
}
Such mapping would expose MyService's postSomething() method by url /myService/{name} which serves POST requests, get 'name' parameter from url and 'data' from request body.
I want that after making this PUT request client get 202 http code and some callback url to poll to get the result once method will be executed.
So the question is:
1. How to make JAX-RS return 202 code?
2. How to pass callback url to the client?
Have the postSomething method return a Response object:
public Response postSomething(#PathParam("name") String name, MyObject data) {
return Response.status(Status.ACCEPTED).build();
}
If you want the callback URI as plain-text in the HTTP Body you could do something like this:
public Response postSomething(#PathParam("name") String name, MyObject data) {
return Response.status(Status.ACCEPTED).entity("http://google.com").build();
}
For generating URIs by resource classes, have a look at UriBuilder
Use #Context HttpServletResponse servletResponse to get direct control over the servlet's response mechanism.
#PUT
#Path("/myService/{name}")
#Consumes({APPLICATION_XML, APPLICATION_JSON})
void postSomething(#PathParam("name") String name, #Context HttpServletResponse response, MyObject data) {
// ...
response.setStatus(HttpServletResponse.SC_ACCEPTED);
response.setHeader("Location", myURL);
// ...
}