Spring MVC http REST explanation, POST + GET - java

I have a webapp running on Spring SVM running in this direction:
http://localhost:8085/mongodb
The main controller that handles GET and POST request look looks like this:
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
private static final String DATA_FIELD = "Data";
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
return "home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public ModelAndView createDomain(#RequestBody Domain Domain, HttpServletResponse httpresponse, WebRequest request) throws Exception{
logger.info("Welcome home! The client locale is {}.", "FUCK YOU2");
Domain createdDomain = Domain;
logger.info("Post Received" + Domain.toString());
//Create HTTP Response for the POST request
httpresponse.setStatus(HttpStatus.CREATED.value());
MongoOperations mongoOps = new MongoTemplate(new Mongo(),"rest_database");
mongoOps.insert(Domain);
System.out.println("PRINTED");
return new ModelAndView("post", DATA_FIELD, createdDomain);
}
}
Apparently the code is working. When I use the Chrome Extension Advanced Rest Client (here) to send POST request the client sends a POST then a GET every time, so I always get the page in the GET method.
The weird thing is that google chrome network developer tool shows a POST request pending and then a GET request that returns OK, the network debugger shows this:
https://dl.dropboxusercontent.com/u/7437425/network_debugger.png
When I send this request:
https://dl.dropboxusercontent.com/u/7437425/post_response.png
I would like to know if this is the standard way of sending a POST, by sending a GET shortly afterwards.
A funny thing is that my console never shows the result of this commands:
logger.info("Welcome home! The client locale is {}.", "FUCK YOU2");
System.out.println("PRINTED");
This leads me to believe that actually the method that handle POST requests is not running at all.
Thanks!

The method createDomain is not configured for handling post requests, but only put request.
What you are seeing in the debugger is that upon the post request something answers with a redirect to another URL where a get is performed. Yes, this is a pretty standard way of doing things. But this behavior does not come from the code you are showing, but is probably encoded somewhere else. Possibly in some default controllers that handle almost everything.

Related

How to return Jersey MVC Viewable after authentication redirect?

TLDR: My method requires 2 redirects/forwards to work (1 for authentication and 1 to serve the jsp page). How can I resolve both redirects/forwards (or make it a non-requirement) so as to not run into the error, java.lang.IllegalStateException: Cannot forward after response has been committed.
For more context:
I have a java servlet with a method that looks something like the following:
#GET
#Path("/test")
#Authenticate
public Viewable test(#Context HttpServletRequest request, #Context HttpServletResponse response) {
Map<String, Object> model = createModel();
return new Viewable("/somePath/jspFile", model);
}
The #Authenticate annotation intercepts the call to do some Open ID Connect type authentication which results in the user being forwarded to a different server for all authentication needs. If the user is authenticated, they are redirected back to my application.
However, when hitting the url for this method, I am getting java.lang.IllegalStateException: Cannot forward after response has been committed. I don't know too much about using this Viewable class, but based on the fact that I don't run into that error when returning String/void/whatever else, I assume returning a new Viewable needs to do some forwarding that results in the user seeing the jsp page.
I've read the main SO post about this error, but I am unsure how to apply the fixes to my current problem. For example, I don't know how I would apply something like the following fix:
protected void doPost() {
if (someCondition) {
sendRedirect();
} else {
forward();
}
}
The fix assumes that I can I can either redirect OR forward, but my current method needs a redirect for authentication AND a forward/redirect to serve the jsp page. Maybe there's an obvious fix I'm missing that doesn't require a complete rehaul of the current code?
Edit: It would be nice if I could check if the user was authenticated first, but I assume using this annotation at all automatically entails an initial redirect
Edit: It looks like the user is redirected for the initial login authentication, but does not need to be redirected again after being authenticated once due to SSO
Ok based on some preliminary testing, it seems like the following solution has worked for me:
Check if the user has already been authenticated
Return a Response rather than a Viewable.
Since the user only needs to be redirected the first time for authentication, I can return an empty/meaningless response as a placeholder. And then once the user has been authenticated and is returned to my app, I can return a Viewable wrapped in a Response object.
So the code would look something like the following:
#GET
#Path("/test")
#Authenticate
public Response test(#Context HttpServletRequest request, #Context HttpServletResponse
response) {
Map<String, Object> model = createModel();
if (userIsAuthenticated()) {
return Response.status(401).build();
} else {
return Response.ok(new Viewable("/somePath/jspFile", model)).build();
}
}

POST doesn't work Spring java

I have an web application and I'm trying to creat a simple POSt method that will have a value inside the body request:
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
#CrossOrigin(origins = "http://localhost:3000")
public #ResponseBody String updateTtl(#RequestBody long ttl) {
/////Code
}
My request which I call from some rest client is:
POST
http://localhost:8080/cachettl
Body:
{
"ttl": 5
}
In the response I get 403 error "THE TYPE OF THE RESPONSE BODY IS UNKNOWN
The server did not provide the mandatory "Content-type" header."
Why is that happening? I mention that other GET requests are working perfectly.
Thanks!
Edit:
When I tried it with postman the error message I got is "Invalid CORS request".
Spring application just doesn't know how to parse your message's body.
You should provide "header" for your POST request to tell Spring how to parse it.
"Content-type: application/json" in your case.
You can read more about http methods here: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_and_retrieving_form_data
Updated:
Just in case of debug, remove useless annotations to test only POST mechanism. Also, change types of arg and return type. And try to use case-sensitive header.
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
public void updateTtl(#RequestBody String ttl) {
System.out.println("i'm working");
}
Since the error is about the response type, you should consider adding a produces attribute, i.e :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
Since you are also consuming JSON, adding a consumes attribute won't hurt either :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
The error message is slightly misleading. Your server code is not being hit due an authentication error.
Since you say spring-security is not in play then I suspect you're being bounced by a CORS violation maybe due to a request method restriction. The response body generated by this failure (if any at all) is automatic and will not be of the application/json type hence the client failure. I suspect if you hit the endpoint with something that doesn't care for CORS such as curl then it will work.
Does your browser REST client allow you to introspect the CORS preflight requests to see what it's asking for?

