I am using Jhipster Spring boot + angular 6. But i'm having trouble because of the hash(#) in URL. It is affecting SEO.
I tried setting useHash: false in app-routing-module.ts.
But then the API is not working when I run the project via npm start.
I think somewhere in Java files I have to change a configuration to remove # from the URL.
Here is my WebConfigurer code,
#Configuration
public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer<WebServerFactory> {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
private MetricRegistry metricRegistry;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initMetrics(servletContext, disps);
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
#Override
public void customize(WebServerFactory server) {
setMimeMappings(server);
/*
* Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
* HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
* See the JHipsterProperties class and your application-*.yml configuration files
* for more information.
*/
if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
server instanceof UndertowServletWebServerFactory) {
((UndertowServletWebServerFactory) server)
.addBuilderCustomizers(builder ->
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
}
}
private void setMimeMappings(WebServerFactory server) {
if (server instanceof ConfigurableServletWebServerFactory) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
// IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server;
servletWebServer.setMimeMappings(mappings);
}
}
/**
* Initializes Metrics.
*/
private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Initializing Metrics registries");
servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
metricRegistry);
servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
metricRegistry);
log.debug("Registering Metrics Filter");
FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
new InstrumentedFilter());
metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
metricsFilter.setAsyncSupported(true);
log.debug("Registering Metrics Servlet");
ServletRegistration.Dynamic metricsAdminServlet =
servletContext.addServlet("metricsServlet", new MetricsServlet());
metricsAdminServlet.addMapping("/management/metrics/*");
metricsAdminServlet.setAsyncSupported(true);
metricsAdminServlet.setLoadOnStartup(2);
}
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
}
#Autowired(required = false)
public void setMetricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
}
Here is my AngularRouteFilter servlet code,
public class AngularRouteFilter extends OncePerRequestFilter {
// add the values you want to redirect for
private static final Pattern PATTERN = Pattern.compile("^/((api|swagger-ui|management|swagger-resources)/|favicon\\.ico|v2/api-docs).*");
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (isServerRoute(request)) {
filterChain.doFilter(request, response);
} else {
RequestDispatcher rd = request.getRequestDispatcher("/");
rd.forward(request, response);
}
}
protected static boolean isServerRoute(HttpServletRequest request) {
if (request.getMethod().equals("GET")) {
String uri = request.getRequestURI();
if (uri.startsWith("/app")){
return true;
}
return PATTERN.matcher(uri).matches();
}
return true;
}
}
here is my Swagger index.html(swagger-ui/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
<link href='./dist/css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='./dist/css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='./dist/css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='./dist/lib/object-assign-pollyfill.js' type='text/javascript'></script>
<script src='./dist/lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='./dist/lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='./dist/lib/handlebars-4.0.5.js' type='text/javascript'></script>
<script src='./dist/lib/lodash.min.js' type='text/javascript'></script>
<script src='./dist/lib/backbone-min.js' type='text/javascript'></script>
<script src='./dist/swagger-ui.min.js' type='text/javascript'></script>
<script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
<script src='./dist/lib/jsoneditor.min.js' type='text/javascript'></script>
<script src='./dist/lib/marked.js' type='text/javascript'></script>
<script src='./dist/lib/swagger-oauth.js' type='text/javascript'></script>
<!-- Some basic translations -->
<!-- <script src='lang/translator.js' type='text/javascript'></script> -->
<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
<!-- <script src='lang/en.js' type='text/javascript'></script> -->
<script type="text/javascript">
$(function() {
var springfox = {
"baseUrl": function() {
var urlMatches = /(.*)\/swagger-ui\/index.html.*/.exec(window.location.href);
return urlMatches[1];
},
"securityConfig": function(cb) {
$.getJSON(this.baseUrl() + "/swagger-resources/configuration/security", function(data) {
cb(data);
});
},
"uiConfig": function(cb) {
alert(cb);
$.getJSON(this.baseUrl() + "/swagger-resources/configuration/ui", function(data) {
cb(data);
});
}
};
window.springfox = springfox;
window.oAuthRedirectUrl = springfox.baseUrl() + './dist/o2c.html'
window.springfox.uiConfig(function(data) {
window.swaggerUi = new SwaggerUi({
dom_id: "swagger-ui-container",
validatorUrl: data.validatorUrl,
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi) {
initializeSpringfox();
if (window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
apisSorter: "alpha",
showRequestHeaders: false
});
initializeBaseUrl();
$('#select_baseUrl').change(function() {
window.swaggerUi.headerView.trigger('update-swagger-ui', {
url: $('#select_baseUrl').val()
});
addApiKeyAuthorization();
});
function addApiKeyAuthorization() {
var authToken = JSON.parse(localStorage.getItem("jhi-authenticationtoken") || sessionStorage.getItem("jhi-authenticationtoken"));
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + authToken, "header");
window.swaggerUi.api.clientAuthorizations.add("bearer", apiKeyAuth);
}
function getCSRF() {
var name = "XSRF-TOKEN=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) !== -1) return c.substring(name.length,c.length);
}
return "";
}
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
function oAuthIsDefined(security) {
return security.clientId
&& security.clientSecret
&& security.appName
&& security.realm;
}
function initializeSpringfox() {
var security = {};
window.springfox.securityConfig(function(data) {
security = data;
if (typeof initOAuth === "function" && oAuthIsDefined(security)) {
initOAuth(security);
}
});
}
});
function maybePrefix(location, withRelativePath) {
var pat = /^https?:\/\//i;
if (pat.test(location)) {
return location;
}
return withRelativePath + location;
}
function initializeBaseUrl() {
var relativeLocation = springfox.baseUrl();
$('#input_baseUrl').hide();
$.getJSON(relativeLocation + "/swagger-resources", function(data) {
var $urlDropdown = $('#select_baseUrl');
$urlDropdown.empty();
$.each(data, function(i, resource) {
var option = $('<option></option>')
.attr("value", maybePrefix(resource.location, relativeLocation))
.text(resource.name + " (" + resource.location + ")");
$urlDropdown.append(option);
});
$urlDropdown.change();
});
}
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'>
<select id="select_baseUrl" name="select_baseUrl"></select>
</div>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/>
</div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate> </div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
here is docs.component.html
<iframe src="swagger-ui/index.html" width="100%" height="900" seamless
target="_top" title="Swagger UI" class="border-0"></iframe>
here my server code is running perfectly # localhost:6060. Butm localhost:6060/api/docs opening a blank page.
here is the screen shot,
Please suggest me where i am doing wrong.
Solution using a servlet filter
First step is to configure client, set useHash: false in app-routing-module.ts
In index.html, change <base href="./" /> to <base href="/" />
But it's not enough because it does not support deep linking which means linking to a client route from an external link like from a mail message or from another web site, or veen when refreshing page in browser.
When deep linking, the server receives the request first and if the URL has to be handled by client side, it will not be found so our server app must detect whether the URL has to be served as-is by server (e.g. all API calls) or forwarded to index.html so that the javascript app can interpret it.
One solution is to use pattern matching in a servlet filter (Html5RouteFilter) that we register in WebConfigurer.java
WebConfigurer.java
#Bean
public Html5RouteFilter html5RouteFilter() {
return new Html5RouteFilter();
}
Html5RouteFilter.java
package com.mycompany.myapp.web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* Filter that distinguishes between client routes and server routes when you don't use '#' in client routes.
*/
public class Html5RouteFilter extends OncePerRequestFilter {
private Logger log = LoggerFactory.getLogger(getClass());
// These are the URIs that should be processed server-side
private static final Pattern PATTERN = Pattern.compile("^/((api|content|i18n|management|swagger-ui|swagger-resources)/|error|h2-console|swagger-resources|favicon\\.ico|v2/api-docs).*");
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (isServerRoute(request)) {
filterChain.doFilter(request, response);
} else {
RequestDispatcher rd = request.getRequestDispatcher("/");
rd.forward(request, response);
}
}
protected static boolean isServerRoute(HttpServletRequest request) {
if (request.getMethod().equals("GET")) {
String uri = request.getRequestURI();
if (uri.startsWith("/app")) {
return true;
}
return PATTERN.matcher(uri).matches();
}
return true;
}
}
Solution using a REST controller
First step is to configure client, set useHash: false in app-routing-module.ts
In index.html, change <base href="./" /> to <base href="/" />
But it's not enough because it does not support deep linking which means linking to a client route from an external link like from a mail message or from another web site, or veen when refreshing page in browser.
When deep linking, the server receives the request first and if the URL has to be handled by client side, it will not be found so our server app must detect whether the URL has to be served as-is by server (e.g. all API calls) or forwarded to index.html so that the javascript app can interpret it.
ClientRouteForwarder .java
package com.mycompany.myapp.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* A REST controller that forwards all GET requests that did not match a RequestMapping to /index.html so that
* client routes can be handled by client code in browser.
*
* This works because Spring resolves exact matches first.
*/
#Controller
public class ClientRouteForwarder {
#GetMapping(value = "/**/{[path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}
As per their official documentation Configuring html5, AngularJS uses a “#” in it’s urls. HTML5Mode of AngularJS removes these “#” from URL.
Activate HTML 5 Mode
Create html5.mode.config.js file in webapp/app/blocks/config/ directory:
(function() {
'use strict';
angular
.module('<YourAppName>')
.config(html5ModeConfig);
html5ModeConfig.$inject = ['$locationProvider'];
function html5ModeConfig($locationProvider) {
$locationProvider.html5Mode({ enabled: true, requireBase: true });
}
})();
Then open index.html and add this line in head tag:
<base href="/">
We are using AngularJS in frontend and spring in backend. Spring security shall do the authentication and login, but it doesnt even work with the help of spring's tutorial (https://spring.io/guides/tutorials/spring-security-and-angular-js/). Everytime we are trying to log in the "user"-service the principal object is null. In frontend we are receiving this answer: data = Object {data: "", status: 200, config: Object, statusText: "OK"} EVERYTIME. Doesn't matter logging in with correct or incorrect data...I read so many articles, but I couldn't find a solution.
Our login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Login</title>
<link rel="stylesheet" type="text/css" href="stylesheets/bootstrap.min.css" />
<script src="scripts/angular.min.js"></script>
<script src="scripts/login.js"></script>
<style>
body {
position: relative;
}
</style>
</head>
<body ng-app="LoginApp">
<div class="modal show" ng-controller="LoginController">
<div class="modal-header">
<h1 class="text-center">Login</h1>
</div>
<div class="modal-body">
<form>
<div class="control-group">
<div class="controls">
<input class="input-block-level" type="text" placeholder="Username" ng-model="username" ng-change="checkValid()" ng-disabled="requesting">
</div>
</div>
<div class="control-group">
<div class="controls">
<input class="input-block-level" type="password" placeholder="Password" ng-model="password" ng-change="checkValid()" ng-disabled="requesting">
</div>
</div>
<span class="error" ng-bind="errormessage" ng-show="error"></span>
<!--
<div class="control-group">
<label class="checkbox">
<input type="checkbox">Remember me</label>
</div>
-->
</form>
</div>
<div class="modal-footer">
<!--
<button class="btn btn-link">Forgot password?</button>
-->
<button class="btn btnExtra btn-large btn-primary" ng-click="submitLogin()" ng-disabled="requesting || !valid">Login</button>
</div>
</div>
</body>
</html>
Our login.js:
(function(angular) {
const app = angular.module("LoginApp",[]);
app.controller("LoginController", ["$scope", "$http", function($scope, $http){
$scope.username = "";
$scope.password = "";
$scope.errormessage = "";
$scope.error = false;
$scope.valid = false;
$scope.requesting = false;
$scope.submitLogin = function() {
$scope.requesting = true;
$scope.error = false;
const credentials = {
username: $scope.username,
password: $scope.password
};
const headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get("user", { headers: headers }).then(function(data){
if(data.data.name) {
window.location.href = "/";
}
else {
$scope.error = true;
$scope.requesting = false;
$scope.errormessage = "Username / Passwort ist falsch!";
}
},
function(reason) {
$scope.error = true;
$scope.requesting = false;
if(reason.status === 404 || reason.status === 408){
$scope.errormessage = "Verbindung zum Server konnte nicht hergestellt werden!";
}else if (reason.status === 403){
$scope.errormessage = "Username / Passwort ist falsch!";
}else{
$scope.errormessage = "Unbekannter Fehler ist bei der Anfrage aufgetreten! Bitte versuchen Sie es erneut";
}
})
};
$scope.checkValid = function(){
if($scope.username != undefined && $scope.username != null && $scope.username.length > 1 &&
$scope.password != undefined && $scope.password != null && $scope.password.length > 1){
$scope.valid = true;
}else{
$scope.valid = false;
}
};
}
]);
})(window.angular);
Our authentication-service (as mentioned in tutorial or many posts):
#RestController
public class UserController {
#RequestMapping(value = "/user")
public Principal user(Principal user) {
return user;
}
}
The SecurityWebAppInitializer with a custom filter that shall log the IP and username.
#Order(2)
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
super.beforeSpringSecurityFilterChain(servletContext);
insertFilters(servletContext,new MultipartFilter(),new MDCFilter());
}
}
Finally our Spring Security config
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
#Configuration
#EnableWebSecurity(debug=true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select email,pwHash,true from user where email = ?")
.authoritiesByUsernameQuery(
"select email, rolle_rollenname from user where email = ?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user", "/login", "/logout", "login.html").permitAll()
.anyRequest().authenticated()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.formLogin()
.loginPage("/login")
//.logoutSuccessHandler(new customLogoutSuccessHandler())
.and()
.logout()
.logoutUrl("/logout");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/scripts/**")
.antMatchers("/stylesheets/**");
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
When using custom authentication and the default loginpage it works without problems. Maybe the login.html or login.js is wrong...
UPDATE
When I use .httpBasic() without specifying the loginform a browser dialog appears when I try to access a secured ressource.
I want a redirect to the custom login page instead of the browser dialog. How to do?
Ok, I got it by using JSON Web Tokens, a custom Stateless Filter and giving the token everytime back to the frontend, when they ask for sth.
Try this :
.factory('AuthFactory', ['$http', 'contextPath', '$q', '$timeout', function ($http, contextPath, $q, $timeout) {
function User() {
};
var currentUser = null;
var userChangeCallbacks = [];
var notifyUserChange = function (newUser) {
angular.forEach(userChangeCallbacks, function (callback) {
$timeout(function () {
callback(newUser);
});
});
};
var exported = {
getCurrentUser: function () {
return currentUser;
},
refresh: function () {
return $q(function (resolve, reject) {
//Get the current user
$http.get(contextPath + '/rest/user/current')
.success(function (data) {
currentUser = new User();
for (var key in data) {
currentUser[key] = data[key];
}
notifyUserChange(currentUser);
resolve(currentUser);
})
});
},
registerUserChangeHandler: function (callback) {
console.log("registered handler: " + callback);
userChangeCallbacks.push(callback);
}
};
return exported;
}]);
Then call that refresh method in your login controller.
LOGIN CONTROLLER
$scope.login = function (username, password) {
UserService.login({
'username': username,
'password': password
}, function () {
AuthFactory.refresh();
$state.go("home");
});
};
i am using grails 3.0.9 , use javax.websocket to create a chatting application.
this is my code...
`
import grails.util.Environment
import javax.servlet.ServletContext
import javax.servlet.ServletContextEvent
import javax.servlet.ServletContextListener
import javax.websocket.server.ServerEndpoint
import javax.websocket.server.ServerContainer
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.servlet.annotation.WebListener
#WebListener
#ServerEndpoint("/chatEndPoint")
class ServerEndPointDemo implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.servletContext
final ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
if (Environment.current == Environment.DEVELOPMENT) {
serverContainer.addEndpoint(ServerEndPointDemo)
}
println "--- we have a connection"
int defaultMaxSessionIdleTimeout = 0 //config.timeout ?: 0
serverContainer.defaultMaxSessionIdleTimeout = defaultMaxSessionIdleTimeout
}
catch (IOException e) {
log.error e.message, e
}
}
#Override
public void contextDestroyed(ServletCo
ntextEvent event) {
}
#OnOpen
public void handleOpen(){
println "is connecting"
}
#OnClose
public void handleClose(){
println "closed lah!"
}
#OnMessage
public String handleMessage(String message){
println "receiveed message from client = "+message
return "my message~"
}
#OnError
public void handleError(Throwable t){
println "error ~"
}
}
this is my controller..
chatController.groovy
package com.akiong.maintenance
class ChatController {
def index() {
}
}
this is my gsp
index.gsp
<html>
<head>
<title>Chatting</title>
<script language="javascript" type="text/javascript">
var websocket;
function init() {
output = document.getElementById("output");
}
function send_echo() {
var wsUri = "ws://localhost:8080/chatEndPoint";
writeToScreen("Connecting to " + wsUri);
websocket= new WebSocket(wsUri);
websocket.onopen = function (evt) {
writeToScreen("Connected !");
doSend(textID.value);
};
websocket.onmessage = function (evt) {
writeToScreen("Received message: " + evt.data);
websocket.close();
};
websocket.onerror = function (evt) {
writeToScreen('<span style="color: red;">ERROR:</span> '
+ evt.data);
websocket.close();
};
}
function showErrorInfo(e) {
alert('Error connecting socket'+e);
}
function doSend(message) {
websocket.send(message);
writeToScreen("Sent message: " + message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
</script>
</head>
<body>
<h1>Echo Server</h1>
<div style="text-align: left;">
<form action="">
<input onclick="send_echo()" value="Press to send"
type="button">
<input id="textID" name="message" value="Hello Web Sockets"
type="text">
<br>
</form>
</div>
<div id="output"></div>
</body>
</html>
i have no idea..i cannot connect to endpoint...
i already read this question grails javax.websocket issues
but im using grails 3.0.9, its look like diferent because under grails 3.0.9 havenot a folder with "scripts"
This is my java class
public class ServerEndPointDemo
{
#OnOpen
public void handleOpen()
{
System.out.print("Connectin is created");
}
#OnMessage
public String handleMessage(String message)
{
System.out.print("message from Client = "+message);
String replyMessage = "echo"+message;
System.out.print("message send to Client = "+replyMessage);
return replyMessage;
}
#OnClose
public void handleClose()
{
System.out.print("Connectin is closed");
}
#OnError
public void handleError(Throwable e)
{
e.printStackTrace();
}
}
This is my jsp page
<!DOCTYPE html>
<html>
<head>
<title>WEB SOCKET 01</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<form>
<input type="text" name="t1" id="textMessage">
<input type="button" value="SendMessage" onclick="sendMessage()" >
</form>
<textarea rows="10" cols="20" id="messagesTextArea"></textarea>
<script type="text/javascript" language="javascript">
var messagesTextArea = document.getElementById("messagesTextArea");
var textMessage = document.getElementById("textMessage");
var webSocket = new webSocket("ws://localhost:8080/WebSocketPrj01/ServerEndPointDemo");
webSocket.Onopen = function Message(){processOpen(message);};
webSocket.Onmessage = function Message(){processMessage(message);};
webSocket.Onclose = function Message(){processClose(message);};
webSocket.Onerror = function Message(){processError(message);};
function processOpen(message)
{
messagesTextArea.value +="server Connected....."+"\n";
}
function processMessage(message)
{
messagesTextArea.value +="Receive from server....."+message.data+"\n";
}
function processClose(message)
{
webSocket.send("client disconnected");
messagesTextArea.value +="server DISConnected....."+"\n";
}
function sendMessage()
{
alert("enter");
if(textMessage.value!=="close")
{
alert(textMessage.value);
webSocket.send(textMessage.value);
alert("2");
messagesTextArea.value +="send to server....."+textMessage.value()+"\n";
alert("3");
textMessage.value="";
alert("4");
}
else{
alert("else message");
webSocket.close();
}
}
function processError(message)
{
webSocket.send("client disconnected");
messagesTextArea.value +="error....."+"\n";
}
</script>
</body>
</html>
This line is not working webSocket.send(textMessage.value);
Also I am getting this error on console while inspecting element
TypeError: webSocket is not a constructor newjsp.jsp:25.
TypeError: webSocket is undefined
It should be:
var webSocket = new WebSocket("ws://localhost:8080/WebSocketPrj01/ServerEndPointDemo");
("WebSocket" starting with capital letter).
I am trying to implement a basic server client communication through web sockets. I have console prints in my server to tell me when a conection opeened and when a message is recieved. As soon as i press send button on my client, i get an "something recieved" console print from the server but the onopen function of client (pasted below) is not fired. Same happens with other functions as well. What am i doing wrong ?
Here is my code :
Client Side code for html age:
<script type="text/javascript">
document.getElementById("msg").value = "Website Opened";
var ws = new WebSocket("ws://localhost:8080/PushServlet/echo");
ws.onopen = function ()
{
document.getElementById("msgArea").textContent += "Connection Opened \n";
};
function connect()
{
ws = new WebSocket("ws://localhost:8080/PushServlet/echo");
var wss = "ws = " + ws.toString();
document.getElementById("msgArea").textContent += wss;
};
ws.onmessage = function(message)
{
document.getElementById("msgArea").textContent += message.data;
};
function postToServer()
{
if(ws == null)
{
document.getElementById("msgArea").textContent += "Connection Null \n";
}
ws.send(document.getElementById("msg").value);
document.getElementById("msg").value = "";
};
ws.onclose = function(evt)
{
document.getElementById("msgArea").textContent += "closed";
};
function updateTextBox()
{
document.getElementById("msg").value = "Blah Blah Blah";
};
function closeConnect()
{
document.getElementById("msg").value = "Blah Blah Blah";
ws.close();
};
</script>
</head>
<body>
<div>
<textarea rows="4" cols="100" id="msgArea" readonly></textarea>
</div>
<div>
<input id="msg" type="text" value="Website Opened 1" />
<button type="submit" id="sendButton" onclick="postToServer()">Send</button>
<button type="submit" id="update_button" onclick="connect()" > Update </button>
</div>
</body>
</html>
Server Side code for websocket connection:
package com.servlet.pushServlet;
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/echo")
public class EchoEndpoint {
#OnMessage
public void onMessage(Session session, String msg) {
try {
session.getBasicRemote().sendText("Send something");
System.out.println("Some thing recieved");
session.close();
} catch (IOException e) { }
}
}