I can't find an answer here to this one (been asked before, but this is specific to MVC5 WebAPI2.1 and jQuery), but in Postman, I'm getting
{ "Message": "No HTTP resource was found that matches the request URI 'https://localhost:44371/api/reportdiscrepancy'.", "MessageDetail": "No action was found on the controller 'ReportProblem' that matches the request." }
error message - which is there (I have other controllers that use GET, but this one is using POST is the only difference and they work just fine). My controller (note: this inherits from ApiController, NOT Controller) - and the code worked fine when it was an ASMX :
[Route("api/reportdiscrepancy")]
[HttpPost]
public async Task<IHttpActionResult> ReportDiscrepancy(string lid, string ProblemTypes, string ProblemTypeOther)
{
string retval = string.Empty;
lid = AppCore.Validation.StripToGUID(lid);
ProblemTypes = AppCore.Validation.StripToDigitInCSVFromStr(ProblemTypes);
ProblemTypeOther = AppCore.Validation.StripAllCharsForMetaTag(StripHtml(ProblemTypeOther));
var session = HttpContext.Current.Session;
if (lid.Length == 36 && ProblemTypes.Length > 0 | ProblemTypeOther.Length > 0)
{
if (IsGUID(lid))
{
if (session != null)
{
if (session.SessionID != null)
{
if (session.SessionID.Length > 0)
{
var dr = new Discrepancy
{
lid = lid,
SessionId = session.SessionID,
IPAddress = Ops.Site.GetUserIP(HttpContext.Current),
UserId = "anonymous"
};
if (User != null)
{
if (User.Identity != null)
{
if (User.Identity.IsAuthenticated)
{
dr.UserId = User.Identity.GetUserId();
}
}
}
dr.ProblemTypeIds = ProblemTypes;
dr.ProblemTypeOther = ProblemTypeOther;
dr.idx = await DiscrepancyReportData.InsertDiscrepancyAsync(dr);
if (dr.idx > 0)
{
retval = "success";
}
}
}
}
}
}
return Ok(retval);
}
The jquery/javascript:
$("#btnSendReport").bind('click', function (e) {
var parElem = $(this).parent().parent().parent();
var listID = parElem.find("#hidLID").val();
var problemTypes = $.map(parElem.find('option:selected'), function (e) { return e.value; }).join(',');
var problemTypeOther = parElem.find("#txtProblemTypeOther").val();
var obj = {};
obj.lid = listID;
obj.ProblemTypes = problemTypes;
obj.ProblemTypeOther = problemTypeOther;
try {
$.ajax({
type: "POST",
url: "../../api/reportdiscrepancy/",
data: JSON.stringify(obj), //not sure if this is necessary w/webAPI
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
var result = data; //not doing anything here
},
error: function (err) {
console.log(err);
}
});
} catch (err) {
console.log(err);
}
});
Anyone have any ideas? There are no examples with WebAPI 2.1 showing a jQuery post (I don't want to use GET because of query string limits and I'm not using a FORM or BODY, so those answers won't work either). The more I look into this, I get many answers, but all very specific to special cases. I would like to just see one working example of POSTing and OBJECT (not a string or anything simple) from JQuery (3.x or higher) to an MVC5 (other versions are different) and NOT Core (totally different) and WebAPI 2.x . How to properly decorate the controller for a jquery post (not a querystring, not a form). Any ideas?
CodePudding user response:
fix the action route by adding ~. The route will start from root
[Route("~/api/reportdiscrepancy")]
fix ajax url to start from root too. also remove stringify and contentType: "application/json; charset=utf-8", since I am not sure that your ancient framework supports it
$.ajax({
type: "POST",
url: "/api/reportdiscrepancy",
data: obj,
dataType: "json",
but I would create a special class for an action too
public class ViewModel
{
public string Lid {get; set;}
public string ProblemTypes {get; set; }
public string ProblemTypeOther {get; set;}
}
[Route("~/api/reportdiscrepancy")]
public async Task<IHttpActionResult> ReportDiscrepancy(ViewModel model)
{
.....
}
CodePudding user response:
On the client side - I made the changes Serge recommended.
$("#btnSendReport").bind('click', function (e) {
var parElem = $(this).parent().parent().parent();
var listID = parElem.find("#hidLID").val();
var problemTypes = $.map(parElem.find('option:selected'), function (e) { return e.value; }).join(',');
var problemTypeOther = parElem.find("#txtProblemTypeOther").val();
if (problemTypeOther === null) { problemTypeOther = '' };
var obj = {};
obj.lid = listID;
obj.ProblemTypes = problemTypes;
obj.ProblemTypeOther = problemTypeOther;
try {
$.ajax({
type: "POST",
url: "../../api/reportdiscrepancy/",
data: obj,
dataType: "json",
success: function (data) {
var result = data;
},
error: function (err) {
console.log(err);
}
});
} catch (err) {
console.log(err);
}
});
In the api controller, the only change required was the inputs to an object (webapi 2.x does automatic conversion - didn't know this)
[Route("api/reportdiscrepancy")]
[HttpPost]
public async Task<IHttpActionResult> ReportDiscrepancy(DiscrepancyPost dp)
{
string retval = string.Empty;
dp.lid = AppCore.Validation.StripToGUID(dp.lid);
dp.ProblemTypes = AppCore.Validation.StripToDigitInCSVFromStr(dp.ProblemTypes);
dp.ProblemTypeOther = AppCore.Validation.StripAllCharsForMetaTag(StripHtml(dp.ProblemTypeOther.xToStr()));
var session = HttpContext.Current.Session;
try
{
if (dp.lid.Length == 36 && dp.ProblemTypes.Length > 0 | dp.ProblemTypeOther.Length > 0)
{
if (IsGUID(dp.lid))
{
if (session != null)
{
if (session.SessionID != null)
{
if (session.SessionID.Length > 0)
{
var dr = new Discrepancy
{
lid = dp.lid,
SessionId = session.SessionID,
IPAddress = Ops.Site.GetUserIP(HttpContext.Current),
UserId = "anonymous"
};
if (User != null)
{
if (User.Identity != null)
{
if (User.Identity.IsAuthenticated)
{
dr.UserId = User.Identity.GetUserId();
}
}
}
dr.ProblemTypeIds = dp.ProblemTypes;
dr.ProblemTypeOther = dp.ProblemTypeOther;
dr.idx = await DiscrepancyReportData.InsertDiscrepancyAsync(dr);
if (dr.idx > 0)
{
retval = "success";
}
}
}
}
}
}
}
catch (Exception)
{
//TODO logging
}
return Ok(retval);
}
and the class:
public class DiscrepancyPost
{
public string lid { get; set; }
public string ProblemTypes { get; set; }
public string ProblemTypeOther { get; set; }
}
Works like a charm. So the problem was in the controller signature (fixed that first and got it to work in Postman perfectly), then in the jQuery side.