HTTP 405 with embedded VAADIN UI in a SPRING-BOOT JSP based webapp - spring

I have integrated Vaadin 7 embedding the UI, as explained in Vaadin Book, in some JSP pages of a Spring Boot based application.
Despite the UI named "v-my-vaadin-ui" is correctly displayed when I call the path in which it is embedded (through a spring MVC controller) I got an HTTP 405 error when interacting with the UI.
Error is on the URL:
http://localhost:8080/v-my-vaadin-ui/UIDL/?v-uiId=4
It seems that because in Spring Boot all controllers have only GET method allowed by default (POST has to be explicitly allowed as explained here)
Already tried disabling the CSRF and configuring Vaadin ralated paths in my Spring Security Configuration:
http.authorizeRequests()
.antMatchers("/vaadinServlet/**",
"/UIDL/**",
"/v-my-vaadin-ui/UIDL/**",
"/v-my-vaadin-ui/PUSH/**",
"/HEARTBEAT/**", "/VAADIN/**").permitAll();
http.csrf().disable();
because VAADIN integration is requiring no CSRF managed by Spring but this doesn't solve the problem.
The VAADIN application is very basic:
#SpringUI(path = "v-my-vaadin-ui")
public class MyVaadinUI extends UI {
private static final long serialVersionUID = -8129815147461786549L;
#Override
protected void init(VaadinRequest vaadinRequest) {
final TextField name = new TextField("Name");
final Button greetButton = new Button("Greet");
greetButton.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Hi " + name.getValue(),
Notification.Type.HUMANIZED_MESSAGE);
}
});
VerticalLayout vl = new VerticalLayout(name, greetButton);
vl.setSpacing(true);
setContent(vl);
}
}
But clicking on the Vaadin button the response code is 405 and the exception in detail is:
.w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/], class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#4a8756c3]]]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
Unfortunately I didn't find any way to configure the POST method for the UI as for a simple controller i.e. via
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
because this annotation cannot be used for Vaadin UI.
Moreover if I call directly the UI (i.e. not passing through a Spring controller) with the URL:
http://localhost:8080/v-my-vaadin-ui
UI displays and works perfectly.
Any idea what is causing this problem? How can I allow the POST method?

Have you tried with this add-on? https://vaadin.com/directory#!addon/jsp-integration

SOLVED!!
The problem was caused by an error I made in the JS script I was using to embed the VAADIN app (I've followed the documentation here: https://vaadin.com/docs/-/part/framework/advanced/advanced-embedding.html) in particular the code fragment for "serviceUrl" parameter:
"serviceUrl": "helloworld/",
that I've understood to be set with the name of the application, in my case "v-my-vaadin-ui".
But the correct "serviceUrl" in my case, is still the default one "vaadinServlet/" so changing from:
"serviceUrl": "v-my-vaadin-ui/",
to
"serviceUrl": "vaadinServlet/",
everything works.
Here below the complete code of the JSP tag to embed the vaadin app that works for me:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%#tag description="ajax widget tag" pageEncoding="UTF-8"%>
<%#attribute name="v_app_url"%>
<%-- Javascript as described here: https://vaadin.com/docs/-/part/framework/advanced/advanced-embedding.html --%>
<div>
<div id="${v_app_url}" class="v-app myvaadinui" >
<div class="v-app-loading"></div>
<noscript>
You have to enable javascript in your browser to use an application built with Vaadin.
</noscript>
</div>
<script type="text/javascript">//<![CDATA[
if (!window.vaadin)
alert("Failed to load the bootstrap JavaScript: vaadinBootstrap.js");
/* The UI Configuration */
vaadin.initApplication("${v_app_url}", {
"browserDetailsUrl": "${v_app_url}/",
"serviceUrl": "vaadinServlet/",
"widgetset": "com.vaadin.DefaultWidgetSet",
"theme": "valo",
"versionInfo": {"vaadinVersion": "7.4.4"},
"vaadinDir": "/VAADIN/",
"heartbeatInterval": 300,
"debug": false,
"standalone": false,
"authErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Authentication problem"
},
"comErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Communication problem"
},
"sessExpMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Session Expired"
}
});//]] >
</script>
</div>

Related

Error Response body changed after Boot 3 upgrade

