Can't connect react.js login to SpringBoot app - reactjs

I am trying to call REST endpoints on one application (spring-boot) from another (reactjs). The applications are running on the following hosts and ports.
REST application, using spring boot, http://localhost:8080
HTML application, using reactjs, http://localhost:9000
I am trying to send the login info from reactjs to spring-boot but without success.
Reactjs:
import React from 'react';
export default class Login extends React.Component {
constructor() {
super();
this.state = {
login:"",
password:""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState({login: this.state.login});
}
render() {
return (
<form role="form">
<div>
<input type="text" name="login" onChange={this.handleChange} />
<input type="password" name="password"/>
</div>
<button onClick={this.login.bind(this)}>Login</button>
</form>
);
}
login () {
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
var url = "http://localhost:8080/test/login"
xmlhttp.open("POST", url );
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify({login: this.state.login}));
}
}
and Spring-boot:
#CrossOrigin(origins = "http://localhost:9000")
#RequestMapping(value = "/test/login")
public Boolean testLogin(#RequestParam String login) {
if ( login.equals ("ajt"))
return true;
else {
return false;
}
}
I can see that the two apps are connecting for even though reactjs gives me error 400, when I submit, the console of the Spring-boot app tells me:
Resolved exception caused by Handler execution: org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'login' is not present
I can only assume that from the Spring-boot side, it cannot translate what ever is sent via react.js.
ps: bear with me, I have been coding for about 6 months.

