Reputation: 9891
I have a Web API controller defined as follows:
[HttpPost]
public class EmailActivitiesController : ApiController
{
public HttpResponseMessage EmailAClientWithNewMailMessage(FormDataCollection aFormData)
{
}
}
which is invoked with the following jQuery:
var aFormData = {};
$.support.cors = true;
$.ajax({
cache: false,
url: '/api/EmailActivities/EmailAClientWithNewMailMessage',
type: "POST",
dataType: "json",
data: aFormData,
success: function (callback) {
//Platform.LoadingPanelHide();
alert("Successfully sent the email.");
},
error: function (data) {
var errorObject = JSON.parse(data);
//Platform.LoadingPanelHide();
alert('There was a problem sending the email message to the individual selected.\n\n' + errorObject.responseText.message);
}
});
and that all works and the API method is reached. However as soon as I populate aFormData as below:
var aFormData = {};
aFormData['aMessageInfo'] = "test";
//And sending via form data
$.support.cors = true;
$.ajax({
cache: false,
url: '/api/EmailActivities/EmailAClientWithNewMailMessage',
type: "POST",
dataType: "json",
data: aFormData,
success: function (callback) {
//Platform.LoadingPanelHide();
alert("Successfully sent the email.");
},
error: function (data) {
var errorObject = JSON.parse(data);
//Platform.LoadingPanelHide();
alert('There was a problem sending the email message to the individual selected.\n\n' + errorObject.responseText.message);
}
});
then the method is not reached and I receieve instead a HttpResonseCode.OK with the following JSON response:
{
"result": null,
"success": false,
"error": {
"code": 0,
"message": "An internal error occured during your request!",
"details": null,
"validationErrors": null
},
"unAuthorizedRequest": false
}
For the life of me I cannot figure out why this is occurring. Has anyone got any insights.
UPDATE
Here is the POST request.
And the exception log:
ERROR 2015-01-29 15:51:08,250 [17 ] Default - Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)
at System.Web.Http.ModelBinding.FormatterParameterBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()
UPDATE #2
I have now gone with the suggested approach of using a DTO as follows:
var aMessageInfo = {
subject: Base64.encode($("#txtSendEmailSubject").val()),
body: Base64.encode($("#txtSendEmailBody").val())
};
$.support.cors = true;
$.ajax({
cache: false,
url: '/api/EmailActivities/Dono2',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(aMessageInfo),
success: function (callback) {
//Platform.LoadingPanelHide();
alert("Successfully sent the email.");
},
error: function (data) {
var errorObject = JSON.parse(data);
//Platform.LoadingPanelHide();
alert('There was a problem sending the email message to the individual selected.\n\n' + errorObject.responseText.message);
}
});
and then in the WebAPI it works if I only have:
public class EmailActivitiesController : ApiController
{
[HttpPost]
public HttpResponseMessage Dono2(MessageInfo aMessageInfo)
{
}
}
but as soon as I also have EmailAClientWithNewMailMessage as a method:
public class EmailActivitiesController : ApiController
{
[HttpPost]
public HttpResponseMessage Dono2(MessageInfo aMessageInfo)
{
}
[HttpPost]
public HttpResponseMessage EmailAClientWithNewMailMessage(FormDataCollection aFormData)
{
}
I get the error:
{
"message":"An error has occurred.",
"exceptionMessage":"Multiple actions were found that match the request: \r\nDono2 on type MakersLane.WebApi.Controllers.EmailActivitiesController\r\nEmailAClientWithNewMailMessage on type MakersLane.WebApi.Controllers.EmailActivitiesController",
"exceptionType":"System.InvalidOperationException",
"stackTrace":" at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n at Abp.WebApi.Controllers.Dynamic.Selectors.AbpApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
Now while there are two actions on the controller Dono2 and EmailClientWithNewMessage I do not understand why they cannot be told apart as they clearly have different names and although both have a single parameter these are named differently and are of different types.
Can anyone explain why this is occurring?
Upvotes: 1
Views: 2044
Reputation: 2282
Problem is that: ABP system configured to work with JSON data only. When you change code like that, it works:
$.ajax({
cache: false,
url: '/api/EmailActivities/EmailAClientWithNewMailMessage',
type: "POST",
contentType: 'application/json',
dataType: "json",
data: JSON.stringify({ aMessageInfo: 'test' }),
success: function (callback) {
//Platform.LoadingPanelHide();
alert("Successfully sent the email.");
},
error: function (data) {
var errorObject = JSON.parse(data);
//Platform.LoadingPanelHide();
alert('There was a problem sending the email message to the individual selected.\n\n' + errorObject.responseText.message);
}
});
I added contentType. In that way, method is called but, form data does not come to the method. Maybe FormDataCollection does not like JSON posts.
Why you don't use just a simple DTO class instead of FormDataCollection? This is more natural for ABP projects.
In AbpWebApiModule, it clears all formatters and adds JSON and plain text.
private static void InitializeFormatters()
{
GlobalConfiguration.Configuration.Formatters.Clear();
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
GlobalConfiguration.Configuration.Formatters.Add(formatter);
GlobalConfiguration.Configuration.Formatters.Add(new PlainTextFormatter());
}
Maybe you can use GlobalConfiguration.Configuration.Formatters.Add to add other formatters.
How did I find the problem? ABP defines an event to handle exceptions (starting from v0.3.2):
This can help you while finding errors.
EventBus.Default.Register<AbpHandledExceptionData>(
data =>
{
//use data.Exception to see exception details
});
Upvotes: 0
Reputation: 301
I think Joel is correct. I had issues passing strings in as parameters and thus wrap all my inputs in DTO objects and it works fine.
Cheers Jeff
Upvotes: 1
Reputation: 469
I am not on a windows machine so can't test this write now but I will offer the alternative solution below. It is a problem with the mapping from the query string sent in the POST to the FormDataCollection
and I think it can be solved by using a JSON object like this:
{ aFormData : { aMessageInfo : "test" } }
but I cannot confirm. I believe the following solution to be better, so use it unless you have to use FormDataCollection.
I would recommend using a view model instead of FormDataCollection
as the mapping of values becomes tedious:
public class Message
{
public string Client { get; set; }
public string Content { get; set; }
}
Then change your controller to:
public HttpResponseMessage EmailAClientWithNewMailMessage(Message data)
{
// use the deserialised object here
return data.Client;
}
And your jquery would look like this (notice the addition of the content-type parameter and the JSON.stringify call):
var aFormData = {};
aFormData['aMessageInfo'] = "test";
//And sending via form data
$.support.cors = true;
$.ajax({
cache: false,
url: '/api/EmailActivities/EmailAClientWithNewMailMessage',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(aFormData),
success: function (callback) {
//Platform.LoadingPanelHide();
alert("Successfully sent the email.");
},
error: function (data) {
var errorObject = JSON.parse(data);
//Platform.LoadingPanelHide();
alert('There was a problem sending the email message to the individual selected.\n\n' + errorObject.responseText.message);
}
});
Upvotes: 2
Reputation: 301
Did you look at the Logs/logs.txt file? It will have the exception thrown in there.
Cheers Jeff
Upvotes: 1
Reputation: 788
I think this code is overriding the result.
try
{
if (task.Result == null)
{
return new AjaxResponse();
}
if (task.Result is AjaxResponse)
{
return task.Result;
}
return new AjaxResponse(task.Result);
}
Upvotes: 1