Redirecting to a page using restful methods? - java

I've created a page which asks user to fill some form fields and when he submits, the form is sent to a Restful method which you can see below:
#POST
#Path("addUser")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void addUser(#FormParam("username") String username,
#FormParam("password") String password,
#FormParam("id") String id,
#FormParam("group_name") String groupName,
#FormParam("authority_name") String authorityName,
#FormParam("authority_id") String authorityId
)
{
//Something will be done here
}
How can I redirect the user at the end of this function to (let's say) index.jsp?

change your code like this, the addUser() should return a Response Object
public Response addUser(#FormParam("username") String username,
#FormParam("password") String password,
#FormParam("id") String id,
#FormParam("group_name") String groupName,
#FormParam("authority_name") String authorityName,
#FormParam("authority_id") String authorityId
)
{
//Something will be done here
java.net.URI location = new java.net.URI("../index.jsp?msg=A_User_Added");
return Response.temporaryRedirect(location).build()
}

Create a URI using javax.ws.rs.core.UriBuilder that maps the parameters and other data you want to preserve. Then use Response.temporaryRedirect to return a redirect to the client and pass it the URI you’ve built.

Finally I come to this conclusion that there are no other way than what I did:
So here is my solution:
try {
java.net.URI location = new java.net.URI("../index.jsp?msg=A_User_Added");
throw new WebApplicationException(Response.temporaryRedirect(location).build());
} catch (URISyntaxException e) {
e.printStackTrace();
}
By adding this block to my code, I got what I needed. Hope it helps you as well.

See below the usage of redirecting in web services:
public class LoginWebService {
#POST
#Path("/check")
public Response checkDetails(#FormParam("name") String name,#FormParam("pass") String pass ) throws URISyntaxException {
URI uri = new URI("/login/success");
URI uri2= new URI("http://localhost:9090/NewWebServiceproject/new/login/failure");
if(name.equals("admin") && pass.equals("pass"))
//#Path("http://localhost:8010/NewWebServiceproject/new/login/success");
{
return Response.temporaryRedirect(uri).build();
//Response.seeOther(uri);
//return Response.status(200).entity("user successfully login").build();
}
else
{
return Response.temporaryRedirect(uri2).build();
//Response.seeOther(uri2);
//return Response.status(200).entity("user logon failed").build();
}
}
#POST
#Path("/success")
public Response successpage()
{
return Response.status(200).entity("user successfully login").build();
}
#POST
#Path("/failure")
public Response failurepage()
{
return Response.status(200).entity("user logon failed").build();
}
}

It is not good idea to use the "WebApplicationException" in order to redirect the request. in Jersey (2.4.1) you should be able to redirect the request via the normal servlet way, (request.getServletContext().getRequestDispatcher().forward() or just response.sendRedirect())
The following is how Jersey process the request
org.glassfish.jersey.servlet.ServletContainer.service(HttpServletRequest request, HttpServletResponse response)
requestScope.runInScope
final ContainerResponse response = endpoint.apply(data)
methodHandler.invoke(resource, method, args);
Responder.process(ContainerResponse);
That methodHandler is your REST service class, method is the function in that service class.
The step to redirect page become straitforward
Get the (request, response) through Jersey injection (#Context HttpServletRequest request, #Context HttpServletResponse response) in class field or function parameter
Call request.getServletContext().getRequestDispatcher() to get the dispatcher for "forward"
or use Response.sendRedirect(url)
Once you application is returned (just null), Jersey will try to process the result in the "Responder.process(ContainerResponse)". In this step, it will use response to set status (204 no contents for your null return).
So the key point here is you must finalize/close response object before return from your service function. Otherwise, Jersey may overwrite your output.
Small tips on why "WebApplicationException" can overwrite Jersey repsponse. It is because org.glassfish.jersey.server.ServerRuntime.mapException() will use the "webApplicationException.getResponse()" as the return response result.

Related

How to pick values from a redirected_url using Springboot

I want to be able to fetch a param from the redirect url whenever it is automated. I am having difficulties doing this as I am getting a bad request after I created another endpoint to effect this.
I have an endpoint that works fine. The endpoint is a get method. Loading the endpoint takes a user to a page where they need to provide some necessary details. Once these details have been verified, the user is redirected to my redirecr_uri. The redirect_uri now contains important information like session_id, code, etc. The most important thing I need is the code. I need to pass the code into yet another endpoint which will return an access token.
I have manually done this process and it works but I want it to be done automatically because I can't keep doing that when I push the code to staging or production.
Here is the endpoint that redirects as well as the method.
#GetMapping("/get-token")
public RedirectView getBvn() throws UnirestException {
return nibss.getAccessToken();
}
This is the method that the controller calls
public RedirectView getAccessToken() throws UnirestException {
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/authorize.htm?scope=profile&acr_values=otp&response" +
"_type=code&redirect_uri=https://www.accionmfb.com/&client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e";
RedirectView redirectView = new RedirectView();
redirectView.setUrl(url);
return redirectView;
}
When the user provides the right information they are redirected to something like this
https://www.accionmfb.com/?code=9ad91f13-4698-4030-8a8f-a857e6a9907e&acr_values=otp&scope=profile&session_state=fa525cabc5b62854c73315d0322fd830c12a5941b89fd8e6e518da369e386572.b78a3d21-e98e-4e9a-8d60-afca779d9fad&sid=fd60ab92-ef37-4a5b-99b9-f8f52321985d
It is important to state that this 3rd party API I am trying to consume uses oath2.0 client authentication.
I created this endpoint to get the code from the redirected_uri
#GetMapping("/redirect-url")
public void handleRedirect(#RequestParam("code") String code) throws UnirestException {
if(Objects.nonNull(code) || !code.isEmpty()){
nibss.getToken(code);
log.info("Code is not being passed {}", code);
} else {
log.info("Code is not being passed {}", code);
}
}
public String getToken(String code) throws UnirestException {
log.info("This is the code here oooooooooo {}", code);
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/restv1/token";
String parameters = "client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e&code="+code+"&redirect_uri=https://www.accionmfb.com/&grant_type=authorization_code";
HttpResponse<String> apiResponse = Unirest.post(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic MDkxNWNkMDAtNjdmMi00NzY4LTk5YWMtMWIyZmY5ZjFkYTJlOlRVRnEwcGFBQXRzbzBZOEcxMkl2WFZHUmx6WG5zaERiaGt1dzI1YUM=")
.body(parameters)
.asString();
//JSONObject apiJson = apiResponse.getBody().getObject();
//return apiJson.getString("access_token");
JSONObject json = new JSONObject(apiResponse.getBody());
String accessToken = json.getString("access_token");
log.info(accessToken);
return accessToken;
}
But this is not working, I get 400 whenever I hit the second endpoint. What am I doing wrong?
The redirect_uri that you are passing to the OAuth server is https://www.accionmfb.com which does not include the path /redirect-url so the redirect never hits your method.
Either register and pass a callback uri like redirect_uri=https://www.accionmfb.com/redirect-url
Or change #GetMapping("/redirect-url") to #GetMapping("/")

Using #RequestBody and forwarding to another endpoint throws exception Stream closed

My Java spring REST API controller looks like this:
public void signup(#RequestBody RequestBody requestBody) throws IOException, ServletException {
I get this exception:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Stream closed; nested exception is java.io.IOException: Stream closed
This happens because I want to cast the request body to RequestBody class (which opens the request input stream and finishes it), and also forward/redirect it to another endpoint.
The actual controller is:
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public void signup(#RequestBody CustomUserDetails user, HttpServletRequest request, HttpServletResponse response) {
String userName = user.getUsername();
logger.debug("User signup attempt with username: " + userName);
try{
if(customUserDetailsService.exists(userName))
{
logger.debug("Duplicate username " + userName);
userName + " already exists");
String newUrl = "login";
RequestDispatcher view = request.getRequestDispatcher(newUrl);
view.forward(request, response);
} else {
customUserDetailsService.save(user);
authenticateUserAndSetSession(user, response);
}
} catch(Exception ex) {
}
}
How should I handle this ?
You can forward to login page in a ExceptionHandler,like this:
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public void signup(#RequestBody CustomUserDetails user, HttpServletResponse response) {
String userName = user.getUsername();
logger.debug("User signup attempt with username: " + userName);
//try{
if (customUserDetailsService.exists(userName)) {
logger.debug("Duplicate username " + userName);
throw new SignupException(userName + " already exists");
} else {
customUserDetailsService.save(user);
authenticateUserAndSetSession(user, response);
}
/*} catch(Exception ex) {
}*/
}
define a ExceptionHandler in the same Controller:
#ExceptionHandler(SignupException.class)
public String duplicateName() {
return "login";
}
and the SignupException could be like this:
public class SignupException extends RuntimeException {
public SignupException(String message) {
super(message);
}
public SignupException() {
}
}
The request body object is a stream which can be read only once. So forwarding it is not very trivial. One way around this is to create a filter which reads the input steam and replace the input stream to something which can be read multiple times. Example can be found at another answer:
How can I read request body multiple times in Spring 'HandlerMethodArgumentResolver'?
As for your method, there is also another problem:
public void signup(#RequestBody RequestBody requestBody)
As far as I know, RequestBody is an annotation and you can't map it like that. But to get the raw data, you can map it as String.
public void signup(#RequestBody String requestBody)
And then you can just manually make an REST call to the api you want to forward it to using the String request body. Just make sure you set the content-type as the original one, which I assume in this case would be JSON.
Root of your problem is using #RequestBody RequestBody requestBody together with HttpServletRequest request.
Opening input stream twice on the same request is not allowed. In your case a system should open a input stream to extract request body and then propagate in forward to reuse.
To handle it you should avoid multiple usage of the same request stream. Possible solutions are:
Wrap request
Copy request body
Use spring native forward
I think you are trying to forward to a url with the RequestBody, please have a check
Spring 3.2 forward request with new object for the answers.
Create the object and add it to the request as an attribute in the first controller,
request.setAttribute("user",user),
return "forward:/login";
Try putting in request mapping consumes= {" application/json"}, produces={"application/json"}
RequestBody is an annotation to process your request object as expected class deserialization. They help you avoid boilerplate code by extracting the logic of messageconversion and making it an aspect.
You can not get RequestBody directly as an object in any controller. It was not designed to use this way.
Though it is not possible to get RequestBody in a RestController so you can't judge whether it is good or bad practice.
If you have to use new controller/endpoint to process.Then i think the better approach is to get the CustomUserDetails as #RequestBody in the controller then process it. Then call nested method or service to further process instead of thinking forwarding to another controller. Then return response from the controller.

Request Header is getting as null in spring mvc

I am having two mapping methods in my controller. one is redirecting to other.
Before redirecting I'm setting a header in response. But I getting the request header as null.
These are my methods in controller. both are in same controller.
#RequestMapping(value="/testStart", method=RequestMethod.POST)
public String testStart(HttpServletRequest request, HttpServletResponse response){
String token = "126712810-1289291":
response.addHeader("authToken", token);
return "redirect:/test";
}
#RequestMapping(value="/test", method={ RequestMethod.POST, RequestMethod.GET })
public String getTestPage(Model model, HttpServletRequest request, HttpServletResponse response){
String token = request.getHeader("authToken");
System.out.println(token); //prints null
model.addAttribute("Testtoken", token);
System.out.println("Test page about to load ..");
return "test";
}
I'm using postman client to test this api. It is hitting the url and redirecting to other url. but the header is null.
I don't know what's wrong. can any one help me to figure this out? Thanks
With Redirect method web app tells browser to load the page which you want to redirect. So this makes new http request from browser, the original requests are not reachable at this moment.
So your problem can be solved with Forward method. Web app forwards all request data to another handler method internally
return "forward:/test";
Additionally , please change your
String token = request.getHeader("authToken");
with
String token = response.getHeader("authToken");
because you are adding your authToken to the response object.(from comments HttpServletResponse -> getHeader(String name) works since Servlet 3.0)
EDIT :
this code will be your complete solution "/test" method supports forwarded reuqest and also supports request from browser.
(You want to get token info from request becuase you want to call /test method without forwarding, so it works in this way, but when forwarding you cant add header so you tried to add in response and get it from request but that doesnt work in that way so you need to resolve token according to dispatcher's type so check the code )
#RequestMapping(value="/testStart", method=RequestMethod.POST)
public String testStart(HttpServletRequest request, HttpServletResponse response){
String token = "126712810-1289291";
request.setAttribute("authToken", token);
return "forward:/test";
}
#RequestMapping(value="/test", method={ RequestMethod.POST, RequestMethod.GET })
public String getTestPage(Model model, HttpServletRequest request, HttpServletResponse response)
{
//-----------------
//resolving token
String token = null;
DispatcherType type = request.getDispatcherType();
if(type == DispatcherType.FORWARD)
{
token = (String) request.getAttribute("authToken");
}
else if(type == DispatcherType.REQUEST)
{
token = (String) request.getHeader("authToken");
}
//-----------------
System.out.println(token); //prints the value
model.addAttribute("Testtoken", token);
System.out.println("Test page about to load ..");
return "test";
}
Use RedirectAttributes to pass parameters with redirect URL:
#RequestMapping(value="/test1", method=GET)
public String test(RedirectAttributes redirectAttributes){
redirectAttributes.addAttribute("authToken", "val");
return "redirect:/test";
}
or if you can use forward:/test you can go with request.setAttribute and request.getAttribute
Still if you want to add in header then use RestTemplate and HTTPHeaders and get the response String

Response.seeOther returns blank screen

I need to redirect to a specific external url in my jersey web service
and I am using this method:
public static Response redirectTo(String path) {
URI uri;
try {
uri = new URI(path);
return Response.seeOther(uri).build();
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
}
}
But it navigates to a white screen page and not to stackoverflow.com.
Why is it happening and how to fix it?
This method is called inside this method
#GET
#Path("/login")
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response login(#Context UriInfo info, #Context HttpServletRequest request) {
...
String url = "http://stackoverflow.com";
redirectTo(url);
}
the url of login method is called when triggering an event from AppDirect (via browser)
I ended up in adding the #Context HttpServletResponse res parameter to my method and calling this method res.sendRedirect(urlApp);
This is working as expected.

How to redirect to a JSP file from Servlet

I'm a beginner and am trying to understand how to re-direct to a JSP file from a Servlet. My Servlet "generates" a result after receiving replies from a current JSP file and with that I result I want to pass it to a different JSP file. I understand that there is a line of code:
request.getRequestDispatcher("/upload.jsp").forward(request, response);
But do I create a separate method for that and call it in the doGET?
you can do
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/upload.jsp").forward(request, response);
}
even though you created a method seperately you need the request and response object to the method.
I am heavily recommending the official docs:
http://docs.oracle.com/javaee/6/tutorial/doc/bnafd.html
and the pictorial
If you're using version 3.0 with annotations the redirects are very simple.
Suppose you have a User class (Strings fullname and Username with setters and getters) and UserDAO class that deals with database manipulation .
Suppose this is your controller:
#RequestMapping(value = "/user_list")
public String users(HttpServletResponse response, HttpServletRequest request)
{
//some function to verify access
boolean authorized = client.getAccess();
request.setAttribute("authorized", authorized);
if (authorized)
{
List<User> users = UserDAO.geUsers();
request.setAttribute("users", users);
return "user_list";
}
else
{
return "access_denied";
}
}
Then you can redirect from any location using the following syntax
#RequestMapping(value = "/create_user", method = RequestMethod.POST)
public String add_user(HttpServletResponse response, HttpServletRequest request)
{
boolean authorized = client.getAccess();
if (authorized)
{
User user = new User();
user.setUserName(request.getParameter("username"));
user.setFullName(request.getParameter("fullname"));
if (UserDAO.saveUser(user))
{
return "redirect:/user_list";
}
else
{
return "error";
}
}
else
{
return "access_denied";
}
}
The redirect:/user_list will return updated user_list (eg if you were inserting to db your
changes will be reflected).
Btw: you can drop the .jsp and path in your controller if you add few lines to your xml:
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-xml-example/
Have a look at those tutorials:
http://www.javatpoint.com/spring-3-mvc-tutorial
http://www.javatpoint.com/servlet-tutorial

Categories