Can't pass data to asp.net core - asp.net

I have a problem, I need to send data from my Angular to my ASP.NET Core server. Here is controller:
[HttpPut]
public IActionResult setCoupon(int id, string CouponCode, int DiscountPercent)
{
try
{
var coupon = new Coupon()
{
Id = id,
CouponCode = CouponCode,
DiscountPercent = DiscountPercent
};
return Ok(coupon);
}
catch (Exception)
{
return BadRequest("Wystąpił błąd");
}
}
Here is factory from ngResource (getCoupon is working):
app.factory('couponApi',
function($resource) {
return $resource("/coupon/setCoupon",
{},
{
getCoupon: {
method: "GET",
isArray: false
},
putCoupon: {
method: "PUT",
isArray: false,
}
});
});
Here is usage of factory:
$scope.addCouponCode = function(coupon) {
couponApi.putCoupon(coupon);
};
When i debug my asp.net server i found my params null or 0. I have the same problem on restangular library.
I also try this way to write controller method
[HttpPut]
public IActionResult setCoupon(Coupon coupon)
{
try
{
return Ok(coupon);
}
catch (Exception)
{
return BadRequest("Wystąpił błąd");
}
}
My json which I try to send is this
{"id":1,"couponCode":"abc","discountPercent":10}
and my Echo method send me this:
{"id":0,"couponCode":null,"discountPercent":0}
Update
Apparently in asp.net core, method need to have attribute[FromBody]
[HttpPut]
public IActionResult setCoupon([FromBody] Coupon coupon)
{
try
{
return Ok(coupon);
}
catch (Exception)
{
return BadRequest(new {errorMessage = "Wystąpił błąd"});
}
}

As Aldo says in the comments. The answer is C# expects case-sensitive matching of the json data. So:
{"id":1,"couponCode":"abc","discountPercent":10}
needs to be:
{"id":1,"CouponCode":"abc","DiscountPercent":10}
You were getting a 0 for 'discountPercent' because that is the default value of the unmatched int, whereas null is the default for a unmatched string, hence Echo returns:
{"id":0,"couponCode":null,"discountPercent":0}

Related

Audit.Net: Process of saving in different dataprovider

Is it possible to establish some mechanism so that in case the insertion of the audit fails in the default dataprovider, oracle for example, another data provider is used, for example in a file?
Thanks for the help
There is not a data provider with automatic fallback capabilities, but you can implement a custom data provider.
Say you want the Sql data provider by default, and another data provider as fallback. Inherit from the SQL data provider (SqlDataProvider) and fallback to another DataProvider when a SqlException is thrown:
public class FallbackSqlDataProvider : SqlDataProvider
{
public AuditDataProvider FallbackProvider { get; set; }
public override object InsertEvent(AuditEvent auditEvent)
{
try
{
return base.InsertEvent(auditEvent);
}
catch (SqlException)
{
return FallbackProvider?.InsertEvent(auditEvent);
}
}
public override async Task<object> InsertEventAsync(AuditEvent auditEvent)
{
try
{
return await base.InsertEventAsync(auditEvent);
}
catch (SqlException)
{
return await FallbackProvider?.InsertEventAsync(auditEvent);
}
}
public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
{
try
{
base.ReplaceEvent(eventId, auditEvent);
}
catch (SqlException)
{
FallbackProvider?.ReplaceEvent(eventId, auditEvent);
}
}
public override async Task ReplaceEventAsync(object eventId, AuditEvent auditEvent)
{
try
{
await base.ReplaceEventAsync(eventId, auditEvent);
}
catch (SqlException)
{
await FallbackProvider?.ReplaceEventAsync(eventId, auditEvent);
}
}
}
Then you can set the fallback data provider on FallbackProvider property, for example like this:
var dp = new FallbackSqlDataProvider()
{
ConnectionString = "cnnstring",
TableName = "table",
IdColumnName = "id",
JsonColumnName = "data",
FallbackProvider = new Log4netDataProvider()
{
Logger = LogManager.GetLogger(typeof(Log4netDataProvider)),
LogMessageBuilder = (ev, id) => ev.ToJson()
}
};
Audit.Core.Configuration.Setup()
.UseCustomProvider(dp);
Also check this related issue.

Adding an extra JSON Object to the Web API Response