I knew this looked familiar. Sorry my last answer didn't fix all your issues.
Your current problem is here;
public Boolean testLogin(#RequestParam String login) {
Should be
public Boolean testLogin(#RequestBody String login) {
EDIT:: Second problem.
Your handleChange function isn't taking in any values! It should look more like this;
handleChange(value) {
this.setState({login: value});
}
When your input field calls this function, it needs to pass a value from the input into the state. Your current code is essentially the same as this;
this.state.login = this.state.login;
Which obviously isn't going to get you anywhere.
Try that change. If it still does not work, be sure you open your dev-tools in your browser and step through the code line by line to be sure it is executing and storing the values you want it to.

Related

This email already exists validation

I am making a React application where i submit the username, password and email to the mongo database.
Now I am trying to figure out how I could check inside of React whether the user or email already exists. Meaning so I could show an error-box telling the user to choose something else as an username.
I do know how to do it when I use Node.js and Handlebars. I know how to check my database with the Find() method of mongoose.
But I just don't understand how to think now that I am using React.
When I check if the user already exists in the back-end and it shows that it does exist, how could I inform my front-end (React) that it does?
When I use node.js and handlebars I use flash messages, and it works fine.
I guess my question could be summarized to, how should I do to get my React front-end to cooperate with my Node/Express back-end to share info about a username inside of the database already existing?
I have no code to show, this is more of asking for advice on what tools or methods I should use. I just can't figure it out.
Thank you in advance!
You'll need to have your back-end inform your front-end about whether or not an email has already been used since the front-end has no way of knowing without the back-end.
Basically, when a user tries to register, you should send your registration request from the front-end to the back-end without worrying about duplicate emails. The response from the server should indicate whether or not registration was successful, and if not, why not.
For example the registration component in your React code might look something like this:
class RegistrationComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
error: "",
}
}
handleSubmit = async event => {
event.preventDefault();
const { email, password } = this.state;
const response = await sendRegisterRequest(email, password);
const responseJson = await response.json();
if (response.status !== 200) {
this.setState({error: reponseJson.error});
} else {
// handle successful registration
}
}
render() {
const { error } = this.state;
return (
<form onSubmit={ this.handleSubmit }>
<span>{ error }</span>
{ /* rest of the form */ }
</form>
)
}
}
Where sendRegisterRequest is some module that handles sending registration requests to the server.
Note that this front-end logic expects the server to respond with status 200 on successful registration and with something else (status 400) if there is an error. Also if there is an error, the server should respond with a payload body that looks like: {"error": "That email is already in use"}.
You mention that you know how to check for existing email addresses on the server, so just check in that manner before creating a new account, and if the email address is already in use send the error payload with a status of 400.
You can respond with a 400 status if it occurs then send the error message to the frontend that way. e.g. return res.status(400).json(errors).
catch((err) => {
console.log(err);
if(err.status=404){
alert("email already used");
}

reactJS application on Amazon Web Services with passed in values

I have a reactJS application which is currently hosted in an S3 bucket on Amazon Web Services. Amazon has provided me with an entry point to my application, lets assume the entry point is
http://myreactjsapp.s3-website.us-east-2.amazonaws.com/
What I would like to do is be able to pass a value into the application and have that value available within the app. So, for example, I would like to do something like this:
http://401kmobileapp.s3-website.us-east-2.amazonaws.com/?userFirstName=Jonathan&userLastName=Small
Then, I would like to be able to reference the value of userFirstName and the value of userLastName within the app so when I display my default page, the application can display something like Welcome Jonathan Small!
How would I do this?
Thank you for any suggestions.
You can use the URLSearchParams API to parse the query parameters and put them in your component state and use it for rendering.
Example
class App extends React.Component {
constructor(props) {
super(props);
const urlParams = new URLSearchParams(window.location.search);
this.state = {
userFirstName: urlParams.get("userFirstName") || "",
userLastName: urlParams.get("userLastName") || ""
};
}
render() {
const { userFirstName, userLastName } = this.state;
return (
<div>
Welcome {userFirstName} {userLastName}
</div>
);
}
}

Meteor React tutorial interacting with mongo not working

I have been trying for a while to learn how to build mobile apps with Javascript, and honestly I have no idea how anyone is able to do anything. Everything is broken. Every tutorial I've tried has failed to work for some bizarre reason. I am at my wits end.
I've finally decided to try and be even simpler, and just do the most basic tutorial I can find. What could go wrong. Well, it only took 3 pages of almost no code to completely stop working. I've done this, and I cannot insert anything to my db. My app fetches no data. When trying to add a new task, it gets added then disappears almost immediately, with a message stating insert failed: Method '/tasks/insert' not found (not even an error with some traceback).
The code really couldn't be simpler:
// imports/api/tasks.js
import { Mongo } from 'meteor/mongo';
export const Tasks = new Mongo.Collection('tasks');
// imports/ui/App.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { withTracker } from 'meteor/react-meteor-data'
import { Tasks } from '../api/tasks.js';
import Task from './Task.js';
// App component - represents the whole app
class App extends Component {
handleSubmit(event) {
event.preventDefault();
// find the text field via the react ref
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Tasks.insert({ text, createdAt: new Date() });
// Clear form
ReactDOM.findDOMNode(this.refs.textInput).value = '';
}
renderTasks() {
return this.props.tasks.map((task) => (
<Task key={task._id} task={task} />
));
}
render() {
return (
<div className="container">
<header>
<h1>Todo List</h1>
<form className="new-task" onSubmit={this.handleSubmit.bind(this)} >
<input
type="text"
ref="textInput"
placeholder="Type to add new tasks"
/>
</form>
</header>
<ul>
{this.renderTasks()}
</ul>
</div>
);
}
};
export default withTracker(() => {
return {
tasks: Tasks.find({}).fetch(),
};
})(App);
What is wrong? What am I missing?
The tutorial is indeed out of date and should be updated.
Background
In June 2017 there was a big security issue with allow/deny identified and the feature has been blocked since then.
Meteor allowed you to define client collection, that automatically synced with the server when the methods insert, update, remove were called on the client.
In order to control the access permissions, the allow/deny feature was implemented.
Now without allow/deny you will get the insert failed: Method '/tasks/insert' not found when classing SomeCollectionOnClient.insert but since this feature is obsolete (you will even get a big warning when setting it up), you need to create a server side method and call it from the client in order resolve this issue:
On the server create this method and ensure it is in the import chain from server/main.js:
new ValidatedMethod({
name: 'tasks.insert',
validate(args) {
// better use simpl-schema here
if (!args.text || !args.createdAt) {
throw new Meteor.Error('incompleteArgs', 'args are incomplete')
}
},
run (args) {
// check user permissions...
return Tasks.insert({ text, createdAt })
}
})
In your client component you can then call it via:
// find the text field via the react ref
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Meteor.call('tasks.insert', {text, createdAt: new Date()}, (err, res) => {
// do something on err / on res
})
Note that this couples your component to the server side method and you may rather try to implement some containers for your pages that handle all the connection / pub-sub / method calling activity wile your components solely render content.
More to read / used in this answer:
https://guide.meteor.com/react.html
https://guide.meteor.com/security.html
https://docs.meteor.com/api/methods.html#Meteor-call
https://guide.meteor.com/methods.html#validated-method

Property 'calendar' does not exist on type 'typeof client'

I'm trying to connect my Google Calender to my React website. I've got a component called Calendar. I've used the JS tutorial from Google and I've changed it to work in Typescript. I've got the authentication and authorization already working, however fetching data from the calendar is not working. I'm getting the following error when compiling/editing.
[ts] Property 'calendar' does not exist on type 'typeof client'. Did you mean 'calendars'?
I've already downloaded the types for the gapi.client.calendar and as you can see in the image below, they are also found in the #types folder. I'm kind of stuck and I don't know how I can fix this issue..
Here is my code from my Calendar.tsx
import * as React from 'react';
import { Button } from 'semantic-ui-react'
import googleApiKey from '../googleapi-key.json';
const CLIENT_ID = googleApiKey.CLIENT_ID;
const API_KEY = googleApiKey.API_KEY;
const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
const SCOPES = "https://www.googleapis.com/auth/calendar.readonly";
class Calendar extends React.Component {
constructor(props: any) {
super(props);
console.log(CLIENT_ID);
console.log(API_KEY);
this.handleClientLoad = this.handleClientLoad.bind(this);
this.handleAuthClick = this.handleAuthClick.bind(this);
this.handleSignoutClick = this.handleSignoutClick.bind(this);
this.initClient = this.initClient.bind(this);
this.updateSigninStatus = this.updateSigninStatus.bind(this);
this.listUpcomingEvents = this.listUpcomingEvents.bind(this);
}
componentDidMount() {
this.initClient();
}
public render() {
return (
<div>
<Button onClick={this.handleAuthClick}>
authorizeButton
</Button>
<Button onClick={this.handleSignoutClick}>
signoutButton
</Button>
</div>
);
}
/**
* On load, called to load the auth2 library and API client library.
*/
public handleClientLoad() {
gapi.load('client:auth2', this.initClient);
}
/**
* Sign in the user upon button click.
*/
public handleAuthClick(event: any) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
public handleSignoutClick(event: any) {
gapi.auth2.getAuthInstance().signOut();
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
public async initClient() {
await gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(this.updateSigninStatus);
// Handle the initial sign-in state.
this.updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
public updateSigninStatus(isSignedIn: any) {
if (isSignedIn) {
this.listUpcomingEvents();
}
}
/**
* Print the summary and start datetime/date of the next ten events in
* the authorized user's calendar. If no events are found an
* appropriate message is printed.
*/
public listUpcomingEvents() {
console.log(gapi.client.calendar); // <--- Compile error: Does not recognize calendar
}
}
export default Calendar;
EDIT
When performing console.log(gapi.client) I can see that the client contains a calendar object (see image). But why can't I reach it in my own code?
I managed to fix my own problem. After performing console.log(gapi.client) I noticed that calender was already there, so I tried the following gapi.client['calendar'] and it worked as it should. I don't know why Typescript does not recognize the calendar in the first place, so if anybody has an idea feel free to leave a comment.
Try the following
Install types npm i #types/gapi.client.calendar
Include https://apis.google.com/js/api.js & https://apis.google.com/js/platform.js in index.html
Add the following inside types in tsconfig.app.json
"types": [
"gapi",
"gapi.auth2",
"gapi.client",
"gapi.client.calendar"
]
You have to add:
"types": ["gapi", "gapi.auth2", "gapi.client", "gapi.client.calendar"]
in tsconfig.app.js and in tsconfig.json.

Bad request status 400. Required String parameter 'login' is not present

I am trying to build login with react.js and connect it to my springboot.
Here is my code, react.js:
import React from 'react';
export default class Login extends React.Component {
constructor() {
super();
this.state = {
login:"",
password:""
}
}
// This will be called when the user clicks on the login button
login(e) {
e.preventDefault();
console.log(this.state.password)
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// Check if the XMLHttpRequest object has a "withCredentials" property.
// "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// Otherwise, check if XDomainRequest.
// XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// Otherwise, CORS is not supported by the browser.
xhr = null;
}
return xhr;
}
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
if (!xhr) {
throw new Error('CORS not supported');
}
fetch().then(r => r.json())
.then(data => console.log(data))
.catch(e => console.log(e))
}
render() {
return (
<form role="form">
<div>
<input type="text" name="login" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
</div>
<button type="submit"onClick={this.login.bind(this)}>Login</button>
</form>
);
}
}
And this is my springboot code that is located as TestController:
#RestController
public class TestController {
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
public Boolean testLogin(#RequestParam String login, #RequestParam String password) {
if ( login.equals ("ajt"))
return true;
else {
return false;
}
}
Each of them are present in two different ports, react on :9000 and springboot on :8080.
Also, on my react page I get the error:
TypeError: Failed to execute 'fetch' on 'Window': 1 argument required, but only 0 present.(…)
Any ideas?
for info: I have only got 6 months coding behind me -_- please be kind!
There's a handful of small mistakes here. I'll try to point you in the right direction for a few of them.
First of all, I just wouldn't use fetch. It's listed as an expiremntal technology by MDN, and it's browser support is weak. For someone just starting out with web development, you're much better off using a more established and "safe" technology. Either simple XMLHTTP ajax or using the ajax method from jquery. You already seem to be going down the path of XMLHTTP, so I would suggest just replacing your fetch commands with the example I linked above.
Second, you're using two different HTTP methods. Your ajax object is going to send a POST command, but your server is listening for a GET command. This is set up on the following lines;
var xhr = createCORSRequest('POST', "http://localhost:8080/test/login");
#RequestMapping(value = "/test/login", method = RequestMethod.GET )
If you want those two pieces of code to talk to one another, they need to be set to the same method. In this case, you want a POST for both, but it's worth learning the distinction between the two for the future.
Lastly, there's the issue of getting information from your inputs and into your ajax XMLHTTP object. To do this, you're going to want to set up onChange hooks on the inputs, and attach them to a handleChange function within the react component. Use this function to save the values to the component's state, and then later take the values out of state to apply to the xmlhttp.
A simple example of what I am describing;
render() {
return (
<div>
<input type="text" name="login" onChange={this.handleChange}/>
<button onClick={this.login.bind(this)}>Login</button>
</div>
);
}
handleChange(value) {
this.setState({login: value});
}
login () {
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.open("POST", "/test/login");
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xmlhttp.send(JSON.stringify({login: this.state.login}));
}

Resources