I have the following Controller endpoint in my project:
#GetMapping(value = "/{id}")
public FooDto findOne(#PathVariable Long id) {
Foo model = fooService.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return toDto(model);
}
My application retrieved the following response when it couldn't find a Foo with the provided id:
{
"timestamp": "2023-01-06T08:43:12.161+00:00",
"status": 404,
"error": "Not Found",
"path": "/foo/99"
}
However, after upgrading to Boot 3, the response body changed to:
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"instance": "/foo/99"
}
I couldn't find any related information in the Spring Boot 3.0 Migration Guide nor in the Upgrading to Spring Framework 6.x one.
Spring Web 6 introduced support for the "Problem Details for HTTP APIs" specification, RFC 7807.
With this, the ResponseStatusException now implements the ErrorResponse interface and extends the ErrorResponseException class.
Having a quick look at the javadocs, we can see that all these are backed by the RFC 7807 formatted ProblemDetail body, which, as you can imagine, has the fields of the new response you're getting, and also uses the application/problem+json media type in the response.
Here is a reference to how Spring now treats Error Responses, which naturally goes in the direction of using Problem Details spec across the board.
Now, normally, if you were simply relying on Boot's Error Handling mechanism without any further change, you would still see the same response as before. My guess is that you are using a #ControllerAdvice extending ResponseEntityExceptionHandler. With that, you are enabling RFC 7807 (as per the Spring docs here)
So, that is why your ResponseStatusException has changed its body content.
Configuring the Problem Detail response body to include previous fields
If you need to stick to the pre-existing fields (at least until you fully migrate to the Problem Detail based approach) or if you simply want to add custom fields to the error response, you can override the createResponseEntity method in the #ControlAdvice class extending ResponseEntityExceptionHandler as follows:
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> createResponseEntity(#Nullable Object body, HttpHeaders headers, HttpStatusCode statusCode, WebRequest request) {
if (body instanceof ProblemDetail) {
ProblemDetail problemDetail = ((ProblemDetail) body);
problemDetail.setProperty("error", problemDetail.getTitle());
problemDetail.setProperty("timestamp", new Date());
if (request instanceof ServletWebRequest) {
problemDetail.setProperty("path", ((ServletWebRequest) request).getRequest()
.getRequestURI());
}
}
return new ResponseEntity<>(body, headers, statusCode);
}
}
Note: I'm using new Date() instead of Java Time simply because that's what Boot's DefaultErrorAttributes class uses. Also, I'm not using Boot's ErrorAttributes for simplicity.
Note that defining the path field is a little bit tricky because problemDetail.getInstance() returns null at this stage; the framework sets it up later in the HttpEntityMethodProcessor.
Of course, this solution is suitable for a servlet stack, but it should help figure out how to proceed in a reactive stack as well.
With this, the response will look as follows:
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"instance": "/foo/99",
"error": "Not Found",
"path": "/foo/99",
"timestamp": "2023-01-06T10:00:20.509+00:00"
}
Of course, it has duplicated fields. You can completely replace the response body in the method if you prefer.
Configuring Boot to also use the Problem Detail spec
Now, to be consistent across the board in your application, note that Boot now provides the spring.mvc.problemdetails.enabled property to use the Problem Details in its Error Handling mechanism (which is disabled by default to avoid breaking changes, as its associated issue):
spring.mvc.problemdetails.enabled=true

Spring boot Authentication Failure Handler - Custom with no additional url or query parameters

My current implementation of Spring Boot Login is as follows:
login.html:
<div class="error-message m-l-4" th:if="${errorMessage}" th:text="${errorMessage}"></div>
WebSecurityConfigurerAdapter - configure method:
formLogin().failureUrl("/login-error")
RequestMapper:
#RequestMapping("/")
public class Root {
#GetMapping("login")
public ModelAndView getLoginView(ModelMap mav) {
return new ModelAndView("login", mav);
}
#GetMapping("login-error")
public RedirectView setErrorMessage(RedirectAttributes attributes) {
attributes.addFlashAttribute("errorMessage", "Username or Password is invalid");
return new RedirectView("login");
}
}
The above is working perfectly when the user inputs invalid credentials the error is displayed on HTML. However, is there a way to accomplish the same result but get rid of the extra directory /login-error? I don't want it to be accessible despite redirecting to the login page with the error. I have tried to translate a custom AuthenticationFailureHandler (.failureHandler(new CustomAuthenticationFailureHandler())) to have the same effect but I can only return JSON not HTML. Basically, I want the exact same behavior as the Instagram Login Page.
See Pages in Order and upon Refresh back to the exact page as Page 1:

Embedding Vaadin spring-boot application into HTML

