How to model AJAX interaction with java spring controller - javascript

I am trying to model a simple ajax interaction between my javascript code and my server.
Client
data = {};
data.url = "google.com";
$.ajax({
type: 'POST',
url: "/first/second",
data: data,
cache: false
}).done(function(response) {
Console.log("hello");
});
Server
#Controller
#HttpOptions(sslHandling = SslHandlingOption.NON_SSL_REDIRECTS_TO_SSL)
public class myController {
#RequestMapping(value = "/first/second", method = RequestMethod.POST)
#ResponseBody
public Map<String, Object> returnURL(#RequestParam(value = "url") String URL) {
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("url", URL);
return responseMap;
}
}
My Error
In the logs, I am getting a 500 (Internal Server error).
Constraints
This code is obviously dumbed down to not give away company info, but the following constraints must be adheared to:
Request method must be POST
the data must be passed to the server as shown (initialized as a blank object)

Related

json string on POST to MVC action method using AJAX is null

I am getting a hard time to find out why the string sent via AJAX request is null. Console.WriteLine(data) shows empty. Status is 200 OK. If I add some code to parse the string received, I get an error stating that JObject.Parse cannot be null. I don't know what am I missing. The javascript code is ok. The action method also seems ok, but my knowledge on Asp.Net Core and MVC is very scarce, so I am not sure. Can someone please point out what am I missing?
The javascript code:
let obj = {email: email_address.value};
let objStringified = JSON.stringify(obj);
$.ajax({
type: 'POST',
contentType: 'application/json; charset=UTF-8',
data: objStringified,
url: '#Url.Action("ReturnCheckAccountDuplication")',
dataType: 'text',
success: function(data) {
console.log(data);
},
error: function(error) {
console.log("Keep trying", error);
}
});
C# code:
[HttpPost]
public ActionResult ReturnCheckAccountDuplication([FromBody] string data)
{
Console.WriteLine(data);
JObject jObject = JObject.Parse(data);
string email = (string)jObject["email"];
bool emailExists = CheckAccountDuplication.Get(email);
string returnResult = emailExists.ToString();
return Content(returnResult);
}
The solution on the controller side is
public class AccountCheckModel
{
public string Email { get; set; }
}
[HttpPost]
public ActionResult ReturnCheckAccountDuplication([FromBody] AccountCheckModel data)
{
string result = CheckAccountDuplication.Get(data.Email).ToString();
return Content(result);
}
Thanks to all the members who commented on my problem, especially John Glenn who provided a solution. I had been trying for several days but without success. My knowledge of Asp.Net Core is very poor indeed. Thank you very much.
The easiest solution is to create a model representing the JSON data that your controller will receive. For example, create a class like so:
public class AccountCheckModel
{
public string email { get; set }
}
Then, use it as the parameter for your controller method:
public ActionResult ReturnCheckAccountDuplication([FromBody] AccountCheckModel data)
This is the preferred way to access the request body. To get the request body as a string, you have to jump through some serious hoops.
An alternative way to send your data via AJAX to your Controller:
var json = {
email: email_address.value
};
$.ajax({
type: 'POST',
data: {'json': JSON.stringify(json)},
url: '#Url.Action("ReturnCheckAccountDuplication")',
dataType: 'json',
success: function(data) {
console.log(data);
},
error: function(error) {
console.log("Keep trying", error);
}
});
And your Controller:
[HttpPost]
public ActionResult ReturnCheckAccountDuplication(string json)
{
Console.WriteLine(json);
JObject jObject = JObject.Parse(json);
string email = (string)jObject["email"];
bool emailExists = CheckAccountDuplication.Get(email);
string returnResult = emailExists.ToString();
return Content(returnResult);
}

Web API POST not found (throws 404 on browser) but works on PostMan and Swagger