I need to attach an extra JSON object to my JSON response generated by the Web API Method. For example:
My code now:
[Route("api/getcomnts")]
public IHttpActionResult GetCommentsForActivity(string actid)
{
List<Comment> cmntList = CC.GetAllComments(actid);
return Ok(cmntList);
}
If the comments were successfully retrieved, I'd like to send:
"status":"success"
along with the comments list that it already sends as the JSON Array.
or
"status":"fail"
if the comments list is EMPTY. Is it possible to attach this extra JSON object named JSON to my already existing method?
This would make it very convenient for my client Android and iOS apps :)
EDIT
Or for a scenario such as this:
[HttpGet]
[Route("api/registeruser")]
public IHttpActionResult RegisterUser(string name, string email, string password)
{
int stat = opl.ConfirmSignup(name, email, password);
string status = "";
if (stat == 0)
{
status = "fail";
}
else
{
status = "success";
}
return Ok(status);
}
You can return anonymous object with Web API.
[Route("api/getcomnts")]
public IHttpActionResult GetCommentsForActivity(string actid)
{
List<Comment> cmntList = CC.GetAllComments(actid);
var success = cmntList.Count() > 0 ? "success" : "success";
return Ok(new { List = cmntList, success } );
}
**EDIT:**
[Route("api/getcomnts")]
public IHttpActionResult GetCommentsForActivity(string actid)
{
List<Comment> cmntList = CC.GetAllComments(actid);
string status = "";
if(cmntList.Count()!=0)
{
status = "success";
}
else
{
status = "fail";
}
return Ok(new { List = cmntList, status } );
}
You can try this
public HttpResponseMessage Get(string actid)
{
//sample..
if (value == true)
return Request.CreateResponse(HttpStatusCode.OK, getStatus("success"), JsonMediaTypeFormatter.DefaultMediaType);
else
return Request.CreateResponse(HttpStatusCode.OK, getStatus("failed"), JsonMediaTypeFormatter.DefaultMediaType);
}
private object getStatus(string s)
{
var status = new { Status = s };
return status;
}

How to return JSON for errors outside the WebApi pipeline?

