protocol WebException Error when using Required attribute - asp.net-web-api

I have created ASP.NET Web API and calling the post method using json from client side and it's working fine :
public class ValueController : ApiController
{
public void Post([FromBody]model value)
{
}
}
public class model
{
public decimal value { get; set; }
}
string JSONString = "{\"value\":\"999.99\"}";
but when I use the [Required] attribute in the value property,
public class model
{
[Required]
public decimal value { get; set; }
}
it's start giving protocol Error when call the GetResponse() method..

Since decimal is a Value type and if we use the Required attribute to a value type, it will cause an error..
it will always have a value (the default of 0) if the incoming request does not provide the value.
Now I am using [DataMember(isRequired=true)] instead of [Required] attribute

Related

Asp.net core 2.2 web api FromForm of complex object contains only null values

Hi I am trying to build an endpoint for slack commands in asp.net core 2.2.
I have a data structure representing a commandrequest from slack like so:
public class SlackCommandDTO
{
[FromForm(Name = "token")]
public string Token { get; set; }
[FromForm(Name = "team_id")]
public string TeamId { get; set; }
[FromForm(Name = "team_domain")]
public string TeamDomain { get; set; }
[FromForm(Name = "channel_id")]
public string ChannelId { get; set; }
[FromForm(Name = "channel_name")]
public string ChannelName { get; set; }
[FromForm(Name = "user_id")]
public string UserId { get; set; }
[FromForm(Name = "user_name")]
public string UserName { get; set; }
[FromForm(Name = "command")]
public string Command { get; set; }
[FromForm(Name = "text")]
public string Text { get; set; }
[FromForm(Name = "response_url")]
public string ResponseUrl { get; set; }
[FromForm(Name = "trigger_id")]
public string TriggerId { get; set; }
}
My controller to receive data looks like this:
[Route("api/[controller]")]
[ApiController]
public class CustomerServiceController : ControllerBase
{
// POST api/customerservice
[HttpPost]
public void Post([FromForm] SlackCommandDTO command)
{
Console.Write(command.Token);
}
}
my startup.cs looks like this
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
I have tried setting the compatability settings in startup.cs to 2.1 and 2.2.
The result is always an instance of the object that contain null in all properties.
I have tried setting the decorator to [FromBody] (not that that is supposed to work) but in that case I get 415 unsupported media type (as it should).
I have tried sending the requests with content-type x-www-form-urlencoded and form-data as well as text/plain and application/json. the latter two return 415.
I have also tried sending the request through swagger with the same result and curl both using -d keyword and -F keyword for each pair of data.
If I am missing some information please let me know, I am drawing a blank here on how to solve it so please help.
The data I am receiving is from Slack according to this article about implementing slash commands in slack.
https://api.slack.com/slash-commands#responding_to_commands
I have solved my problem.
My issue was the fundamental misunderstanding that the parameters would be bound as a single object when using the FromForm attribute when actually I was supposed to parameterize each field as a string input in the post method like so:
[Route("api/[controller]")]
[ApiController]
public class CustomerServiceController : ControllerBase
{
// POST api/customerservice
[HttpPost]
public void Post([FromForm] string token,
[FromForm] string team_id,
[FromForm] string team_domain,
[FromForm] string channel_id,
[FromForm] string channel_name,
[FromForm] string user_id,
[FromForm] string user_name,
[FromForm] string command,
[FromForm] string text,
[FromForm] string response_url,
[FromForm] string trigger_id)
{
Console.Write(token);
}
}
[FromForm] is not for annotating properties on your model. It's for indicating how an action param will be bound. If you were accepting JSON, you could achieve this via [JsonProperty], but there's no way to change the property names for binding from form. They need to match, i.e. you'll either need to change your properties to stuff like Team_Id (with the underscore) or change your field names to stuff like teamId (without the underscore).

Web API Conditional Serialization of properties using attributes

How to include/exclude properties from a WEB API response based on a user's role using attributes? For example:
public class Employee
{
public string FullName { get; set;}
[DataMember(Role="SystemAdmin")] // included only for SystemAdmins
public string SSN { get; set; }
}
You can use conditional serialization depending on what serializer(s) you are using in web api.
If you are just returning JSON from Web API its simple - I use only the JSON serializer and this solution works for me. By default Web API uses JSON.Net for JSON serialization. You can add a ShouldSerialize method that returns a bool. In the should serialize you can test if the user IsInRole
public class Employee
{
public string Name { get; set; }
public string Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property for anyone other than Bob..
return (Name == "Bob");
}
}
More details
The [JsonIgnore] attribute is all or nothing when using the JSON.Net Web API serialization.
Other serializers require different approaches...
The XmlSerializer also supports this but you have to enable it
config.Formatters.XmlFormatter.UseXmlSerializer = true;
The datacontract serializer is the default.
If using this you would have to add logic into the properties and omit them if null.. This can be a problem if you use the class elsewhere. The [IgnoreDataMember] attribute is all or nothing.
[DataContract]
public class Person
{
private string firstName;
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public string FirstName
{
get
{
//Put here any condition for serializing
return string.IsNullOrWhiteSpace(firstName) ? null : firstName;
}
set
{
firstName = value;
}
}
}