I've already posted this question in the Vaadin Forum, unfortunately I did not get any response - maybe answer of the question lies somewhere between spring-boot and Vaadin.
currently I'm having a hard time embedding a Vaadin application into an HTML page.
What do I use:
Vaadin 7.6.6
vaadin-spring
spring-boot 1.3.5.RELEASE
To enable CORS in combination with spring-boot, I adapted Sami's Blog entry and created the following custom CORS servlet:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import com.vaadin.spring.server.SpringVaadinServlet;
/**
* This custom {#link SpringVaadinServlet} enables CORS in combination with
* Spring.
*
* #author Christoph Guse
*
*/
public class CORSServlet extends SpringVaadinServlet {
/**
*
*/
private static final long serialVersionUID = -2482991123719720492L;
/**
* Override to handle the CORS requests.
*/
#Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Origin is needed for all CORS requests
String origin = request.getHeader("Origin");
if (origin != null && isAllowedRequestOrigin(origin)) {
// Handle a preflight "option" requests
if ("options".equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Allow", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
// allow the requested method
String method = request.getHeader("Access-Control-Request-Method");
response.addHeader("Access-Control-Allow-Methods", method);
// allow the requested headers
String headers = request.getHeader("Access-Control-Request-Headers");
response.addHeader("Access-Control-Allow-Headers", headers);
response.addHeader("Access-Control-Allow-Credentials", "true");
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().flush();
return;
} // Handle UIDL post requests
else if ("post".equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Origin", origin);
response.addHeader("Access-Control-Allow-Credentials", "true");
super.service(request, response);
return;
}
}
// All the other requests nothing to do with CORS
super.service(request, response);
}
/**
* Check that the page Origin header is allowed.
*/
private boolean isAllowedRequestOrigin(String origin) {
// TODO: Remember to limit the origins.
return origin.matches(".*");
}
}
Additionally I found some documentation about spring-boot and CORS, so I added this Spring configuration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import CORSServlet;
/**
* #author Christoph Guse
*
*/
#Configuration
public class AuthAppVaadinApplicationConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins(".*");
}
};
}
#Bean(name="vaadinServlet")
public CORSServlet corsServlet(){
return new CORSServlet();
}
}
My HTML looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible"
content="IE=9;chrome=1" />
<title>Embedding a Vaadin Application in HTML Page</title>
<!-- Set up the favicon from the Vaadin theme -->
<link rel="shortcut icon" type="image/vnd.microsoft.icon"
href="/VAADIN/themes/reindeer/favicon.ico" />
<link rel="icon" type="image/vnd.microsoft.icon"
href="/VAADIN/themes/reindeer/favicon.ico" />
</head>
<body>
<!-- Loads the Vaadin widget set, etc. -->
<script type="text/javascript"
src="http://vaadin.poc:8090/VAADIN/vaadinBootstrap.js?v=7.6.6"></script>
<h1>Embedding a Vaadin UI</h1>
<p>This is a static web page that contains an embedded Vaadin
application. It's here:</p>
<!-- So here comes the div element in which the Vaadin -->
<!-- application is embedded. -->
<div style="width: 100%; height: 75vh; border: 2px solid green;"
id="helloworld" class="v-app">
<!-- Optional placeholder for the loading indicator -->
<div class=" v-app-loading"></div>
<!-- Alternative fallback text -->
<noscript>You have to enable javascript in your browser to
use an application built with Vaadin.</noscript>
</div>
<script type="text/javascript">//<![CDATA[
if (!window.vaadin)
alert("Failed to load the bootstrap JavaScript: "+
"VAADIN/vaadinBootstrap.js");
/* The UI Configuration */
vaadin.initApplication("helloworld", {
"browserDetailsUrl": "http://vaadin.poc:8090/",
"serviceUrl": "http://vaadin.poc:8090/",
"theme": "valo",
"versionInfo": {"vaadinVersion": "7.6.6"},
"widgetset": "com.vaadin.DefaultWidgetSet",
"vaadinDir": "http://vaadin.poc:8090/VAADIN/",
"heartbeatInterval": 300,
"debug": true,
"standalone": false,
"authErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Authentication problem"
},
"comErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Communication problem"
},
"sessExpMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Session Expired"
}
});//]] >
</script>
<p>Please view the page source to see how embedding works.</p>
</body>
</html>
My problem is the application is initally loaded, but several icons are missing and if I trigger an action in the appliction, i.e. open a dropbox, then the application is not able to do the connect to the spring-boot application.
Error messages look like this:
XMLHttpRequest cannot load http://vaadin.poc:8090/UIDL/?v-uiId=0. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
Is there anybody out there who managed to embed a Vaadin spring-boot application into another HTML application?
Any hint is highly appreciated!
Christoph
fortunately someone in the Vaadin forum gave me the missing link. I forgot to add some JavaScript in the standalone HTML:
<script>
XMLHttpRequest.prototype._originalSend = XMLHttpRequest.prototype.send;
var sendWithCredentials = function(data) {
this.withCredentials = true;
this._originalSend(data);
};
XMLHttpRequest.prototype.send = sendWithCredentials;
</script>
That helped, but the fonts were not properly loaded by CORS problems, so I removed the custom Vaadin CORSServlet and added the filter based CORS support provided by spring-boot (as explained in this blog article).
My example now runs properly, the demo application is fully functional, fonts are loaded and used correctly.
Please have a look at https://github.com/flexguse/vaadin-html-embedding to get the fully working example.
Cheers,
Christoph