I'm creating a public-facing API using WebApi 2.1, have a dedicated WebApi project (no MVC), have the API hosted in IIS 7.5 on its own server, and have the design goal of returning only JSON or empty content, and never returning HTML.
I am happily using ExceptionFilterAttribute to deal with exceptions arising within the WebApi pipeline, as follows:
public class GlobalExceptionHandler : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
// Log the exception to Elmah
Elmah.Error error = new Elmah.Error(context.Exception, HttpContext.Current);
error.Detail = ActionState.GetRequestParameters(context) + error.Detail;
Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(error);
if (context.Exception is NotImplementedException)
{
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.NotImplemented
, "The API method has not yet been implemented"
);
}
else
{
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError
, "A " + context.Exception.GetType().ToString() + " was thrown"
);
}
base.OnException(context);
}
}
The filter is properly added in App_Start:
config.Filters.Add(new SecureVideoApiGlobalExceptionHandler());
The problem comes when an error arises that is outside the WebApi pipeline. For example, a request comes with the URI of the web root, https://mysite.com/, and the response is a 403.11 with an HTML body stating "The Web server is configured to not list the contents of this directory." Another possibility is that there is an error in code called in the Application_Start method of global.asax, for example in the AutoMapper code, which is asserting that all the mappings are valid.
My question is: how can I make it such that when any error occurs on the API server, only a JSON error message is returned, and never an HTML error message?
I have tried
<modules runAllManagedModulesForAllRequests="true">
This allows me to handle any error in Application_Error() in global.asax, but I do not have the ability to access a Response object there to emit JSON.
You can do somthing like this Attribute
[AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class ExceptionActionFilter : ExceptionFilterAttribute
{
private static Logger _log ;//= LogManager.GetLogger("mysite");
public override void OnException(HttpActionExecutedContext contex)
{
if (_log == null)
_log = LogManager.GetCurrentClassLogger();
var ex = contex.Exception;
_log.Error(ex);
contex.Response = contex.Request.CreateResponse(HttpStatusCode.OK,
new
{
ErrorMessage = contex.Exception.Message,
RealStatusCode = (int)(ex is NotImplementedException || ex is ArgumentNullException ? HttpStatusCode.NoContent : HttpStatusCode.BadRequest),
ReturnUrl = CommonContext.ErrorUrl
},
new JsonMediaTypeFormatter());
base.OnException(contex);
}
}
And then put the Attribute on a class exc and on the clien side
or with filter
public class ExceptionLoggerFilter : System.Web.Http.Filters.IExceptionFilter
{
private static Logger _log;
public ExceptionLoggerFilter()
{
if (_log == null)
_log = LogManager.GetCurrentClassLogger();
}
public bool AllowMultiple { get { return true; } }
public System.Threading.Tasks.Task ExecuteExceptionFilterAsync(
System.Web.Http.Filters.HttpActionExecutedContext contex,
System.Threading.CancellationToken cancellationToken)
{
return System.Threading.Tasks.Task.Factory.StartNew(() =>
{
_log.Error(contex.Exception);
contex.Response = contex.Request.CreateResponse(HttpStatusCode.OK,
new { RealStatusCode = (int)HttpStatusCode.Forbidden, ReturnUrl = "#/error.html"},
contex.ActionContext.ControllerContext.Configuration.Formatters.JsonFormatter);
}, cancellationToken);
}
}
And then in Global.asax.cs
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
Database.SetInitializer<MySiteContext>(null);
}
and
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)//RouteCollection config)//
{
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Filters.Add(new ExceptionLoggerFilter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
}
}
And in the client side maybee somthing like
$.controller.ajax.promise = controller.ajax.promise = function ( obj )
{
var deferred = q.defer();
$.ajax( {
type: obj.type || "GET",
url: obj.url,
context: obj.context || null,
data: obj.data || null,
contentType: obj.contentType || "application/json; charset=utf-8",
dataType: obj.dataType || "json",
success: function ( res, textStatus, jqXHR )
{
if ( res.RealStatusCode )
{
switch ( res.RealStatusCode )
{
case 400://x error
res.ClientMessage = res.ErrorMessage;
deferred.reject(res);
break;
case 408://y errors
location.href = res.ReturnUrl;
return false;
case 403://ext
msgbox.alert( {
message: 'Ma belle msg',
title: "Error"
} );
deferred.reject();
location.href = res.ReturnUrl;
return false;
default:
deferred.reject();
location.href = res.ReturnUrl;
break;
}
}
deferred.resolve( res );
return true;
},
error: function ( jqXHR, textStatus, errorThrown )
{
deferred.reject( { msg: jqXHR.statusText, jqXHR: jqXHR, textStatus:textStatus, errorThrown:errorThrown } );
}
} );
return deferred.promise;
};
I hope this helps other googlers out there! (Like #Thomas C. G. de Vilhena said =)

Setting Up Multiple Actions

I was wondering if it was possible to have more than 1 action in the link. For example, If I wanted to have multiple links such as:
http://www.mywebsite.com/(CONTROLLER)/(ID)/(ACTION)
[http://]www.mywebsite.com/user/Micheal/EditMovies
[http://]www.mywebsite.com/user/Micheal/EditFavorites
Is there some sort of way to do this? If not, do I have to specify multiple id's in the function and then use a case to determine which page they are going to be sent to?
In my UserController.cs I have:
public ActionResult Index(string username)
{
if (username != null)
{
try
{
var userid = (Membership.GetUser(username, false).ProviderUserKey);
Users user = entity.User.Find(userid);
return View(user);
}
catch (Exception e)
{
}
}
return RedirectToAction("", "Home");
}
In my Routes I have:
routes.MapRoute(
name: "User",
url: "User/{username}",
defaults: new { controller = "User", action = "Index" }
);
What I'm trying to make it do is have additional functions for second actions so I can do something like:
User/{username}/{actionsAdditional}
And In my UserController I can put more actions which will leader to the second action actionsAdditional
public ActionResult Index(string username)
{
if (username != null)
{
try
{
var userid = (Membership.GetUser(username, false).ProviderUserKey);
Users user = entity.User.Find(userid);
return View(user);
}
catch (Exception e)
{
}
}
return RedirectToAction("", "Home");
}
public ActionResult EditFavorites()
{
//DoStuff
}
You could do this multiple ways, here's just one:
Set up a route to handle this:
routes.MapRoute("UserEditsThings",
"user/{id}/edit/{thingToEdit}",
new { controller = "UserController", action="Edit" },
new { thingToEdit = ValidThingsToEditConstraint() }
);
Then your action in the User Controller should look like this:
public ActionResult Edit(ThingToEdit thingToEdit) {
ThingToEditViewModel viewModel = new ThingToEditViewModel(thingToEdit);
return View(viewModel);
}
The RouteConstraint is what would take their input (the thingToEdit) and make sure it was valid (you could do this in a few places - like in a Custom ModelBinder):
public class ValidThingsToEditConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
//simplistic implementation simply to show what's possible.
return values['thingToEdit'] == "Favorites" || values['thingToEdit'] == "Movies";
}
}
Now, that way, you can have one method to Edit both Movies and Favorites, and you simply add a parameter to show what 'type' of thing they're editing.
If you wanted to keep your current route, you should be able to do the following:
routes.MapRoute("UserEditsThings",
"user/{id}/edit{thingToEdit}",
new { controller = "UserController", action="Edit" },
new { thingToEdit = ValidThingsToEditConstraint() }
);
I've been away from ASP.NET MVC for about 7 months, so this could be a little rusty. It has not been tested for syntax errors and bits of python may shine through. It should get you there, though.

MVC JSON actions returning bool

I had my ASP.NET MVC actions written like this:
//
// GET: /TaxStatements/CalculateTax/{prettyId}
public ActionResult CalculateTax(int prettyId)
{
if (prettyId == 0)
return Json(true, JsonRequestBehavior.AllowGet);
TaxStatement selected = _repository.Load(prettyId);
return Json(selected.calculateTax, JsonRequestBehavior.AllowGet); // calculateTax is of type bool
}
I had problems with this because when using it in jquery functions I had all sorts of error, mostly toLowerCase() function failing.
So I had to change the actions in a way that they return bool as string (calling ToString() on bool values), so that thay return true or false (in the qoutes) but I kinda don't like it.
How do others handle such a case?
I would use anonymous object (remember that JSON is a key/value pairs):
public ActionResult CalculateTax(int prettyId)
{
if (prettyId == 0)
{
return Json(
new { isCalculateTax = true },
JsonRequestBehavior.AllowGet
);
}
var selected = _repository.Load(prettyId);
return Json(
new { isCalculateTax = selected.calculateTax },
JsonRequestBehavior.AllowGet
);
}
And then:
success: function(result) {
if (result.isCalculateTax) {
...
}
}
Remark: if the selected.calculateTax property is boolean the .NET naming convention would be to call it IsCalculateTax.

Resources