I am trying to create a delete function that will delete my data using React, which will make an Axios call to my API which will proceed to remove the data from the DB.
I managed to make a somewhat working copy eventually, but why does my original code hit a CORS error?
Function Call
function deleteProposal(refNum) {
console.log('Delete Proposal Clicked', refNum);
const apiData = {
refNum: refNum,
};
AxiosCall.deleteProposal(JSON.stringify(apiData)).then((result) => {
console.log('deleteProposal Result: ' + result);
console.log('deleteProposal Result.data: ' + result.data);
});
}
Axios Call
const deleteProposal = (data) => {
console.log('delete Proposal');
return axiosHTTP.delete('/deleteProposal', data);
};
Spring API
#CrossOrigin
#ResponseBody
#DeleteMapping(value="deleteProposal", consumes = "application/json")
public HttpStatus deleteProposal(#RequestBody HashMap<Object,Object> selectedProposal) {
System.out.println("deleteProposal: "+selectedProposal);
return HttpStatus.OK;
}
Is it because this isn't the right way to do it in the first place, or is it due to something else?
Please note that I am still relatively new to all the technologies I'm using.
Related
I want to get shop by userId and userName if the shop for that user exists.
i have used the url in axios get call as follows:
const url = `${config.apiUrl}/api/Shops/`
useEffect(() => {
axios.get(url + `details?id=${user.id}&shopuser=${user.username}`)
.then((res) => {
setShop(res.data);
})
.catch((error)=>{
console.log(error);
}
)
At the back-end asp.net core web api I have used the following action Method:
[HttpGet("{dashboard}")]
public async Task<ActionResult<Shop>> Dashboard(int id,string shopuser)
{
var shop = await _context.Shops.FirstOrDefaultAsync(a=>a.UserId==id && a.UserName==shopuser);
if (shop == null)
{
return NotFound();
}
return shop;
}
The problem is that the call is not sent to this action method with correct parameters and hence the response with error error 500 .
The controller name is as follows:
{
[Route("api/[controller]")]
[ApiController]
public class ShopsController : ControllerBase
{
.......
....
}
}
I have checked the Network tab of console the parameters are sent as follows:
/api/Shops/details?id=1&shopuser=asifranjha
thanks in advance for help.
[HttpGet("{dashboard}")] annotation means that your get request is expecting a route parameter (dashboard).So if you want to trigger your method, you should call /api/Shops/1?shopuser=asifranjha (I recommend you to keep the same naming so you should change dashboard to id or the opposite, you could also provide the type for your route with route constraints).
If you want to use /api/Shops/details?id=1&shopuser=asifranjha, you should just change your annotation parameter so it would look like this: [HttpGet("details")] and everything should work.Good luck with your project!
I want to post an id to the backend and get the expected result, so
here is the code in the frontend side :
import axios from "axios"
export async function getList(val) {
return await axios.post('http://localhost:5107/PlantsInfo', { id:val }).then(({ data }) => {
return data;
});
}
and in the backend, I have code something like this:
app.MapPost("/PlantsInfo", ([FromServices] DastShafaContext context, int? id) =>
{
// database interaction code according to the id
}
When I attempt this and check it by setting a breakpoint, it takes a request but without an id (that is null)...
But when I attempt to pass an id through Postman, everything is okay.
I think it seems the main problem is related to Axios.
How can I fix it?
This is how my problem was solved!
return await axios.post('http://localhost:5107/GetPlantInfoById?id=' + val).then(({ data }) => {
return data;
});
But this is not the standard way.
I still welcome the best way
I am in the middle of learning react/redux and decided to take an old JQuery UI that makes request to Spring REST API and rewrite it in React. I am using Axios to make the request. In my old Query UI , I do the following when a form is submitted.
var formInputs = $(form).serialize();
$.post("/addAttrItem", formInputs, function(updated){
refresh();
showRecord(updated);
displayControlMsg("Record below was added successfully");
}
This is handled by the following code below in Spring
#ResponseBody
#RequestMapping(value="/someURL", method=RequestMethod.POST)
public AttrItem restfulAdd(AttrItem item) throws Exception
{
item.setLastUpdate(new java.util.Date());
itemService.create(item);
return item;
}
When sending the request through JQuery, AttrItem item param populated with all right values sent in by JQuery
However when I try the following axios
axios.post(someUrl, data).then
(res => {
dispatch(addAttributeSync(res));
}).catch(error =>{
alert('add item failed ' + error);
}
the AttrItem item param while not null itself, is empty with none of the fields set to values from the form. I confirmed that the data object contains right data prior to the post request.
See if mapping the HTTP request body to the method argument item using #RequestBody annotation helps.
#ResponseBody
#RequestMapping(value="/someURL", method=RequestMethod.POST)
public AttrItem restfulAdd(#RequestBody AttrItem item) throws Exception
{
item.setLastUpdate(new java.util.Date());
itemService.create(item);
return item;
}
The following seems to have resolved the issue. In React I added header config
return dispatch => {
var config = {
headers: { 'Content-Type': 'application/json' },
};
axios.post(someUrl, data).then
(res => {
dispatch(addAttributeSync(res));
}).catch(error =>{
alert('add item failed ' + error);
}
And I modified my Spring Controller endpoint to set the consumes and produces attribute as follows.
#ResponseBody
#RequestMapping(value="/attributeItem", method=RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AttributeItem restfulAdd(#RequestBody AttributeItem attributeItem) throws Exception
{
attributeItem.setLastUpdate(new java.util.Date());
attributeItemService.create(attributeItem);
return attributeItem;
}
I have my Spring-Boot service setup so I can send messages through websocket to my browser and it works.
//#MessageMapping
#RequestMapping(value = "/notify")
#SubscribeMapping("/notification")
#SendTo("/topic/notification")
public String sendNotification() throws Exception {
sendMessage();
return "Request to update Tanks has been sent!";
}
public void sendMessage() {
this.messagingTemplate.convertAndSend("/topic/notification", "IT WORKS");
}
Here's the console log from chrome:
<<< MESSAGE
destination:/topic/notification
content-type:text/plain;charset=UTF-8
subscription:sub-1519225601109-13
message-id:f2qodiqn-8
content-length:8
IT WORKS
I want to be able to receive a message from the service and update the state in react, so, that it refetches from the backend. This is what my client looks like:
var socket = new SockJS("http://localhost:6667/refresh");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('connected: ' + frame);
stompClient.subscribe('/topic/notification', function(notification){
console.log(notification.body);
//this.showNotification(JSON.parse(notification.body).content);
//this.showNotification(notification.body);
})
}, function(err) {
console.log('err', err);
});
And the fetch in componentDidMount()
fetch(`http://localhost:6666/front/objects`)
.then(result=>result.json())
.then(fuelTanks=>this.setState({fuelTanks}))
.catch(function(err) {
console.log('Could not fetch: ' + err.message);
}
)
I can't use this.showNotification(notification.body), hence I can't set the state to be able to refetch my objects. I tried making methods outside the class but then I can't use anything from the main class.
Is there a way to make react run componentDidMount again, or better, just access the fetch method in my class when I get a message from spring through the websocket?
Like this:
componentDidMount(){
var socket = new SockJS("http://192.168.1.139:8610/refresh");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('connected: ' + frame);
stompClient.subscribe('/topic/notification', function(notification){
refetchTanks(); // call fetch tanks -> can't use "this"
})
}, function(err) {
console.log('err', err);
});
Thanks!
I know, it is a bit old question, but since it pops every time when you search for stomp issue, i thought of answering it. The way to access this in callbacks is to bind callbacks with this first, then the whole of object can be accessed in the callback.
Example:
connectCallBack(){
this.setState({loading:false})
}
errorCallback=()=>{
}
componentDidMount() {
axios.post('http://localhost:8080/subscribe', null, { params: {
deviceId
}})
.then(response => response.status)
.catch(err => console.warn(err));
const socket = new SockJS('http://localhost:8080/test');
const stompClient = Stomp.over(socket);
//stompClient.connect();
stompClient.connect( {}, this.connectCallBack, this.errorCallback);
If see above code both callbacks can access this.
I tried everything to be able to use my class methods and the state in stompClient's .subscribe method. I was able to connect and reconnect if the service died, nevertheless it wasn't working.
I decided to use react-stomp, which worked. I could use a class method in onMessage=.... This is what my code looks like:
<SockJsClient
url = 'http://localhost:8610/refresh/'
topics={['/topic/notification']}
onConnect={console.log("Connection established!")}
onDisconnect={console.log("Disconnected!")}
onMessage={() => this.update()} <------ this method performs a new GET
request
debug= {true}
/>
I also had to send the message in a specific way on the server side, since I was getting a JSON error when sending a string.
this.messagingTemplate.send("/topic/notification", "{"text":"text"}");
<<< MESSAGE
destination:/topic/notification
content-type:text/plain;charset=UTF-8
subscription:sub-0
message-id:aaylfxl4-1
content-length:49
{
"text": "text"
}
It currently works, but I am curious if there are other, better solutions to this issue.
EDIT: a much better solution here! Use the code from the first post and create a variable before connect to be able to access this like this var self = this;, then just access is as self.update() after subscribe!
I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.