My Web API returns 404 when called from AJAX but works well on PostMan and Swagger.
Post method
// POST api/<controller>
[HttpPost]
public int Post(string url, string RData, string aid, string AuthToken)
{
//do something
}
jQuery AJAX call:
function SaveData() {
var $form = $("#formMain");
var data = Serialize($form);
var RData = JSON.stringify(data);
var Url = window.location.href;
var aid = "12332";
//Get Auth Token
$.ajax({
url: "/api/Register/MyAPIKey", //Getting Auth Token first
type: "GET",
success: function (res) {
let AuthToken = res;
//After receiving Auth token, finally submit the registration data. This throws 404 error only on browser.
$.ajax({
type: "POST",
url: '/api/Register',
data: {
'url': Url,
'RData': RData,
'aid': aid,
'AuthToken': AuthToken
},
success: function (data) {
//Show success message
}
});
}
});
}
The error I get in browser console is:
Message: "No HTTP resource was found that matches the request URI 'http://localhost:54318/api/Register'."
MessageDetail: "No action was found on the controller 'Register' that matches the request."
Postman Screenshot: Please note -1 is expected in response, since some other validation in the API method is incorrect. But we can ignore that for now, since the status is 200.
The issue was resolved when I replaced the individual parameters in the Post method with an Object having all these as properties.
[HttpPost]
public int Post(PostData Reg)
{
//do something
}
public class PostData
{
public string url { get; set; }
public string RData { get; set; }
public string aid { get; set; }
public string AuthToken { get; set; }
}
And finally setup the dataType in the ajax method as JSON.
dataType: "json"

SpringBoot / Fetch : Invalid mime type "undefined": does not contain '/'

I'm trying to make a post request with a form data, containing a file and a Json Object.
To perform this, i set the Content-Type to undefined, according to the following post
https://stackoverflow.com/a/25183266/4573767
This causes the browser to set the Content-Type to multipart/form-data
and fill the boundary correctly.
However, on the (springboot) server side, i get this error message :
Resolved exception caused by Handler execution:
org.springframework.web.HttpMediaTypeNotSupportedException: Invalid
mime type "undefined": does not contain '/'
So, it seems that the "undefined" content type is not correctly managed by the browser.
Here is the fetch command, on the client side :
// My document blob
var documentBlob = new Blob([JSON.stringify({ documentName: "toto" })], {
type: "application/json"
});
// My Form data containing a file and the document blob
var formData = new FormData();
formData.append("file", this.state.file);
formData.append("document", documentBlob);
// Fetch command
fetch("/document/", {
method: "POST",
headers: {
"Content-Type": undefined
},
data: formData
}).then(function(response) {
console.log("response!");
});
Here is the server side (spring boot rest controller) :
#RestController
#RequestMapping("/document")
public class DocumentController {
#Autowired
private DocumentRepository documentRepository;
#RequestMapping(value = "/", method = RequestMethod.POST, consumes = { "multipart/form-data" })
public boolean addDocument(#RequestPart("document") Document document, #RequestPart("file") MultipartFile file) {
documentRepository.save(document);
return true;
}
}
"Document" is a simple pojo :
#Entity
public class Document {
private String documentName;
public Document() {
}
public Document(String documentName) {
this.setDocumentName(documentName);
}
public String getDocumentName() {
return documentName;
}
public void setDocumentName(String documentName) {
this.documentName = documentName;
}
}
So, i don't really get if the problem is in the client or server side.
Thanks!
//////////////////////////////
EDIT :
I finally got it working, but using axios instead of fecth:
Here is my finaly spring boot rest controller :
#RequestMapping(value = "/", method = RequestMethod.POST)
public boolean addDocument(#RequestPart("document") Document document, #RequestPart("file") MultipartFile file) {
// Do things!
return true;
}
And my javascript/axios call :
var documentBlob = new Blob([JSON.stringify({ documentName: "test" })], {
type: "application/json"
});
var formData = new FormData();
formData.append("document", documentBlob);
formData.append("file", this.state.file);
axios({
method: "post",
url: "/document/",
data: formData,
config: { headers: { "Content-Type": "multipart/form-data" } }
})
.then(response => {
console.log("it's working!");
console.log(response);
})
.catch(function(error) {
console.log(error);
});
I finally got it working, but using axios instead of fecth.
See the edited original post to see my solution.
I think the issue is in your spring controller request mapping.
You should not have the mapping to / there.
Try this...
#RestController
#RequestMapping("/document")
public class DocumentController {
#Autowired
private DocumentRepository documentRepository;
#RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
public boolean addDocument(#RequestPart("properties") Document document, #RequestPart("file") MultipartFile file) {
documentRepository.save(document);
return true;
}
}
Have you tried to make that request with the "multipart/form-data" content type header?
With that mapping method consuming the defined header, your controller won't be able to parse the request without the proper content type.