Spring Cloud Zuul: RedirectView redirecting to service and not gateway

I'm a bit new to microservices and Spring. I have Spring Cloud microservices (ports: 8xxx-8xxx) with a Zuul gateway running on port 9000. There's a method inside a controller on a UI service which should do a login and then return to a index.html page:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public RedirectView doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return new RedirectView("index");
}
The problem is that when above method completes it returns an url of the microservice itself localhost:8888/index but not localhost:9000/services/ui/.
If I use a simpler method:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public String doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return "index";
}
This returns correctly an url of gateway localhost:9000/services/ui/do-login but with a /do-login which I do not need.
Maybe I can get rid of /do-login/ part of url? Or maybe there is a solution for the incorrect redirect?
Thanks in advance!
If you use relative path like in return "index"; the result of the POST request sent to http://localhost:9000/services/ui/do-login will include URLs to http://localhost:9000/... unless coded otherwise in the jsp / freemarker / thymeleaf file.
If you want to get rid of the do-login, you would need to implement what's called a Redirect After Post (or redirect after form submit) approach so that a page refresh doesn't resubmit the form. If you take this approach, which seem what you were doing when using: return new RedirectView("index");, I can think of a couple ways of fixing the URL and set it to the proxy host.
1) http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/RedirectView.html, there are a couple of constructors that takes a host parameter, you would need to inject the proxy host in the controller class and most-likely in every controller class that implements Redirect After Post.
2) http://tuckey.org/urlrewrite/, include UrlRewriteFilter and configure rules to rewrite from webapp host to proxy host when webapp http status code response is 302. With this approach it would only be once rule and no need to inject proxy host to controller classes or change the return new RedirectView("index");`
3) Maybe this rewriting is implemented in Zuul and you don't need include and configure UrlRewriteFilter as suggested in 2).
As a side note, I have configured Nginx's proxy_pass to a Java webapps (where I implemented Redirect After Post) in the past and I don't recall having this issue. Will have to take a look at both UrlRewriteFilter and Nginx config files to expand on this.
I found that this (thanks to answer in here: Spring redirect url issue when behind Zuul proxy) seems to work as required (but is considered a 'workaround'):
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public void doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity,
final Model model,
HttpServletResponse servletResponse) throws IOException {
...
String rUrl = ServletUriComponentsBuilder.fromCurrentContextPath().path("/").build().toUriString();
servletResponse.sendRedirect(rUrl);
}

Jersey - Redirect after POST to outside URL

I'm using Jersey to create REST API. I have one POST method and as a response from that method, the user should be redirected to a custom URL like http://example.com that doesn't have to be related to API.
I was looking at other similar questions on this topic here but didn't find anything that I could use.
I'd suggest altering the signature of the JAX-RS-annotated method to return a javax.ws.rs.core.Response object. Depending on whether you intend the redirection to be permanent or temporary (i.e. whether the client should update its internal references to reflect the new address or not), the method should build and return a Response corresponding to an HTTP-301 (permanent redirect) or HTTP-302 (temporary redirect) status code.
Here's a description in the Jersey documentation regarding how to return custom HTTP responses: https://jersey.java.net/documentation/latest/representations.html#d0e5151. I haven't tested the following snippet, but I'd imagine that the code would look something like this, for HTTP-301:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.seeOther(targetURIForRedirection).build();
}
...or this, for HTTP-302:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.temporaryRedirect(targetURIForRedirection).build();
}
This worked for Me
#GET
#Path("/external-redirect2")
#Consumes(MediaType.APPLICATION_JSON)
public Response method2() throws URISyntaxException {
URI externalUri = new URI("https://in.yahoo.com/?p=us");
return Response.seeOther(externalUri).build();
}

Spring REST service returns HTTP 400 status

I have a REST service written with the Spring Framework v 3.2.0
Here is the Controller:
#Controller
#RequestMapping("/volunteer")
public class VolunteerController {
//several methods not included (all HTTP GET & working properly)
#RequestMapping(value="/{volunteerId}/assignments/{sessionId}",
method=RequestMethod.PUT, params={"worked"})
#ResponseStatus(HttpStatus.NO_CONTENT)
public void setWorkedFlag(#PathVariable int volunteerId,
#PathVariable int sessionId,
#RequestParam int worked) {
assignmentMapper.setWorked(volunteerId, sessionId, worked);
}
}
When I submit a PUT request with this URL:
http://localhost:8080/volunteer/298/assignments/1?worked=true
I get a 400 restonse with the message : The request sent by the client was syntactically incorrect.
I can also confirm that the underlying resource has not need modified.
I have compared the annotations against the request URL and can't see anything obviously wrong. Also, the other methods (omitted here) all work correctly, so the Application Context has been set up OK.
Any ideas?

Categories

Resources