C# - IF with more string variables and OR operator - c#

I would like to check if any variable is empty or null in other words I would like to check if all parameters are sent.
I have this code but it isn't working.
[HttpGet]
[Route("{regid}/{year}")]
public HttpResponseMessage Get(string regid, int? year, string un, string ps)
{
var isRegIDNumeric = int.TryParse(regid, out _);
if (!isRegIDNumeric || year == null || (String.IsNullOrEmpty(un)) || (String.IsNullOrEmpty(ps)))
{
...
}
If I call like this, one parameter is missing, IF isn't working
localhost/test/?regid=001&year=2019&un=ws.test
I got this message:
<Error>
<Message>No HTTP resource was found that matches the request URI
'localhost/test/?regid=001&year=2019&un=ws.test'.
</Message>
<MessageDetail>No action was found on the controller 'test' that matches
the request.
</MessageDetail>
</Error>
If I call with all parameters everything is OK, whole procedure is executed.
localhost/test/?regid=001&year=2019&un=ws.test&ps=test
When I had only 2 variables ...
[HttpGet]
[Route("{regid}/{year}")]
public HttpResponseMessage Get(string regid, int? year)
{
var isRegIDNumeric = int.TryParse(regid, out _);
if (!isRegIDNumeric || year == null)
{
...
}
and called like this, one parameter is missing ...
localhost/test/?regid=001
Everything was OK, IF was regularly executed.
How to make functional IF with all 4 variables if any of them is null or empty?

In your 4 parameters example both string parameters are mandatory. That's why you get an error.
If you want them to be optional, assign them default value null. Than if statement would work.
Or add another Get() method.

I believe that this another question have the answer of yours and corroborates what David Pivotar said.

Related

Route based attributes taking the incorrect method when data constraints are applied

I have the following API controller class.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace Core31Test.Controllers
{
[ApiController]
[Route("[controller]")]
public class DataController : ControllerBase
{
[HttpGet]
[Route("query/{idr:int?}")]
public string Get(int idr, [FromQuery] int id)
{
var idx = id == 0 ? idr : id;
return $"Value: {idx}";
}
[HttpGet]
[Route("query/{cityr}")]
public string GetByCity(string cityr, [FromQuery] string city)
{
var cityx = string.IsNullOrEmpty(city) ? cityr : city;
return cityx;
}
}
}
When I attempt to query by an id, both the route based path and the query string work. When I attempt to query by city, only the route based path works. The querystring path ends up taking the incorrect path.
For example:
http://localhost:51123/data/query/1
Result: value: 1
http://localhost:51123/data/query?id=1
Result: value: 1
http://localhost:51123/data/query/mycity
Result: mycity
http://localhost:51123/data/query?city=acity
Result: value: 0
In the last case, the incorrect route is being selected. Why is this happening and how can I fix it?
Edit 1
If I modify the route for the GetByCity method to be the one given below, the Get method is still selected. In this case, both methods have an optional route parameter and a querystring. Since the Get route specifies the input is an integer, I do not understand why the GetByCity method is going to that one. What I would like to know is how to make this work.
[Route("query/{cityr?}")]
In the last case, the incorrect route is being selected. Why is this happening and how can I fix it?
No, it's not. The correct and expected route is being selected. Let's take a closer look:
The route is query?city=acity
The available actions are:
query/ + optional int parameter (query/{idr:int?})
query/ + required string parameter ("query/{cityr}")
If you think about it, query/ does not satisfy query/ + required string parameter, as no value for cityr was given, so the correct route is the idr overload since that parameter is optional.

Multiple optional parameters web api attribute routing

I am new to attribute routing, and I am not sure if this is even possible.
I have an attribute route, which works fine like this:
[HttpGet]
[Route("GetIssuesByFlag/{flag:int=3}")]
public IEnumerable<IssueDto> GetIssuesByFlag(int flag)
Now I want to add some extra optional parameters to narrow down my search, so I want to add 2 extra optional parameters.
What I have tried:
[HttpGet]
[Route("GetIssuesByFlag/{flag:int=3?}/{categoryId:int?}/{tagIds?}")]
public IEnumerable<IssueDto> GetIssuesByFlag(int flag , int? categoryId = null, int?[] tagIds = null)
This works fine if my call is /api/controller/1/2, but fails with 404 when it comes to /api/controller/1.
How can I achieve this?
Edit 1: Nkosi's answer below worked, however an extra modification was needed.
[HttpGet]
[Route("GetIssuesByFlag/{flag:int=3}/{tagIds?}/{categoryId:int?}")]
public IEnumerable<IssueDto> GetIssuesByFlag(int flag , List<int> tagIds, int? categoryId = null )
The list or array must be second as it is automatically null if no value is provided and cant be marked as optional with = null.
{flag:int=3?} is the problem. it is either optional {flag:int?} with the default value in the action or {flag:int=3}.
[HttpGet]
Route("GetIssuesByFlag/{flag:int=3}/{categoryId:int?}/{tagIds?}")]
public IEnumerable<IssueDto> GetIssuesByFlag(int flag , int? categoryId = null, int?[] tagIds = null)
You currently have 3 optional parameters. when you have just the 1 value routing table wont know which optional parameter you are referring to, hence the 404
Use a query string.
[HttpGet]
[Route("GetIssuesByFlag/{flag:int=3?}")]
public IEnumerable<IssueDto> GetIssuesByFlag(int flag, List<int> tagIds, int? categoryId = null)
Url: /getissuesbyflag/1?tagIds=2,5,6&categoryId=56
You really should use query strings for optional parameters and path parameters if they are required.

am using this for Query String to filter the Movies I have in the database there's something wrong

string mid = Request.QueryString["ID"];
if ( db.Movies.Where(i=>i.ID==mid))
{
repMovie.DataSource = db.Movies.Find().ToString();
repMovie.DataBind();
}
Try adding .Any() to return a boolean in your if statement.
Any() returns true if any results are returned by an enumerable object.
if ( db.Movies.Where(i=>i.ID==mid).Any())
{
//...
}
First, db.Movies.Where(i=>i.ID==mid) isn't a boolean. Why are you testing if it's true or false?
Second, repMovie.DataSource = db.Movies.Find().ToString(); has no condition on it. How will that return the right thing? Why don't you do this instead:
(Assuming there's going to be a maximum of one match, ever):
var matchingMovie = db.Movies.FirstOrDefault(i => i.ID == movieId);
if (matchingMovie !== null) {
// you found it in one operation and can work with it now
}
I recommend against variable names like mid. What's that? Anyone else who sees it, or even yourself in a few months, will have no idea and will have to do extra scanning work to figure out its meaning. Use movieId instead.
And for that matter, why are you getting the movieId directly from the querystring yourself? Your MVC action methods should do this for you:
public ActionResult Index(int ID) {
// Lookie! you've automatically gotten the ID from the querystring, and
// you also know it's a non-null integer. If it were null or a non-integer,
// it wouldn't match this route and the client would get a 404, unless you
// had another route to handle those.
}
Alternate ways
int numMid = Convert.ToInt32(mid);
if ( db.Movies.Where(i=>i.ID==numMid).Count()>0)
{
//...
}
or
int numMid = Convert.ToInt32(mid);
if (db.Movies.Any(i=>i.ID==numMid))
{
//...
}

Optional Parameters in Web Api Attribute Routing

I want to handle POST of the following API-Call:
/v1/location/deviceid/appid
Additional Parameter are coming from the Post-Body.
This all works fine for me. Now I wnat to extend my code by allowing "deviceid" and/or "appid" and/or BodyData to be null:
/v1/location/deviceid
/v1/location/appid
/v1/location/
These 3 URLs should responded by the same route.
My first approach (BodyData required):
[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody] location_fromuser BodyData)
{
return repository.AddNewLocation(deviceid, appid, BodyData);
}
This does not work and returns a compile error:
"optional Parameters must be at the end"
Next try:
[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post([FromBody] location_fromuser BodyData, string deviceid = null, string appid = null)
Now my function AddNewLocation() get always an BodyData=null - even if the call send the Body.
Finally I set all 3 Parameter optional:
[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid = null, string appid = null, [FromBody location_fromuser BodyData = null)
DonĀ“t work:
Optional parameter BodyData is not supported by FormatterParameterBinding.
Why do I want a solution with optional Parameters? My Controller handles just the "adding of a new Location" via a POST.
I want to send on wrong data my own exceptions or error messages. Even if the call has missing values. In this case I want to be able to decide to throw an exception or Setting Defaults by my code.
For an incoming request like /v1/location/1234, as you can imagine it would be difficult for Web API to automatically figure out if the value of the segment corresponding to '1234' is related to appid and not to deviceid.
I think you should change your route template to be like
[Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")] and then parse the deiveOrAppid to figure out the type of id.
Also you need to make the segments in the route template itself optional otherwise the segments are considered as required. Note the ? character in this case.
For example:
[Route("v1/location/{deviceOrAppid?}", Name = "AddNewLocation")]
Another info: If you want use a Route Constraint, imagine that you want force that parameter has int datatype, then you need use this syntax:
[Route("v1/location/**{deviceOrAppid:int?}**", Name = "AddNewLocation")]
The ? character is put always before the last } character
For more information see: Optional URI Parameters and Default Values
An additional fact to complement #Kiran Chala's answer -
When we mark any parameter (appid) as optional in the action URI using ? character(for nullable value types) then we must provide default value to the parameter in the method signature as shown below:
[Route("v1/location/{deviceid}/{appid}", Name = "AddNewLocation")]
public location_fromuser Post(string deviceid, int? appid = null)
Ok, I fallen here with my internet research and I continue my way, because the accepted solution not working with dotnet core 3.1.
So here is my solution, following this doc
[HttpPost]
[Route("{name}")]
[Route("{name}/parent/{parentId}")]
public async Task<IActionResult> PostSomething(string name, Guid? parentId = null)
{
return Ok(await Task.FromResult(new List<string>()));
}
By this way many routes go to this single API function

How do I accept an array as an ASP.NET MVC controller action parameter?

I have an ASP.net MVC controller called Designs that has an action with the following signature:
public ActionResult Multiple(int[] ids)
However, when I try to navigate to this action using the url:
http://localhost:54119/Designs/Multiple?ids=24041,24117
The ids parameter is always null. Is there any way to get MVC to convert the ?ids= URL query parameter into an array for the action? I've seen talk of using an action filter but as far as I can tell that will only work for POSTs where the array is passed in the request data rather than in the URL itself.
The default model binder expects this url:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
in order to successfully bind to:
public ActionResult Multiple(int[] ids)
{
...
}
And if you want this to work with comma separated values you could write a custom model binder:
public class IntArrayModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value == null || string.IsNullOrEmpty(value.AttemptedValue))
{
return null;
}
return value
.AttemptedValue
.Split(',')
.Select(int.Parse)
.ToArray();
}
}
and then you could apply this model binder to a particular action argument:
public ActionResult Multiple([ModelBinder(typeof(IntArrayModelBinder))] int[] ids)
{
...
}
or apply it globally to all integer array parameters in your Application_Start in Global.asax:
ModelBinders.Binders.Add(typeof(int[]), new IntArrayModelBinder());
and now your controller action might look like this:
public ActionResult Multiple(int[] ids)
{
...
}
To extend on Darin Dimitrov's answer, something you can get away with is accepting a simple string in your URL parameter and converting it to an array yourself:
public ActionResult Multiple(string ids){
int[] idsArray = ids.Split(',').Select(int.Parse).ToArray();
/* ...process results... */
}
If you get a parse error while doing this (because someone passed you a malformed array), you can cause your exception handler to return a 400 Bad Request error instead of the default, more unfriendly 404 Not Found error that MVC returns when an endpoint is not found.
You can also use this URL format, and ASP.NET MVC will do everything for you. But, remember to apply URL encoding.
?param1[0]=3344&param1[1]=2222
I don't know where Groky's URL string was coming from, but I had the same problem with some javascript calling my controller/action. It would build up a URL of null, 1, or many "IDs" from a multiple-select list (which is unique to the solution I'm going to share).
I copy/pasted Darin's custom model binder and decorated my action/parameter, but it didn't work. I still got null valued int[] ids. Even in the "safe" case where I actually did have many IDs.
I ended up changing the javascript to produce an ASP.NET MVC friendly parameter array like
?ids=1&ids=2
I had to do some silly stuff, though
ids || [] #=> if null, get an empty array
[ids || []] #=> if a single item, wrap it in an array
[].concat.apply([], ...) #=> in case I wrapped an array, flatten it
So, the full block was
ids = [].concat.apply([], [ids || []])
id_parameter = 'ids=' + ids.join('&ids=')
It's messy, but it's the first time I had to hack like this in javascript.
.Net Core Answer
For those coming here in recent times, you can do this in .Net Core with:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
and:
public ActionResult Multiple([FromQuery] int[] ids)
{
...
}

Categories

Resources