How to reload a specfic part of a JSP with a new Model over AJAX (Spring MVC)?

What am I using?
Tomcat 7.0.56
JAVA 1.8
Spring MVC
jQuery
What is the goal?
I'm implementing a content directory.
The user searches for a specific name or phone number for example. Then a list of contacts shows up on the left side of the page. On the right side there should details be displayed of a contact that gets selected.
Where is the problem?
My problem lays on the part of showing the details.
My Code:
index.jsp
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html >
<%# include file="includes/header.jsp" %>
<body>
<div class="pageContainer container-fluid">
<div class="row">
<%# include file="searchBar.jsp" %>
</div>
<div class="row">
<div class="containerOverView col-lg-5 col-md-6 col-sm-12 col-xs-12">
<%# include file="overView.jsp" %>
</div>
<div class="containerDetailView col-lg-7 col-md-6 col-sm-12 col-xs-12">
<%# include file="detailView.jsp" %>
</div>
</div>
</div>
</body>
</html>
detailView.jsp
<ul class="flex-container">
<li class="flex-item-photo">Photo</li>
<li class="flex-item-pdetails">Personal Details</li>
<li class="flex-item-cdetails">Company Details</li>
<li class="flex-item-map">Map:
${detailSearchResult.name}
</li>
</ul>
MainController.java
// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
#RequestMapping(value = "/refreshDetail", method = RequestMethod.POST)
#ResponseBody
private String refreshDetailView(HttpServletRequest hsr, #RequestParam String id, ModelMap model){
model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
return "detailView";
}
main.js
$(document).ready(function(){
$(".overViewListEmployee").click(function () {
var id = $(this).find(".overViewEmployeeID").text();
console.log("Works: " + id + ".");
$.ajax({
type: "POST",
accepts: "text/plain",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "/refreshDetail",
data: ({"id" : id}),
success: function(response) {
$(".containerDetailView").html(response);
}
});
});
});
What exactly doesn't work?
It seems to me, that the ajax request doesn't really get a valid response. From my understanding ajax accepts every kind of response but somehow, when I debug the JavaScript it says that the response has a Response Error: response is not defined
Error Codes:
With the code above:
400: Failed to load resource: the server responded with a status of 400 (Bad Request)
When I comment out following line in main.js:
//contentType: "application/json; charset=utf-8",
406: (Not acceptable)
What does work? (due to debugging)
When I debugged the code I saw that the value of the id is getting into the Controller correctly and also the function Dao.getDetailView(id) works properly. So it's really just an issue of the response.
What else have I tried?
I'm not sure if it works that way, by returning a string with the name of the detailView.jsp page that should be reloaded since I don't know if the model I'm adding the attribute to is also going with it and rendered in the index.jsp (where detailView.jsp is included).
So I also tried to put the Controller-Function like this:
// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
#RequestMapping(value = "/refreshDetail", method = RequestMethod.POST)
private ModelAndView refreshDetailView(#RequestParam String id, ModelMap model){
model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
return new ModelAndView("detailView");
}
and removing this line from main.js:
accepts: "text/plain",
I also tried by returning the model as well:
return new ModelAndView("detailView", model);
I'm not working with JavaScript that often, so it's possibly a really silly and obvious mistake but I can't seem to find it. I literally tried out everything I could find but it looks like I'm stuck here.
I would really appreciate any kind of help on this case :)
Update:
main.js
$(".overViewListEmployee").click(function () {
var id = parseInt($(this).find(".overViewEmployeeID").text());
console.log("Works: " + id + ".");
$.ajax({
type: "POST",
accept:"text/html",
//contentType: "application/json; charset=utf-8",
dataType: "html",
url: "/refreshDetail",
data: ({"id": id}),
success: function(response) {
$(".containerDetailView").html(response);
}
});
});
MainController.java
// Delivers the refresh-information for the ajax request, when the detail view gets reloaded
#RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
private ModelAndView refreshDetailView(HttpServletRequest hsr, #RequestBody IdObject id, ModelMap model){
model.addAttribute("detailSearchResult", Dao.getDetailViewData(id.getId()));
return new ModelAndView("detailView", model);
}
IdObject.java
public class IdObject {
int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Right now the Controller isn't reached at all - still got that 415 Error.
Update 2:
WebInit.java (before)
#Configuration
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer{
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
WebInit.java (after)
#Configuration
public class WebInit implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(WebConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(RootConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Now I'm not sure if it's what you meant but from what I read #AnnotationDrivenConfig is the same as to do it with AnnotationConfigWebApplicationContext. And since I'm doing it with pure Java based Configuration I figured, that'd be my way to go.
But I still got the same error though.
WebConfig.java
#Configuration
#EnableWebMvc
#ComponentScan("com.avectris.newtel")
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/view/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/sass_compiled/**").addResourceLocations("/sass_compiled/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.extendMessageConverters(converters);
}
}
Taking a look to your ajax request I think you are trying to send a json object (but you are not, really) and then your are trying to get the value for the id with #RequestParam instead of #RequestBody.
If you do not want to send a json object in the ajax payload, then your content-type is wrong. Try something like this. The data property is going to be is converted to a query string by jQuery.
$(document).ready(function(){
$(".overViewListEmployee").click(function () {
var id = $(this).find(".overViewEmployeeID").text();
console.log("Works: " + id + ".");
$.ajax({
type: "POST",
headers: {
"Accept" : "text/html",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
url: "/refreshDetail",
//you can get rid of data if you change your url to "/refreshDatil?id="+id
data: {"id" : id},
success: function(response) {
$(".containerDetailView").html(response);
}
});
});
});
If you really want to send a json object, as I have already said on my comments, you need do some changes at your controller level.
Edit
Ok, you are now mixing the two possible solutions. With your current ajax object you are not sending a json object, but a normal http request. You can check the network tab at your browser when the ajax request happend and you will see a request like /refreshDetail?id=yourid, without json object in the paylod.
Your controller, on the other hand, using #RequestBody is trying to get an object like IdObject from the payload, but your are not doing a request like that. Your controller should looks like:
#RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, produces = MediaType.TEXT_HTML_VALUE)
private ModelAndView refreshDetailView(HttpServletRequest hsr, #RequestParam int id, ModelMap model){
model.addAttribute("detailSearchResult", Dao.getDetailViewData(id));
return new ModelAndView("detailView", model);
}
Also remove the brackets from the data property at your ajax object:
$(".overViewListEmployee").click(function () {
var id = parseInt($(this).find(".overViewEmployeeID").text());
console.log("Works: " + id + ".");
$.ajax({
type: "POST",
accept:"text/html",
//contentType: "application/json; charset=utf-8",
dataType: "html",
url: "/refreshDetail",
data: {"id": id},
success: function(response) {
$(".containerDetailView").html(response);
}
});
});
In case you want to send a json payload
If what you really need is send a json object from javascript then you need to change your ajax request
$(".overViewListEmployee").click(function () {
var id = parseInt($(this).find(".overViewEmployeeID").text());
console.log("Works: " + id + ".");
$.ajax({
type: "POST",
accept:"text/html",
dataType: "html",
contentType: "application/json",
url: "/refreshDetail",
data: JSON.stringify({"id": id}),
success: function(response) {
$(".containerDetailView").html(response);
}
});
});
With contentType jQuery is setting the header Content-Type, telling your controller the request is coming with a json payload. Then, JSON.stringify is converting the javascript object with the info to be send into a json string and it will be used by jQuery as payload.
On the backend side, your controller, now, should use #RequestBody because the info is not coming within the request parameters, but a json payload. So, it should looks like something to this:
#RequestMapping(value = "/refreshDetail", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_HTML_VALUE)
private ModelAndView refreshDetailView(HttpServletRequest hsr, #RequestBody IdObject id, ModelMap model){
model.addAttribute("detailSearchResult", Dao.getDetailViewData(id.getId()));
return new ModelAndView("detailView", model);
}
The consumes property at the request mapping, in this case, is not mandatory, as you do not have any other mapping to the same value /refreshDetail, but it makes the code easier to read.
You do not have to use #ResponseBody at your controller, on any of this cases, because you are sending back a view. You will use it when sending back a json, xml, etc.

How to POST data from AngularJS to Struts 2 using REST

I need to get the value from client side to server side. I am using AngularJS and Struts2 REST.
My controller doesn't get the value passed or am I wrong in passing or using RESTful controller?
Here is my code:
angularcontroller.js:
app.controller('saveAddCatCtrl', function($scope, $http){
$scope.save = function(newAddCat){
$http({
method: "POST",
url: "api/additionalcategory",
data: $scope.additionalCategory.addCatName
}).success(function(data, status, header, config){
//success
}).error(function(data, status, header, config){
//error
});
}
});
Here is my REST controller:
public class AdditionalcategoryController extends ActionSupport implements ModelDriven<Object>{
private AdditionalCategoryRepository additionalCategoryRepository =
(AdditionalCategoryRepository)ServletActionContext.getServletContext()
.getAttribute("additionalCategoryRepository");
private List<AdditionalCategory> additionalCategories;
public AdditionalCategory additionalCategory = new AdditionalCategory();
private int id;
public HttpHeaders index(){
setAdditionalCategories(additionalCategoryRepository.findAllAdditionalCategory());
System.out.println("GET api/additionalCategory");
return new DefaultHttpHeaders("index").disableCaching();
}
public HttpHeaders create(){
additionalCategoryRepository.createAdditionalCategory(additionalCategory);
return new DefaultHttpHeaders("create").disableCaching();
}
public void setAdditionalCategories(List<AdditionalCategory> additionalCategories){
this.additionalCategories = additionalCategories;
}
#Override
public Object getModel() {
if (additionalCategories == null){
return additionalCategory;
}else{
return additionalCategories;
}
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<AdditionalCategory> getAdditionalCategories() {
return additionalCategories;
}
public AdditionalCategory getAdditionalCategory() {
return additionalCategory;
}
public void setAdditionalCategory(AdditionalCategory additionalCategory) {
this.additionalCategory = additionalCategory;
}
}
Since angular service http sends JSON, you can send a JSON object using data property. The JSON object will be deserialized to your model class properties because you are using ModelDriven or Action class properties otherwise.
Struts2 REST plugin is made to handle JSON content from the request. It's possible due ContentTypeInterceptor, which is called rest, be able to find JSON handler for the request. The JSON handler is guessed from the Content-Type header or .json extension to the file in the URL. You can use either ways, but second is easier.
Note:
URLs without a file extension the Struts2 REST plugin defaulting to handle xhtml content. So you can modify your code
$http({
method: "POST",
url: "api/additionalcategory.json",
data: {}
}).success(function(data, status, header, config){
//success
}).error(function(data, status, header, config){
//error
});

Categories

Resources