Need example for web api post with multiple parameter

In c#, Need example for web api post with multiple parameter ,Below i have attached my sample code.Please look into the InsertLeave method.In my code without CompanyId parameters working fine. When i add the companyid not able to invoke.
namespace AeS.SaaSAPI_2116
{
[RoutePrefix("{CompanyId}/{SecurityKey}")]
public class LeaveController : ApiController
{
[HttpPost]
[Route("{APIName}/x")]
public string InsertLeave(List<LeaveRequest> objList, string CompanyId)
{
foreach (LeaveRequest LR in objList)
{
}
return "Sucess ";
}
}
}
public class LeaveRequest
{
[Required]
public string EMP_STAFFID { get; set; }
[Required]
public string LEAVE_TYPE { get; set; }
}
}
I think you can use send your parameter to the server by request body, You will have to create a single class that wrapping your all parameters.You can use model binding to resolve this kind of issue

MVC3 Optional parameters

This is a follow up to this:
What does MVC3 do with C# Optional Parameters?
I have an action with the following signature:
public ViewResult Show(int Id, PublishingErrorSummary pubErrors=null, String title=null)
On requesting server/show/1 pubErrors is not null, but title is null. How is it possible? These are just two objects but string somehow manages to become null. Where can I fix this?
Edit: class definition added
public class PublishingErrorSummary
{
public List<string> StepOneErrors { get; set; }
public List<string> StepTwoErrors { get; set; }
public List<string> StepThreeErrors { get; set; }
public List<string> StepFourErrors { get; set; }
}
PublishingErrorSummary is a complex object. The default model binder always initializes complex objects. It doesn't really make sense to set its default value to null. Same stands for the title parameter. Strings are reference types and their default value will be null anyway if no request parameter title is sent.

ASP.NET MVC 3 - Setting null field in DTO when binding to HTTP POST instead of failing

I have an HttpPost controller action that takes in a simple form DTO object.
[HttpPost]
public ViewResult Index(ResultQueryForm queryForm)
{
...
}
public class ResultQueryForm
{
public DateTime? TimestampStart { get; set; }
public DateTime? TimestampEnd { get; set; }
public string Name { get; set; }
}
The DTO object has nullable datetime fields used to create a range. The reason that it is set to nullable is because the form that is bound to the model is a query form, and the user doesn't have to enter a date value in the form.
The problem I'm running into is that if the user enters an invalid date, i would like the MVC default model binding to provide an error message. This happens flawlessly if I have a controller action that takes a DateTime? type as a argument, but since I'm passing a DTO that holds a DateTime? type the model binding appears to just set the DateTime? variable to null. This causes unexpected results.
Note:
[HttpPost]
public ViewResult Index(DateTime? startDate)
{
// If the user enters an invalid date, the controller action won't even be run because the MVC model binding will fail and return an error message to the user
}
Is there anyway to tell MVC model binding to "fail" if it can't bind the DateTime? value to the form DTO object, instead of just setting it to null? Is there a better way? Passing each individual form input to the controller is infeasible, due to the large amount of properties in the form/dto object (I've excluded many of them for easy reading).
You can validate your model in the controller action.
if(!Model.IsValid)
{
return View(); // ooops didn't work
}
else
{
return RedirectToAction("Index"); //horray
}
Of course you can put whatever you want in there, and return Json object if you want to display it on your page.
Also you need to add ValidateInput(true) up the top of your action method like this: [HttpPost, ValidateInput(true)]
I think you can create a custom ValidationAttribute for this.
[DateTimeFormat(ErrorMessage = "Invalid date format.")]
public DateTime? TimestampStart { get; set; }
[DateTimeFormat(ErrorMessage = "Invalid date format.")]
public DateTime? TimestampEnd { get; set; }
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class DateTimeFormatAttribute : ValidationAttribute
{
public override bool IsValid(object value) {
// allow null values
if (value == null) { return true; }
// when value is not null, try to convert to a DateTime
DateTime asDateTime;
if (DateTime.TryParse(value.ToString(), out asDateTime)) {
return true; // parsed to datetime successfully
}
return false; // value could not be parsed
}
}

Resources