Send a null value through AJAX - ASP.NET MVC - c#

I have a method that would take 3 parameters, but a dillema comes by. One of the parameters has to be located on the 3rd place, but sometimes the 1st and 2nd paramaters can take a null value. So in order for the first parameter to Not take in the 3rd value, but the 3rd parameter, I have to send in 1, or 2 null values.
To make this more clear, I have a method:
public ActionResult TurnoverPerItem(string startDate = null, string endDate = null,
int extra = 0) {
//...
}
and a JS/JQuery function that sends a request:
$.get(url, function (data) {
//....
}
but, now, the variable url can be one of the following (with all of the parameters (startDate, endDate and extra)):
path/to/action/15-09-2015/23-09-2015/0
or with only a startDate and extra
path/to/action/15-09-2015/0
or even just extra
path/to/action/0
but in the second example the parameter endDate would take in the value od 0 instead of extra, while extra would be null and in the third example, the parameter startDate would take in the extra value, while the others would be null.
My question would be, whether it is possible to send in a null value to the controller? ie. something like (one of the methods that I tried)
path/to/action/null/null/1
the web application is pretty robust, so there is a valid reason why the parameter extra has to take in 3rd place, but I really hope I won't have to go into details about that.
EDIT: the rout config:
routes.MapRoute(
name: "Default-Date",
url: "{controller}/{action}/{startDate}/{endDate}/{extra}",
defaults: new { controller = "reports", startDate = UrlParameter.Optional, endDate = UrlParameter.Optional, extra = UrlParameter.Optional }
);

I have a very simple one liner answer for your question.
Just try to create separate routing for each URL pattern.
Just try above solution, this work around will really solve your issue.

I'm not saying this is a perfect solution, but it did solve the problem for me. So from what I've got through research, you can't pass a null value through a GET request.
What I can do, though, is send a string or a number. startDate and endDate are both string types and are usually formatted like:
12-09-1983 (dd-MM-yyyy) | or | 12-09-1983-13-45 (dd-MM-yyyy-hh-mm)
While creating separate Routes is a good idea (and I'm thankful to Chetan Sharma and DPac for answering), it isn't an option due to some backend reasons that I won't go into.
What I did is simply fill in the blank spots. So if I only send in extra, it'll look like: link/to/ajax/null/null/0
If I only send in startDate: link/to/ajax/12-12-2012/null/0
Or if I'm sending a full value, normally through: link/to/ajax/12-12-2012/09-03/2015/0
so ajax can also send in a string = "null", the only step needed would be to test out the string and treat it as a normal null value.
public ActionResult TurnoverPerItem(string startDate = null, string endDate = null,
int extra = 0) {
startDate = (startDate != null && startDate.Equals("null")) ? null : startDate;
endDate = (endDate != null && endDate.Equals("null")) ? null : endDate;
//...
}
and afterwards the code will continue running normally.
More info : <Click here>

Related

ASP.NET Web API 2 getting null on Request after sending a non-integer value for an integer parameter

I have a project on Asp.net Web Api 2. I faced kind of interesting behavior of .net framework during testing one of the endpoint.
this is the simplified version of endpoint:
[Route("api/v1/bookings/documenttype/{clientId}/{companyCode}/{year}")]
[HttpGet]
[ResponseType(typeof(IEnumerable<UserVoucherType>))]
public IHttpActionResult GetUserDocumentTypes(int clientId, string companyCode,
int year, [FromUri] int? month = null)
{
//Do Some work
return Ok();
}
the problem here when I send a non integer value as value of 'month' parameter instead of getting bad request or something like this I get null during run time and this cause problem. For example one of the consumer of endpoint were sending '[1,2,3]' and getting all data for whole year instead of getting an error message.
For example for this request I am getting below values that I pointed on ss:
http://localhost:64652/api/v1/bookings/documenttype/1/0001/2019?month=[1,2,3]
Now my question how can I return a bad request for a case like this.
The month parameter is optional and I can't understand on endpoint did consumer send this parameter null on purpose or they send a not valid value for parameter.
Is there a setting for this ? I was thinking to write a custom paramater binder but isn't this over engineering ? I think the behavior of framework should be default returning bad request or am I missing something?
You could create a model for your optional params and apply the Range attribute to month.
(This is .NET5 so I'm using [FromQuery], but this should work for [FromUri] too)
public OkResult GetUserDocumentTypes(int clientId, string companyCode,
int year, [FromQuery] GetUserDocumentTypesOptionalInput optional)
{
//Do Some work
return Ok();
}
public class GetUserDocumentTypesOptionalInput
{
[Range(1, 12, ErrorMessage = "Invalid month")]
public int? Month {get; set; } // 1 or 0 based?
}
It will depend on your configuration if you have to manually check ModelState.IsValid to catch the error.
Also, with <TargetFramework>net5.0</TargetFramework> and [FromQuery] int? month = null the following error would be returned:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-65615fa2ff03a141b2cd80d7fc4f9dca-db45cf7466ae6f4f-00",
"errors": {
"month": [
"The value '[1,2,3]' is not valid."
]
}
}
You can change the Route attribute to include the data type constraint.
[Route("api/v1/bookings/documenttype/{clientId}/{companyCode}/{year:int{min:2000}/{month:int?}")]
I have added 2 constraints - data type for month and year, and min value for year.
For more details, refer to the official documentation from Microsoft.

API Get Method not invoking when passing an integer value with empty string in Angular 6

In my angular application, I am getting an error when invoking an API method from angular. I have to pass two parameters. First one an integer value and second one string value. it is optional.
Please see the below code snippet (Typescript)
let id:number = 5;
let value: string = "";
this.http.get<string[]>(this.appService.baseUrl + 'api/File/' + id + "/" + value)
In Controller:
[HttpGet("{id:int}/value")]
[ResponseCache(NoStore = true)]
public async Task<IActionResult> Get(int id, string value) { }
Here the Get method is not invoking since the value parameter is empty.
In your example, you're building this URL:
/api/File/5
However, your controller expects the following:
/api/File/5/value
If you want value here to be optional and to be placed into the value parameter (string value), you can adjust your HttpGet attribute, like this:
[HttpGet("{id:int}/{value?}")]
This makes value optional and is a placeholder for whatever gets passed in.
I only have experience with asp.net web api but as far as i am aware having the string value as empty causes the routing to change. Since you don't have a route that takes only an int, your request never reaches your api. What you need to do is overload your Get method and make one that only takes an int.
You entered the type of the variable shareType in the URL, and you are passing empty string for variable shareValue. Remove the :number after the variable shareType and assign a value to the variable shareValue.

Check for Date overlapping where end date might be null

I am currently trying to check for date overlapping where the end date might be null. With my current code, I am able display the conflict message if scenario 1 occurs. However, I am unable to display the conflict message for scenario 2. For example,
Scenario 1: End date not null
1/7/2018 to 1/9/2018
1/6/2018 to 1/9/2018
Result: there is conflict
Scenario 2: End date is null
1/7/2018 to null
1/6/2018 to 1/9/2018
Result: There is conflict
Here are my codes:
if ((A.StartDate < B.EndDate) &&
(B.StartDate < A.EndDate)) {
Console.WriteLine("Conflict");
}
Assuming that EndDate being null is essentially "no end date", so any date is always before this.
You can make use of the null object pattern, replacing null with a suitable always matching instance (and the easiest way to do that is to use the null-coalescing operator).
var ed = A.EndDate ?? DateTime.MaxValue;
if (theDate < ed) {
// We're in range
}

Unable to save Date to salesforce?

To my surprise , its not st.forward thing to do; saving Date to Salesforce .
I'm trying to update one field which is of type Date but it throws me some weird error .
Code :
var objSer = new JavaScriptSerializer();
string json = objSer .Serialize(new{
startdate = sfdcValue
});
MyUpdateMethod("objectName/" + id, json);
I tried to convert date to IS0 8601 standard (as suggested over SO)
1.) DateTime.UtcNow.ToString("s",System.Globalization.CultureInfo.InvariantCulture)
2.) DateTime.UtcNow.ToString("o")
Error Info :
{"message":"Cannot deserialize instance of double from VALUE_STRING
value 2017-05-26T10:31:40.5790708Z or request may be missing a
required field at [line:1, column:2]","errorCode":"JSON_PARSER_ERROR"}
You didn't elaborate on which method you are using to communicate between the server and client. I am using Javascript Remoting (#RemoteAction on the apex method) and I ran into this issue. For me, the date and datetime fields were being expected by Salesforce as date serials (your mileage may vary if using a different access method).
Given I was dealing with a dynamic list of fields, I ended up with a pair of marshall / unmarshall functions on the client that created client-only slave fields I removed before sending the data back to Salesforce (NB: the example below is javascript not c#). In your case, a marshall / unmarshall may take a different approach:
// Provide an client-only date field based on a date serial (SFDC input)
function createDateDerivedField(currentRecord, fieldName) {
Object.defineProperty(currentRecord, fieldName + '__ui', {
enumerable: true,
get: function () {
return currentRecord[fieldName] == null ? null : new Date(currentRecord[fieldName]);
},
set: function(newValue) {
// Update the original field
currentRecord[fieldName] = newValue == null ? null : (new Date(newValue)).getTime(); // Convert back to date serial
}
});
}

Attribute Routing with Constraint

If I have an ActionResult method like so:
public ActionResult AllSummaries(int? page, DateTime? yesterday)
Instead of the route being like:
http://serverName/projectName/controllerName/AllSummaries?yesterday=04/03/2017
I would like it to be:
http://serverName/projectName/controllerName/AllSummaries/04/03/2017
So on top of the ActionResult, how do I add a constraint to make the datetime only show the date in format MM/dd/yyyy?
[Route("allsummaries/yesterday/{yesterday:}")]
public ActionResult AllSummaries(int? page, DateTime? yesterday)
I do not need to validate against whether or not the date and day are 1 digit or 2 digit.. it will always be 2 digit.
Any help is appreciated.
UPDATE
Now getting 404 errors:
Here is the link that I am using:
http://serverName/projectName/controllerName/allsummaries/yesterday/3/4/2017
Here is my action:
[Route("controllerName/allsummaries/yesterday/{month?}/{day?}/{year?}")]
[ValidateInput(false)]
public ActionResult AllSummaries(int? page, int? day, int? month, int? year)
{
if (day.HasValue && month.HasValue && year.HasValue)
{
var yesterday = new DateTime(year.Value, month.Value, day.Value);
}
The route that I am generating is from a console application that is going to send out emails automatically via windows service, so I can't use #Url.Action...etc.. I am hardcoding the link like so:
mail.Body = mail.Body + "<div>" + "<p>" +
"http://serverName/projectName/controllerName/allsummaries/yesterday/" +
DateTime.Today.AddDays(-1).Day +
"/" + DateTime.Today.AddDays(-1).Month + "/" +
DateTime.Today.AddDays(-1).Year + "</p>" + "</div>";
The issue is the slashes in the date, which will be interpreted as path separators. The routing framework only parses params between path separators, unless you use the greedy param syntax, i.e. {*yesterday}. However, if you do that any further portions of the URL path will be consumed. For example, if a user changed the URL to something like allsummaries/yesterday/04/03/2017/foo, then 04/03/2017/foo would be passed in as yesterday and your action explodes.
You have two options.
You can use a different date format, like ISO: yyyy-MM-dd, which would make your URL /allsummaries/yesteday/2017-04-03, and you could capture the date portion with a single param: {yesterday}.
[Route("allsummaries/yesterday/{yesterday}")]
You can break up the date components and then recompose them into a DateTime in the action:
[Route("allsummaries/yesterday/{month?}/{day?}/{year?}")]
Then, in your action:
public ActionResult AllSummaries(int? page, int? month, int? day, int? year)
{
var yesterday = DateTime.Today.AddDays(-1); // default
if (month.HasValue && day.HasValue && year.HasValue)
{
yesterday = new DateTime(year.Value, month.Value, day.Value);
}
EDIT
I didn't want to confuse the main issue, but if you choose to follow the second option, breaking up the date into components, there is an issue you'll need to be aware of. If you have an explicit URL like /allsummaries/yesterday/04/03/2017, the modelbinder will be able to parse the "04" and "03" into ints. However, if you try to create the URL, using something like Url.Action, Url.RouteUrl, etc., you will need to feed the params values like "04", rather than an int, or you'll end up with URLs like /allsummaries/yesterday/4/3/2017. You could do that via something like:
#Url.Action("AllSummaries", new
{
month = date.ToString("MM"),
day = date.ToString("dd"),
year = date.Year
})
In other words, you would need to use ToString to get the two digit value, rather than date.Month or date.Day directly.
You should also probably protect the URL a little from tampering, by adding a regex contraint to these params:
[Route("allsummaries/yesterday/{month?:regex([0-9]{2})}/{day?:regex([0-9]{2})}/{year?:regex([0-9]{4}}")]
You can parse day, month and year alone,
and then create the date.
Your code will be like this:
[Route("allsummaries/yesterday/{day}/{month}/{year}")]
public ActionResult AllSummaries(int? page, int day, int month, int year)
{
var yesterday = new Date(day, month, year);
}
[Route("allsummaries/yesterday")]
public ActionResult AllSumaries(int? page)
{
}

Categories