How can I call a generic WebAPI action from JavaScript? and pass the T type.
C#:
public class ClassUtilitiesController : ApiController
{
[HttpPost]
public string GetClassNamespace<T>()
{
return typeof(T).FullName;
}
}
JavaScript:
$.post('/api/ClassUtilities', {
T: 'string',
// ...
});
I need an action that returns something from input T type (ex. typeof(T).FullName or Create an instance and call a method). What should I do?
The JSON data of the POST request it's actually a string, meaning that you have to deserialize is as the object you want.
Try:
public class ClassUtilitiesController : ApiController
{
[HttpPost]
public string GetClassNamespace(string data)
{
T t;
try
{
t = JsonConvert.DeserializeObject<T>(data);
}
catch (Exception e)
{
return e;
}
return t.FullName;
}
}
Related
I have a dropzone function to upload pdf files and receive it in my controller method to process and save in database, but i need to pass an extra parameter which is the value from a select, i already pass it but i receive "null" value in controller method.
this is mi JQuery dropzone method:
...
$("div#uploadFile").dropzone({
url: "/demo/uploadFile"
,params: {
name: $( "#selectedName" ).val()
}
,acceptedFiles: ".pdf"
,clickable: false
,maxFilesize: 100
,addRemoveLinks: true
,init: function() {
this.on('addedfile', function(file) {
//i can see the value in alert
alert($( "#selectedName" ).val());
});
this.on('success', function(file, json) {
alert("ok");
});
});
the controller method (trying to receive in requestParam):
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public ResponseEntity<?> uploadFile( #RequestParam("name") String name, MultipartFile file) {
//here print parameter and is NULL
System.out.println("My extra param received is : "+ name);
//process the file-pdf
...
....
}
Any idea? thanks in advance!
Can you try:
$("div#uploadFile").dropzone({
url: "/demo/uploadFile"
,acceptedFiles: ".pdf"
,clickable: false
,maxFilesize: 100
,addRemoveLinks: true
,init: function() {
this.on('addedfile', function(file) {
//i can see the value in alert
alert($( "#selectedName" ).val());
});
this.on("sending", function(file, xhr, formData) {
formData.append("name", $( "#selectedName" ).val());
console.log(formData);
});
this.on('success', function(file, json) {
alert("ok");
});
});
You can convert any file to byte array on JS side and decode it in java side, so you can send byte array instead of file. Use something like this on your JS side (You should add promises to this JS method):
function getBufferArray(file) {
var fr = new FileReader();
fr.readAsArrayBuffer(file);
return new Uint8Array(fr.result);
}
And on your java side:
public class BASE64DecodedMultipartFile implements MultipartFile {
private final byte[] imgContent;
public BASE64DecodedMultipartFile(byte[] imgContent) {
this.imgContent = imgContent;
}
#Override
public String getName() {
// TODO - implementation depends on your requirements
return null;
}
#Override
public String getOriginalFilename() {
// TODO - implementation depends on your requirements
return null;
}
#Override
public String getContentType() {
// TODO - implementation depends on your requirements
return null;
}
#Override
public boolean isEmpty() {
return imgContent == null || imgContent.length == 0;
}
#Override
public long getSize() {
return imgContent.length;
}
#Override
public byte[] getBytes() throws IOException {
return imgContent;
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(imgContent);
}
#Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
}
For pharameters try adding to your dropzone:
params: {
new_value: 'value'
},
How can should I send the data from Spring controller to the client? Do I need to wrap it as Json or is there an easier way?
(noob in web. So, please bear with me)
#RequestMapping("/abc")
#RestController
public class ListController {
#RequestMapping(value = "/d", method = RequestMethod.GET)
public StringOrJson? getData() {
return "myData";
}
On the client:
function checkBoxToggled(){
$(document).get('abc/d', function( data ) {
alert('Data Loaded2:' + data );
});
}
It will be great if you declare the #RequestMapping as follows:
#RequestMapping(value = "/d", method = RequestMethod.GET, produces = "application/json")
You can return simple String response.
Or you can always reutrn JSONObject as follows:
return new JSONObject("{'data':'myData'}");
In that case the return value in the method signature should be replaced to JSONObject.
You just need to modify your Controller to:
#RequestMapping("/abc")
#Controller
public class ListController {
#RequestMapping(value = "/d", method = RequestMethod.GET)
public String getData() {
return "myData";
}
}
And the client should be:
jQuery.get("abc/d", function (data) {
alert('Data Loaded2:' + data );
});
Whenever I'm sending JSON from client to server and it tries to convert this JSON into java object. It is giving following exception
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: N/A\n at [Source:
java.io.PushbackInputStream#18f89001; line: 1, column: 1102] (through
reference chain:
Request["fields"]->java.util.ArrayList[0]->Field["value"]); nested
exception is com.fasterxml.jackson.databind.JsonMappingException
My java code:
public class Field implements Serializable {
private String name;
private String value;
public Field(){
}
public Field(String name, String value ) {
this.name = name;
this.value = value;
}
public String getName(){
return name;
}
public String getValue(){
return value;
}
public void setValue(String newValue) throws Exception {
someValidation();//Some validation on value
value = newValue;
}
public void setName(String name) {
this.name = name;
}
}
public class Request implements Serializable{
private String name;
private List<Field> fields;
public Request() {
}
public String getName() {
return viewName;
}
public void setName(String name) {
this.name = name;
}
public List<Field> getFields() {
return fields;
}
public void setField(List<Field> fields) {
this.fields = fields;
}
}
My JSON is :
{
"name" : "name0",
"fields" : [{"name":"abc0", "value":"0"}, {"name":"def0", "value":"0"}]
}
Your code looks fine, I think the problem might be with your request header, which can confuse the server.
I suggest you try to post a simpler JSON to your server, maybe just a string and find out step by step where is your failure point, check your request and maybe simulate one with POSTMAN or something familiar to make sure the client isn't the problem here.
I am trying to pass a javascript array to the back end. But apparently, I am not expecting the right type in the back end. What type should I expect? I added the delete method as well. Do I need to iterate in this array to be able to delete records?
[{"questionId":135,"answer":"A6.1"},{"questionId":135,"answer":"A6.2"}]
function deleteAnswers(arrayToDelete) {
$http.post(baseUrl + "Admin/deleteAnswers", {data: arrayToDelete})
.success(function (data, status, headers, config) {})
}
[HttpPost]
public ActionResult deleteAnswers(string data) {
Console.Write(data);
Response.StatusCode = 200;
return Content("Sucess");
}
public void deleteAnswers(dynamic answersToDelete) {
try {
using (TestEntities testEntity = new TestEntities()) {
foreach (var item in answersToDelete) {
Console.Write(item.);
//remove from
}
testEntity.SaveChanges();
}
} catch (Exception ex) {
Console.Write(ex);
}
}
You are sending an object with a property data with an array value, so your signature should look something like this:
[HttpPost]
public ActionResult deleteAnswers(MyObject data)
{
Console.Write(data);
Response.StatusCode = 200;
return Content("Sucess");
}
public class MyObject {
public List<Answer> Data { get; set; }
}
public class Answer {
public int QuestionId { get; set; }
public string Answer { get; set; }
}
I have multiple methods with different signature, and each method has a try-catch block with custom log exception. (Same structure on multiple controllers).
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName1");
}
public static ActionResult Two(Date param3, bool param4)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName2");
}
}
I wonder if there's a way to avoid try-catch block for every method and execute another
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
// Do something (*)
// Call "ActionWithTryCatch" method that has a "function argument" to "Do something (*)"
}
public ActionResult ActionWithTryCatch(MyDelegate del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
¿How can I do that? I've seen examples using delegates but I understand that's strongly typed, so didn't find a way to do that. Thanks!
The pattern that you are describing is close to a form of Aspect Oriented Programming (AOP). However, if you just want to apply specific Try Catch error handling logic to all actions on your controller then it's probably not worth it for you to pull in a whole AOP framework. Instead you could leverage the HandleErrorAttribute or override the OnException method of the controller class.
For example your could write your controller like this:
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
this.service.MethodOne(param1, param2);
return View("ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
this.service.MethodTwo(param3, param4);
return View("ViwName2");
}
protected override void OnException(ExceptionContext filterContext)
{
LogException(filterContext.Exception.Message);
AddModelError(filterContext.Exception.Message);
var errorView = new ViewResult { ViewName = "~/Path/To/Error/View" };
filterContext.Result = errorView;
}
}
If you wanted to abstract it out even more then you could just move the overridden OnException logic into a base controller class and then have all of your controller inherit from the base controller.
If you want to see some additional methods of unified error handling in MVC then check out this blog too: https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
UPDATE
Per my comment, if you insist on implementing the pattern you are describing you could use this modified version of gilmishal's answer.
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
return this.ActionWithTryCatch(() => this.service.MethodOne(param1, param2), "ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
return this.ActionWithTryCatch(() => this.service.MethodTwo(param3, param4), "ViwName2");
}
public IActionResult ActionWithTryCatch(Action action, string viewName)
{
try
{
action.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
this method you created won't work properly if you need to pass parameters to this function in js - so I am assuming you are calling only parameterless methods.
in that case you could use Func<IActionResult> instead of MyDelegate.
public TResult ActionWithTryCatch<TResult>(Func<TResult> del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
throw;
}
}
this will be more similar to your javascript implementation, and will return a 500 http result upon unhandledexception.
You should call it like this, if you want IActionResult return type -
ActionWithTryCatch<IActionResult>(MethodThatReturnsIActionResult, viewName);
I recommand you look into generics