Trying to pass an object to a spring controller using Jquery getJSON - javascript

I'm trying to pass a javascript object
var questionConstraintLineItem = {
"string1": "bhanu",
"string2": "prasad"
};
Using jQuery getJson
$.getJSON("/SafeSiteLive/common/createTaskWizard/saveTask.json", {
questionConstraintLineItem: questionConstraintLineItem
}, function (data)
{
try {
dialog.dialog("close");
getGroups();
} catch (e) {
alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
}
});
To my spring controller
#RequestMapping(value = "common/createTaskWizard/saveTask.json", method=RequestMethod.GET)
public #ResponseBody void saveTask(
QuestionConstraintLineItem questionConstraintLineItem) {
rest of the code...
I have tried using #RequestParam("questionConstraintLineItem") also.
Neither method works. Without the #RequestParam the request goes through to the server but the object is not filled with the data.
Am I doing this the wrong way?
Here is the QuestionConstaintLineItem POJO
public class QuestionConstraintLineItem implements Serializable{
private String string1;
private String string2;
public String getString1() {
return string1;
}
public void setString1(String string1) {
this.string1 = string1;
}
public String getString2() {
return string2;
}
public void setString2(String string2) {
this.string2 = string2;
}
}
Here is the error I get when I use the #RequestParam.

Related

How to load and insert some HTML only if the user is human (reCaptcha v3)?

Goal
To protect the web app from malicious spam bot crawlers and similar malicious actors my goal is to use reCAPTCHA v3 to analyze the user visiting the site and if the Captcha v3 score is good enough (let's say 0.5 or better) use the Fetch API to POST the token and so verify it and if the score is good enough as mentioned previously return the E-Mail address within some HTML. For simplicity sake, the function loadContactbubble() gets executed when you click a button.
Problems
I am not sure where to implement the if (response.score => 0.5) check.
Frontend semi-works in that regard that in the network browser debug tools it gives an response but in the console it prints out the response as undefined
Is my implementation secure enough? Can't the secret key somehow siphoned or similar?
I get a lot of CSP warnings in the browser, might this be an issue in production?
Code
I was following this guide: https://dev.to/spencer741/google-recaptcha-v3-server-side-validation-using-asp-net-core-5-0-3hfb (that means that the code is like 80-90% from this article)
My appsettings.json contains the secret key and the siteverify Link (API link).
GHttpModels.cs:
using System;
using System.Runtime.Serialization;
namespace _projectname.Tooling
{
public class GRequestModel
{
public string path { get; set; }
public string secret { get; set; }
public string response { get; set; }
public string remoteip { get; set; }
public GRequestModel(string res, string remip)
{
response = res;
remoteip = remip;
secret = Startup.Configuration["GoogleRecaptchaV3:Secret"];
path = Startup.Configuration["GoogleRecaptchaV3:ApiUrl"];
if (String.IsNullOrWhiteSpace(secret) || String.IsNullOrWhiteSpace(path))
{
//Invoke logger
throw new Exception("Invalid 'Secret' or 'Path' properties in appsettings.json. Parent: GoogleRecaptchaV3.");
}
}
}
//Google's response property naming is
//embarrassingly inconsistent, that's why we have to
//use DataContract and DataMember attributes,
//so we can bind the class from properties that have
//naming where a C# variable by that name would be
//against the language specifications... (i.e., '-').
[DataContract]
public class GResponseModel
{
[DataMember]
public bool success { get; set; }
[DataMember]
public string challenge_ts { get; set; }
[DataMember]
public string hostname { get; set; }
//Could create a child object for
//error-codes
[DataMember(Name = "error-codes")]
public string[] error_codes { get; set; }
}
}
GoogleReCaptchaV3Service.cs:
using System;
using System.Threading.Tasks;
using System.Text.Json;
using System.Web;
using System.Net.Http;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
namespace _projectname.Tooling
{
public class CaptchaRequestException : Exception
{
public CaptchaRequestException()
{
}
public CaptchaRequestException(string message)
: base(message)
{
}
public CaptchaRequestException(string message, Exception inner)
: base(message, inner)
{
}
}
public interface IGoogleRecaptchaV3Service
{
HttpClient _httpClient { get; set; }
GRequestModel Request { get; set; }
GResponseModel Response { get; set; }
void InitializeRequest(GRequestModel request);
Task<bool> Execute();
}
public class GoogleRecaptchaV3Service : IGoogleRecaptchaV3Service
{
public HttpClient _httpClient { get; set; }
public GRequestModel Request { get; set; }
public GResponseModel Response { get; set; }
public HttpRequestException HttpReqException { get; set; }
public Exception GeneralException { get; set; }
public GoogleRecaptchaV3Service(HttpClient httpClient)
{
_httpClient = httpClient;
}
public void InitializeRequest(GRequestModel request)
{
Request = request;
}
public async Task<bool> Execute()
{
// Notes on error handling:
// Google will pass back a 200 Status Ok response if no network or server errors occur.
// If there are errors in on the "business" level, they will be coded in an array;
// CaptchaRequestException is for these types of errors.
// CaptchaRequestException and multiple catches are used to help seperate the concerns of
// a) an HttpRequest 400+ status code
// b) an error at the "business" level
// c) an unpredicted error that can only be handled generically.
// It might be worthwhile to implement a "user error message" property in this class so the
// calling procedure can decide what, if anything besides a server error, to return to the
// client and any client handling from there on.
try
{
//Don't to forget to invoke any loggers in the logic below.
//formulate request
string builtURL = Request.path + '?' + HttpUtility.UrlPathEncode($"secret={Request.secret}&response={Request.response}&remoteip={Request.remoteip}");
StringContent content = new StringContent(builtURL);
Console.WriteLine($"Sent Request {builtURL}");
//send request, await.
HttpResponseMessage response = await _httpClient.PostAsync(builtURL, null);
response.EnsureSuccessStatusCode();
//read response
byte[] res = await response.Content.ReadAsByteArrayAsync();
string logres = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Retrieved Response: {logres}");
//Serialize into GReponse type
using (MemoryStream ms = new MemoryStream(res))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(GResponseModel));
Response = (GResponseModel)serializer.ReadObject(ms);
}
//check if business success
if (!Response.success)
{
throw new CaptchaRequestException();
}
//return bool.
return true; //response.IsSuccessStatusCode; <- don't need this. EnsureSuccessStatusCode is now in play.
}
catch (HttpRequestException hre)
{
//handle http error code.
HttpReqException = hre;
//invoke logger accordingly
//only returning bool. It is ultimately up to the calling procedure
//to decide what data it wants from the Service.
return false;
}
catch (CaptchaRequestException ex)
{
//Business-level error... values are accessible in error-codes array.
//this catch block mainly serves for logging purposes.
/* Here are the possible "business" level codes:
missing-input-secret The secret parameter is missing.
invalid-input-secret The secret parameter is invalid or malformed.
missing-input-response The response parameter is missing.
invalid-input-response The response parameter is invalid or malformed.
bad-request The request is invalid or malformed.
timeout-or-duplicate The response is no longer valid: either is too old or has been used previously.
*/
//invoke logger accordingly
//only returning bool. It is ultimately up to the calling procedure
//to decide what data it wants from the Service.
return false;
}
catch (Exception ex)
{
// Generic unpredictable error
GeneralException = ex;
// invoke logger accordingly
//only returning bool. It is ultimately up to the calling procedure
//to decide what data it wants from the Service.
return false;
}
}
}
}
Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
//from captchav3
using _projectname.Tooling;
namespace _projectname
{
public class CookieCheckMiddleware
{
private readonly RequestDelegate _next;
public CookieCheckMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Cookies["ModalShown"] == null && httpContext.Request.Path != "/Cookies")
{
httpContext.Response.Redirect("/Cookies?q="+ httpContext.Request.Path);
}
await _next(httpContext); // calling next middleware
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CookieCheckMiddlewareExtensions
{
public static IApplicationBuilder UseCookieCheckMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CookieCheckMiddleware>();
}
}
public class Startup
{
internal static IConfiguration Configuration { get; private set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
//public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Captcha v3
services.AddHttpClient<IGoogleRecaptchaV3Service, GoogleRecaptchaV3Service>();
services.AddTransient<IGoogleRecaptchaV3Service, GoogleRecaptchaV3Service>();
services.AddControllers();
//Register dependencies
services.AddRazorPages();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//app.Use(async (ctx, next) =>
//{
// await next();
// if (ctx.Response.StatusCode == 404 && !ctx.Response.HasStarted)
// {
// //Re-execute the request so the user gets the error page
// string originalPath = ctx.Request.Path.Value;
// ctx.Items["originalPath"] = originalPath;
// ctx.Request.Path = "/Cloud";
// await next();
// }
//});
// orig
//app.UseExceptionHandler("/Errors/{0}");
app.UseStatusCodePagesWithRedirects("/Errors/{0}");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// TEST
app.UseCookieCheckMiddleware();
app.UseAuthentication();
app.UseAuthorization();
var cookiePolicyOptions = new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict,
};
app.UseCookiePolicy(cookiePolicyOptions);
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
// Experimental
endpoints.MapControllers();
});
}
}
}
_Layout.cshtml:
<button onclick="loadContactbubble();">Load contacts</button>
site.js (only the function for brevity):
function loadContactbubble() {
grecaptcha.execute('sitekeyhere', { action: 'onclick' }).then(function (token) {
console.log(token);
fetch("/load/contactbubble?RecaptchaToken=" + token, {
method: "POST",
body: token,
})
}).then((response) => {
console.log(response);
if (!response.ok) {
const errorBuild = {
type: "Error",
message: response.message || "Something went wrong",
data: response.data || "",
code: response.code || "",
};
}
}
//addText("Error: " + JSON.stringify(errorBuild));
//toggleLoader(2, "hidden");
//return;
)
}
ApiController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using _projectname.Tooling;
using System.Threading.Tasks;
namespace _projectname.Controllers
{
public class SignUpModel
{
public string RecaptchaToken { get; set; }
}
[ApiController]
[Route("load/contactbubble")]
public class SignUp : ControllerBase
{
IGoogleRecaptchaV3Service _gService { get; set; }
public SignUp(IGoogleRecaptchaV3Service gService)
{
_gService = gService;
}
[HttpPost]
public async Task<IActionResult> Post([FromQuery] SignUpModel SignUpData)
{
GRequestModel rm = new GRequestModel(SignUpData.RecaptchaToken,
HttpContext.Connection.RemoteIpAddress.ToString());
_gService.InitializeRequest(rm);
if (!await _gService.Execute())
{
//return error codes string.
return Ok(_gService.Response.error_codes);
}
//call Business layer
//return result
return base.Content("<div>Welcome human! Here is our secret e-mail: test#test.com</div>", "text/html");
}
}
}
Errors
If I click the button the following gets printed out in the console:
Uncaught (in promise) TypeError: can't access property "ok", response is undefined
The response contains proper HTML in the network tab, which is weird.
How do I fix this?
Your function
function (token) {
console.log(token);
fetch("/load/contactbubble?RecaptchaToken=" + token, {
method: "POST",
body: token,
});
}
does not return anything, hence why the argument passed to the next .then((response) => ... is undefined.
Make this function return the fetched data, and it should hopefully work:
function (token) {
console.log(token);
return fetch("/load/contactbubble?RecaptchaToken=" + token, {
method: "POST",
body: token
});
}
(Well, it should then at least forward the fetch result to the next .then((response) => .... I have not looked for other errors in your code, so "it should hopefully work" is to be understood with respect to the one problem i explained here...)

How to map html form data to a Spring Boot model containing a composite key?

I have popup form in my html that looks like this:
<dialog id="favDialog">
<div id="feedback"></div>
<form id="add_watchlist_symbol_form">
<label for="symbol">Enter Symbol:</label>
<input type="text" class="form-control" id="symbol" placeholder="SYMB"/><br><br>
<button type="submit" class="btn btn-default" id="add-watchlist-symbol-btn">Add</button>
</form>
<button id="cancelBtn" value="cancel">Cancel</button>
</dialog>
The dialog pops up successfully when I click a button.
The dialog contains a button called Add. It's click event is handled by javascript which sends an ajax POST request containing the form field values to Spring Boot like this:
function submit_watchlist_symbol() {
console.log("Im in submit_watchlist_symbol");
var formData = {
symbol: $("#symbol").val(),
name: "My Portfolio"
}
//$("#btn-search").prop("disabled", true);
$.ajax({
type: "POST",
contentType: "application/json",
url: "/api/v1/AddSymbolToWatchlist",
data: JSON.stringify(formData),
dataType: 'json',
success: function (result) {
if(result.status=="Done") {
$('#feedback').html(result.data.symbol +" added.");
}
else {
$('#feedback').html("<strong>Error</strong>");
}
console.log("ERROR: ",e);
},
error: function (e) {
alert("Error!")
console.log("ERROR: ",e);
}
});
// Reset FormData after Posting
resetData();
}
When I click that button I get Spring Boot error:
Resolved
[org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: null; nested exception is
com.fasterxml.jackson.databind.JsonMappingException: N/A at [Source:
(PushbackInputStream); line: 1, column: 11] (through reference chain:
net.tekknow.moneymachine.model.Watchlist["symbol"])]
I suspect the form data is not being mapped correctly to the Watchlist.java model due to the model containing a composite key, like this:
#Entity
#Table(name = "watchlist")
public class Watchlist {
#EmbeddedId
public WatchlistId watchlistId;
public String getSymbol() {
return watchlistId.getSymbol();
}
public void setSymbol(String symbol) {
watchlistId.setSymbol(symbol);
}
public String getName() {
return watchlistId.getName();
}
public void setName(String watchlistName) {
watchlistId.setName(watchlistName);
}
public String toString() {
return "watchlist:symbol=" +getSymbol() +", name="+getName();
}
}
where watchlistId contains the symbol and name, like this:
#Embeddable
public class WatchlistId implements Serializable {
#Column(name="symbol")
private String symbol;
#Column(name="name")
private String name;
WatchlistId() {
}
WatchlistId(String symbol, String name) {
this.symbol = symbol;
this.name = name;
}
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WatchlistId that = (WatchlistId) o;
return Objects.equals(symbol, that.symbol) && Objects.equals(name, that.name);
}
#Override
public int hashCode() {
return Objects.hash(symbol, name);
}
}
Here is the Spring Boot controller that handles the request:
#PostMapping("/AddSymbolToWatchlist")
#ResponseBody
public AddWatchlistSymbolResponse addSymbolToWatchlist(#RequestBody Watchlist watchlist){
System.out.println("made it to AddWatchlistSymbolResponse");
// Create Response Object
AddWatchlistSymbolResponse response = new AddWatchlistSymbolResponse("Done", watchlist);
return response;
}
The AddWatchlistSymbolResponse class looks like this:
public class AddWatchlistSymbolResponse {
private String status;
private Object data;
public AddWatchlistSymbolResponse(){
}
public AddWatchlistSymbolResponse(String status, Object data){
this.status = status;
this.data = data;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
Suggestions?
I figured it out. I changed the controller to this:
#PostMapping(value = "/AddSymbolToWatchlist")
#ResponseBody
public WatchlistIdResponse addSymbolToWatchlist(#RequestBody WatchlistId watchlistId){
System.out.println("made it to AddWatchlistSymbolResponse");
watchlistService.addSymbolToWatchlist(watchlistId);
// Create Response Object
WatchlistIdResponse response = new WatchlistIdResponse("Done", watchlistId);
return response;
}
And created a separate response for WatchlistId called WatchlistIdResponse.java:
package net.tekknow.moneymachine.model;
public class WatchlistIdResponse {
private String status;
private Object data;
public WatchlistIdResponse() {
}
public WatchlistIdResponse(String status, Object data) {
this.status = status;
this.data = data;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
The reason is that the Watchlist class contains only the WatchlistId property. WatchlistId contains the symbol and name properties that make up the composite index.

how to receive Dictionary in WebAPI from javascript json

I have this controller function in WebAPI:
public class EntityController : APIController
{
[Route("Get")]
public HttpResponseMessage Get([FromUri]Dictionary<string, string> dic)
{ ... }
}
and my request, in javascript, looks like this:
{
"key1": "val1",
"key2": "val2",
"key3": "val3"
},
but the parse failed. is there a way to make this work without writing to much code? Thanks
my full request:
http://localhost/Exc/Get?dic={"key1":"val1"}
You could use a custom model binder:
public class DicModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(Dictionary<string, string>))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
string key = val.RawValue as string;
if (key == null)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type");
return false;
}
string errorMessage;
try
{
var jsonObj = JObject.Parse(key);
bindingContext.Model = jsonObj.ToObject<Dictionary<string, string>>();
return true;
}
catch (JsonException e)
{
errorMessage = e.Message;
}
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Cannot convert value: " + errorMessage);
return false;
}
}
And then use it:
public class EntityController : APIController
{
[Route("Get")]
public HttpResponseMessage Get([ModelBinder(typeof(DicModelBinder))]Dictionary<string, string> dic)
{ ... }
}
In the ModelBinder I used the Newtonsoft.Json library to parse the input string, then converted it to Dictionary. You could implement a different parsing logic.
See here ... with some adaptations to make. I think your difficulties are in how to call the URL.
Complex type is getting null in a ApiController parameter

Passing Object to Java from AngularJS

i'm in trouble with this problem.
I've a Javascript structur like this:
$scope.setting= {
enabled: false,
host:"",
port:"",
user:"",
pwd:"",
path:"/",
filePrefix:"",
type:"",
sendInterval:"",
dataPeriod:"",
compression:false,
subscription:[]
};
In the controller i modify the subscription array, but when i pass it to the java code:
$http.post('/api/testAndSetFTPSetting', $scope.setting)
.success(function (data) {
console.log(data);
})
.error(function (data, status, header, config) {
});
the subscription array is null.
Here the API
#RequestMapping(value = {"/api/testAndSetFTPSetting"}, method={RequestMethod.POST})
#ResponseBody
public boolean testAndSetFTPSetting(FTPConfiguration ftp) throws JAXBException {
System.out.println(ftp.getSubscribtion().size()); // here i've ever 0 and ftp.getSubscribtion() return me null
return true;
}
Here the Java Class who controls the object:
#XmlRootElement(name="FTPconfiguration")
#XmlAccessorType (XmlAccessType.FIELD)
public class FTPConfiguration{
boolean enabled = false;
String host="127.0.0.1";
int port=22;
String user="root";
String pwd="";
String path="/";
String filePrefix="data";
FTPType type=FTPType.SFTP;
int sendInterval=15;
int dataPeriod=5;
boolean compression=false;
#XmlElementWrapper(name="subscriptions")
List<String> subscription = new LinkedList<String>();
public FTPConfiguration() {
}
public FTPConfiguration(boolean enabled,String host, int port, String user, String pwd, String path, String filePrefix,
FTPType type, int sendInterval, int dataPeriod, boolean compression, List<String> subscription) {
super();
this.host = host;
this.port = port;
this.user = user;
this.pwd = pwd;
this.path = path;
this.filePrefix = filePrefix;
this.type = type;
this.sendInterval = sendInterval;
this.dataPeriod = dataPeriod;
this.compression = compression;
if(subscription != null)
this.subscription.addAll(subscription);
}
// setter and getter method
Where is my fault?
Finally solved!!! The problem was that the javascript array in java is a comma separated String. For this the value that i received was null!
EX
JavaScript OBJ
var arr = ["1", "2", "3"];
$http.post('someUrl', arr)...
Java
#RequestMapping(value = {"/someUrl"}, method={RequestMethod.POST})
public void foo(String s) { // s will be = "1,2,3"
}

Object POSTed to Wcf web service request is null

This is my .Net Wcf WebService Service1.cs
[DataContract]
public class CompositeType
{
string degree;
string Stre;
string YearOfPass;
string Institute;
string StatusMessage;
string CheckStatus;
[DataMember]
public string deg
{
get { return degree; }
set { degree = value; }
}
[DataMember]
public string stream
{
get
{ return Stre; }
set
{ Stre = value; }
}
[DataMember]
public string yop
{
get
{ return YearOfPass; }
set
{ YearOfPass = value; }
}
[DataMember]
public string inst
{
get
{ return Institute; }
set
{ Institute = value; }
}
[DataMember]
public string StatusMsg
{
get
{ return StatusMessage;}
set
{ StatusMessage = value; }
}
[DataMember]
public string check
{
get
{ return CheckStatus; }
set
{ CheckStatus = value; }
}
}
Here, in Wcf, variables and methods are created. I used AngularJS GET method to call this Wcf Service. The output is Perfect. But using POST method in AngularJS, I get: StatusMsg= "Request object is nullTestCORS_1101.CompositeType"
Here is the AngularJS code:
var app = angular.module('starter', []);
app.controller('customerCtrl',function($scope,$http){
var Composite={"deg":"s","stream":"m","inst":"t","yop":"w"};
console.log(Composite);
$http({
url:'http://192.168.1.50/TestSubCors/Service1.svc/GetDataUsingDataContract',
method: 'POST',
data:Composite,
headers:{'Content-Type':'application/json;charset=utf-8'}
}).then(function(response){
var parsed_data = angular.toJson(response);
console.log("Success")
console.log(parsed_data);
$scope.events=parsed_data;
}),function(response){
$log.error('failure loading', errorresponse);
}
});
Why I am getting this null request object?

Categories

Resources