Return all available mappings on RequestMethod GET

I'm learning about spring boot and having a REST Server with JPA.
For my RestControllers, I wanted to have the behavior on the base page that when someone goes to the base page they would be able to see all the available RequestMappings under the base page.
#RestController
#RequestMapping("/food")
public class FoodRestController {
#RequestMapping(value = "/all", method = RequestMethod.GET)
#ResponseBody
public Iterable<Food> printAllFoods() {
return foodRepository.findAll();
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ResponseEntity<?> addFood(#RequestBody Food f) {
foodRepository.save(f);
HttpHeaders httpHeaders = new HttpHeaders();
return new ResponseEntity<Food>(f, httpHeaders, HttpStatus.CREATED);
}
So for the above going to "localhost:8080/food" would give a page showing something like not a valid endpoint, possible endpoints are localhost:8080/food/all or localhost:8080/food/add.
I could just have a RequestMapping with GET and return it as a body but it would be a manually typed response. Wanted to see if Spring offers anything like this
You can also use Swagger . It is actually a documentation framework. It also builds around a beautiful UI to try out the APIs that are available along with the documentation.
The SpringBoot Actuator already has a functionality that does something like this. Add a dependency on the SpringBoot Actuator, start up your application and point your browser to:
http://[yourHostAndPort]/mappings
This will produce something like the following (assuming JSON), which contains ALL mappings that are a part of your project (Spring endpoints too!).
{
...
"{[/refresh],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()"
},
"{[/restart],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.context.restart.RestartMvcEndpoint.invoke()"
},
"{[/configprops],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
},
"{[/env],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}": {
"bean": "endpointHandlerMapping",
"method": "public java.lang.Object org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint.value(java.util.Map<java.lang.String, java.lang.String>)"
},
...
}
This snippet shows a small handful of the mappings that are available from some other Actuator endpoints.
I understand that your requirements are a bit different, so if this setup isn't exactly what you need, you should be able to create your own endpoint(s) that do something similar, just by browsing the SpringBoot Actuator source code. The specific file that does the majority of the work for the mappings endpoint is org.springframework.boot.actuate.endpoint.RequestMappingEndpoint.java.

Exception handling in JSF ajax requests

How do I handle the exception and access the stack trace when an exception is thrown while processing a JSF ajax request? Right now, I only get the exception class name and message in a JavaScript alert when JSF project stage is set to Development. Even worse, there's no visual feedback whatsoever when JSF project stage is set to Production, and the server log doesn't show any information about the exception.
If that's relevant, I'm using GlassFish in Netbeans.
This problem is known and fleshed out in among others the OmniFaces FullAjaxExceptionHandler showcase.
By default, when an exception occurs during a JSF ajax request, the enduser would not get any form of feedback if the action was successfully performed or not. In Mojarra, only when the project stage is set to Development, the enduser would see a bare JavaScript alert with only the exception type and message.
The technical reason is that asynchronous requests (read: Ajax requests) by default don't return a synchronous response (read: a full page). Instead, they return small instructions and parts how to update the HTML DOM tree of the already-opened page. When an exception occurs, then these instructions are basically fully absent. Instead, some error information is sent back. You can usually handle them in the onerror attribute of the Ajax component and e.g. display an alert or perhaps perform a window.location change. At least, this is what JSF expected from you.
In order to catch and log the exception and optionally change the whole response, you basically need to create a custom ExceptionHandler. Standard JSF unfortunately doesn't provide a default one out the box (at least, not a sensible one). In your custom exception handler you will be able to get hands on the Exception instance causing all the trouble.
Here's a kickoff example:
public class YourExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public YourExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException(); // There it is!
// Now do your thing with it. This example implementation merely prints the stack trace.
exception.printStackTrace();
// You could redirect to an error page (bad practice).
// Or you could render a full error page (as OmniFaces does).
// Or you could show a FATAL faces message.
// Or you could trigger an oncomplete script.
// etc..
}
getWrapped().handle();
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
}
In order to get it to run, create a custom ExceptionHandlerFactory as follows:
public class YourExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public YourExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
return new YourExceptionHandler(parent.getExceptionHandler());
}
}
Which needs to be registered in faces-config.xml as follows:
<factory>
<exception-handler-factory>com.example.YourExceptionHandlerFactory</exception-handler-factory>
</factory>
Alternatively, you can go ahead using the OmniFaces one. It will fully transparently make sure that exceptions during asynchronous requests behave the same as exceptions during synchronous requests, using <error-page> configuration in web.xml.
See also:
Why FullAjaxExceptionHandler does not simply perform an ExternalContext#redirect()?
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same

Resources