I'm trying to overwrite my old project from native JS to React with MobX. My application has several pages and I want to use react-router to handle them. For that I need to return index.html page for all routes from my Spring-boot controller:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class RoutesController {
#RequestMapping(value = { "", "/**" }, method = RequestMethod.GET)
public String index() {
return "index";
}
}
Also I have special folder for static components on embedded Apache Tomcat server. This folder contains build.js for my client-side application.
Controller presented above can serve all requests to server with index.html page, but I have also client-side application on /static/** route.
How can I specify all routes in my controller except /static/**?
I solved my problem. To get only one page for all requests your controller must implement ErrorController and two methods: error and getErrorPath.
Here is an example:
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class RoutesController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "index";
}
#Override
public String getErrorPath() {
return PATH;
}
}
Server get the request and will not find any routes, so it will return result of error method.
Maybe that's not the best solution, but after three days of searching the Internet I have not found anything better.
Related
I'm a beginner in spring boot and I'm trying to create REST endpoint that's adding two numbers (type: POST) so can anyone help me with that and tell me where is the problems in my code??
package demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import demo.service.ServiceAdding;
#RestController
public class DemoController {
#Autowired
private ServiceAdding ServiceAdding;
#RequestMapping(value="/getNumbers", method=RequestMethod.GET)
public ServiceAdding getNumbers() {
ServiceAdding.setNumber1(1);
ServiceAdding.setNumber2(3);
return ServiceAdding;
}
#RequestMapping(value="/postNumbers", method=RequestMethod.POST)
public int postNumbers (#RequestBody final ServiceAdding ServiceAdding ) {
int sum = ServiceAdding.getNumber1() + ServiceAdding.getNumber2();
return sum;
}
}
Try using #PostMapping instead of #RequestMapping
#PostMapping(value = "postNumbers")
public int postNumbers(...){
...
}
what errors are you getting?
Also try adding a value to the restController maybe?
#RestController(value="api/v1")
#RequestMapping(value = "/getNumbers, method = RequestMethod.GET, produces = "application/json")
You can also use #GetMapping, #PostMapping etc
Sounds like you're not performing a POST request. How do you load that URL?
You need to have either:
Webpage that has a FORM that does a POST
Use a tool like PostMan (or similar tool) to submit the post request.
The correct one should be like below :
#RestController
#RequestMapping("/api")
public class DemoController {
... other codes
#RequestMapping(value="/postNumbers", method=RequestMethod.POST)
public int postNumbers (#RequestBody SumNumbersRequest sumNumbersRequest)
{
int sum = sumNumbersRequest.getNumber1() + sumNumbersRequest.getNumber2();
return sum;
}
}
#Data // from Lombok for getter setter
class SumNumbersRequest {
private int number1;
private int number2;
}
And you should post json request to url localhost:8080/api/postNumber as below with Content-Type : application/json :
{
"number1":5,
"number2":15
}
i need your help. My brain will explode without your help! I am writting Quiz app on Spring MVC + JSP.
What i done for now:
1. I created HashMap + hardcode correct answer.
2. I created #RequestMapping(value = "/level_one", method = RequestMethod.POST)
public String levelOne() {
return "levelone";
}
How it shoul be:
pics
Then i just freezed and i don't know what to do and how to unite this code in #RequestMaping with HashMap and write code in jsp to make it visible and clickable???
In controller i wrote :
package ua.kiev.prog;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.HashMap;
import java.util.Map;
#Controller
#RequestMapping("/")
public class MyController {
final private String rightAnswerOne = "Dance";
#ModelAttribute("answerList")
public Map answerList() {
Map<String, String> answerList = new HashMap<String, String>();
answerList.put("one", "Sandbox");
answerList.put("two", "Pixel");
answerList.put("three", "Game");
answerList.put("four", "Picture");
return answerList;
}
#RequestMapping(value = "/level_one", method = RequestMethod.POST)
public String levelOne() {
return "levelone";
}
}
#modelAttribute is executed before the request mapping. That populate the Model model object (that you didn't pass as a parameter on the method annotated with #requestmapping.
You can access to that object using model.asMap().Get("yourkey").
#RequestMapping(value = "/level_one", method = RequestMethod.POST) public String levelOne(Model model) {
model.asMap().get("answerList");
...
return "levelone"; }
This is how modelattribute works but it is a long argument. You might search for some tutorial on spring MVC.
I recommend to Google mkyong spring mvc, he has a huge set of basic tutorials for spring mvc , easy to understand.
Check this link
http://www.mkyong.com/spring3/spring-3-mvc-hello-world-example/
Thank you.I will do it. But on this example, please answer on one more question. If i write code as you wrote before. How should i write jsp page, to unite answers from hash map in the controller and web view? So then if i click on the button on the web, it will be united with hash map on the server side page.
So then i will be able to write something like this:
public static int getCount(HashMap<String, String> answerList, String rightAnswerOne, String rightAnswerTwo, String message) {
int count = 0;
for (String tmp : answerList.values()) {
if (rightAnswerOne.equals(tmp) ) {
count++;
System.out.println("SUCCESS");
} else {
System.out.println("DENIED");
}
}
return count;
}
I just want to understand how to write such things.
We have an issue where embedded Tomcat is throwing IllegalArgumentException from the LegacyCookieProcessor. It throws a 500 HTTP response code.
We need to handle the exception and do something with it (specifically, send it as a 400 instead).
The typical #ExceptionHandler(IllegalArgumentException.class) doesn't seem to get triggered and Google only seems to give results for dealing with Spring Boot specific exceptions.
Example:
Here is an example to reproduce the behavior. You can execute the example by downloading the initial project including spring-web (https://start.spring.io/) in version 2.1.5.RELEASE. Then add the following two classes to your project.
DemoControllerAdvice.java
package com.example.demo;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
#RestControllerAdvice
public class DemoControllerAdvice {
#ExceptionHandler(IllegalArgumentException.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public Map<String, String> forbiddenHandler() {
Map<String, String> map = new HashMap<>();
map.put("error", "An error occurred.");
map.put("status", HttpStatus.FORBIDDEN.value() + " " + HttpStatus.FORBIDDEN.name());
return map;
}
}
DemoRestController.java
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DemoRestController {
#GetMapping(value = "/working")
public void working() {
throw new java.lang.IllegalArgumentException();
}
#GetMapping(value = "/not-working")
public String notWorking(#RequestParam String demo) {
return "You need to pass e.g. the character ^ as a request param to test this.";
}
}
Then, start the server and request the following URLs in the browser:
http://localhost:8080/working An IllegalArgumentException is thrown manually in the controller. It is then caught by the ControllerAdvice and will therefore produce a JSON string containing the information defined in the DemoControllerAdvice
http://localhost:8080/not-working?demo=test^123 An IllegalArgumentException is thrown by the Tomcat, because the request param cannot be parsed (because of the invalid character ^). The exception however is not caught by the ControllerAdvice. It shows the default HTML page provided by Tomcat. It also provides a different error code than defined in the DemoControllerAdvice.
In the logs the following message is shown:
o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header
Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:467) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
This is a feature of Tomcat itself as mentioned in this answer.
However, you can do something like this by allowing the special characters that you are expecting as part of your request and handle them yourself.
First, you need to allow the special characters that you would need to handle by setting up the relaxedQueryChars like this.
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
#Component
public class TomcatCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers((connector) -> {
connector.setAttribute("relaxedQueryChars", "^");
});
}
}
and later handle the special characters in each of your requests or create an interceptor and handle it in a common place.
To handle it in the request individually you can do something like this.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DemoRestController {
#GetMapping(value = "/working")
public void working() {
throw new java.lang.IllegalArgumentException();
}
#GetMapping(value = "/not-working")
public String notWorking(#RequestParam String demo) {
if (demo.contains("^")) {
throw new java.lang.IllegalArgumentException("^");
}
return "You need to pass e.g. the character ^ as a request param to test this.";
}
}
You might also want to refer this answer to decide if you really need this fix.
Try to catch the IllegalArgumentException in your filter, then call HttpServletResponse.sendError(int sc, String msg);. This may catch the IllegalArgumentExceptions that do not come from Tomcat though. But I suppose you already handle them properly.
I am trying to write the spring REST code to get the parameters send by the android users. For example android users fills out the form and click send button. Now I want to receive the values or parameters in REST API. I search google but could not figure out how to do it. Below is the code I tried but it didn't work
EmailController.java
package com.intern.training
import java.awt.PageAttributes.MediaType;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
#RestController
#RequestMapping("/email")
public class EmailController
{
#RequestMapping(method= RequestMethod.GET)
public void getAll(WebRequest webRequest)
{
Map<String,String[]>params=webRequest.getParameterMap();
System.out.println(params);
}
}
I strongly advice you to not send parameters with a GET request. Prefer a POST request. (See Cross-site request forgery)
Then, create a class which represents the parameters that you are to receive :
public class RequestParams {
private String name;
private String surname;
//Getters, Setters...
}
Then expect this object as a param of your method :
#RequestMapping(method= RequestMethod.POST)
/**
Pay attention to the above #RequestBody annotation
or you will get null instead of the parameters
**/
public void getAll(#RequestBody RequestParams request)
{
request.getName();
request.getSurname();
//...
System.out.println(request.getName() + " " + request.getSurname());
}
We are building a webapp that communicates with a remote API. I would like to design the client for this remote API like this:
def RemoteApi
constructor (username, password)
getCurrentUser() //implementation will use username and password
getEmployees() //implementation will use username and password
...
The point being, I want to pass in the credentials to this client during construction, and have all the other methods use these credentials. My second requirement is I want this RemoteApi instance to be in the session.
I have found out how to pass dynamic constructor arguments here.
I have found out how to create a session attribute here.
But I can't figure out a way to combine these two techniques. From what I gather, you have to instantiate a session attribute in its own getter-like method. This getter-like method won't have access to the form's fields so I won't be able to pass in credentials at this point.
Here's where I'm stuck:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import java.util.Map;
#Controller
#SessionAttributes("remoteClient")
public class LoginController {
#Value("${company.name}")
private String companyName;
#Autowired
private RemoteClient remoteClient;
#ModelAttribute("remoteClient")
public RemoteClient addRemoteClientToSession() {
return remoteClient; //how do initialize this with credentials in here?
}
#RequestMapping("/")
public String showLogin(Model model) {
model.addAttribute("credentials", new Credentials());
return "login";
}
#RequestMapping("/login")
public String login(#ModelAttribute("credentials") Credentials credentials, Map<String, Object> model) {
if (remoteClient.auth(companyName, credentials.getUsername(), credentials.getPassword())) {
model.put("fullname", remoteClient.findCurrentUser().getName());
return "projectView";
} else {
return "login";
}
}
}
Update: Maybe the solution has to do with this technique. I didn't know about ModelAndView before this.
The article I linked to in the question did have the necessary technique in it. What I would do is create an #Autowired RemoteApiFactory that has a newInstance(Credentials) method and pass that into addObject. The implementation of newInstance would probably end up using the new keyword. If anyone knows how to avoid this detail, I'd love to hear it.
Here's its example tweaked for my needs:
#Controller
#SessionAttributes("remoteApi")
public class SingleFieldController {
#Autowired
private RemoteApiFactory remoteApiFactory;
#RequestMapping(value="/single-field")
public ModelAndView singleFieldPage() {
return new ModelAndView("single-field-page");
}
#RequestMapping(value="/remember")
public ModelAndView rememberThought(#MethodAttribute("credentials") Credentials credentials) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("remoteApi", remoteApiFactory.newInstance(credentials));
modelAndView.setViewName("single-field-page");
return modelAndView;
}
}