nnmmss
nnmmss

Reputation: 2992

passing optional parameter to GET method of ASP.NET API

I am learning ASP.NET API. in the controller I have this methof for GET Verb

 public HttpResponseMessage Get(string gender="All")
    {
        using (EmployeeDBEntities entities = new EmployeeDBEntities())
        {
            switch(gender.ToLower())
                {
                case "all":
                    return Request.CreateResponse(HttpStatusCode.OK, entities.Employees.ToList());
                    
                case "female":
                case "male":
                    return Request.CreateResponse(HttpStatusCode.OK,
                        entities.Employees.Where(x=>x.Gender==gender).ToList());
                default:
                    return Request.CreateErrorResponse(HttpStatusCode.NotFound, " value of gender is wrong");
            };
        }
    }

when I browse to http://localhost:61491/api/Employees/gender=female , I recieve the following error:

 <Error>
 <Message>The request is invalid.</Message>
 <MessageDetail>The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Net.Http.HttpResponseMessage Get(Int32)' in 'EmployeeService.Controllers.EmployeesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.</MessageDetail>
 </Error>

/**Edit This is the web.config file of API Service

   <?xml version="1.0" encoding="utf-8"?>
  
   <configuration>

    <connectionStrings>
    <add name="EmployeeDBEntities" connectionString="metadata=res://*/EmployeeDataModel.csdl|res://*/EmployeeDataModel.ssdl|res://*/EmployeeDataModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=localhost;initial catalog=EmployeeDB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
    </connectionStrings>

 <appSettings>
   <add key="webpages:Version" value="3.0.0.0" />
   <add key="webpages:Enabled" value="false" />
   <add key="ClientValidationEnabled" value="true" />
   <add key="UnobtrusiveJavaScriptEnabled" value="true" />
 </appSettings>
 <system.web>
      <compilation debug="true" targetFramework="4.5.2" />
      <httpRuntime targetFramework="4.5.2" />
       <httpModules>
         <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
      </httpModules>
   </system.web>
   <system.webServer>
   <handlers>
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="TRACEVerbHandler" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
       </handlers>
       <validation validateIntegratedModeConfiguration="false" />
       <modules>
       <remove name="ApplicationInsightsWebTracking" />
       <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
      </modules>
      </system.webServer>
     <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
    <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
    <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
  </dependentAssembly>
   </assemblyBinding>
   </runtime>
  <system.codedom>
<compilers>
  <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
  <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
   </configuration>

what could be the reason?

Upvotes: 0

Views: 10079

Answers (1)

CorrieJanse
CorrieJanse

Reputation: 2598

Your code is fine. It's your url that is wrong:

http://localhost:61491/api/Employees/gender=female

I will give a quick solution as to what it should be, then give more details I highly recommend you read if you are learning APIs. Your URL should be:

http://localhost:61491/api/Employees?gender=female

With C# APIs you can send data in 3 possible ways:

  • Query Parameter
  • Route Parameter
  • In the Request Body

Query Parameters

This is the default type. Meaning if you don't specify the method it will always assume your inputs are query parameters. Not only that, but query parameters are by default optional and nullable. If you put in a null for a query parameter it will be assigned the default value.

Query parameters can be identified in a URL by the ? mark. Any data after that are query parameters and can be assigned in any order. For example:

 MyUrl?Name=Jhon&Age=20&Sex=M

The & shows it's a split. The data above will map to the endpoint:

 public HttpResponseMessage Get(string name, string sex, int age)

The paremeters can be in any order. It will map correctly.

Route Paremeters

These are queries taken directly from route. Adapting your request it will look like this:

 /api/Employees/gender/female

And the endpoint itself will look as follow:

[HttpGet("gender/{sex}")]
public HttpResponseMessage Get([FromRoute] string sex) {...}

You can't by default use Route parameters, you will need to specify it is a route parameter by:

  • Putting the parameter in the route for the end point in brackets {}
  • Indicate with the [FromRoute] attribute that the api should use the route to get the attribute. By Default the route is required. You cannot use it as an optional parameter.

Body Parameters

This is simply you read the body of the request as a parameter. Good when you are posting or putting data Not going into too much detail but it will look as follow:

 public HttpResponseMessage Get([FromBody] Model data) {...}

It is important to note that the [FromBody] parameters aren't compatible with Get requests.


Request Types

One important thing you will need to know is the request types. There are many but by default it will always be a Get request.

Your code:

public HttpResponseMessage Get(...) {...}

Is not a get reqest because you wrote GET. You can name that request whatever you like such as:

public HttpResponseMessage ThisIsMyPostRequest(...) {...}

But you will still be able to retrieve data using a GET request. The Specify what type of requests it is, you can add a http attribute tag:

[HttpGet]
public HttpResponseMessage MyGetFuncName(){...}

[HttpPost]
public HttpResponseMessage MyPostFuncName(){...}

[HttpPut]
public HttpResponseMessage MyPutFuncName(){...}

[HttpDelete]
public HttpResponseMessage MyDeleteFuncName(){...}

[HttpOptions]
public HttpResponseMessage MyOptionsFuncName(){...}

[HttpHead]
public HttpResponseMessage MyHeadFuncName(){...}
...

It is really that easy. The different HttpRequest mostly work the same. But there are a few differences. The most common used are GET, POST, PUT and DELETE.

This becomes usefull when say you want to use the same route but different functions. For example GET and DELETE can be on the same route:

/api/Employees/Byname?name=Jhon

but depending on the request type will either return the user or delete it.


Custom Routes

The last important and powerfull thing you will need to know is Routes. If I am not mistaken at the top of your controller page you have the following attribute:

  [Route("[controller]")]

or

  [Route("api/[controller]")]

What that function does is it ands a prefix to all of the requests on the controller. Now you can actually add this to individual functions as well. Meaning if you have multiple GET functions on a single page, this will be essential to keep it working properly. Example:

     [Route("ByGenders")]
     public HttpResponseMessage Get(string gender="All")

      /api/Employees/ByGenders?gender=female

     ...

     [Route("Byname")]
     public HttpResponseMessage Get(string name)

     /api/Employees/Byname?name=Jhon

But it can actually be combined with the HttpRequest attributes. Meaning the above will be equivalent to:

     [HttpGet("ByGenders")]
     public HttpResponseMessage Get(string gender="All")

     [HttpGet("Byname")]
     public HttpResponseMessage Get(string name)

Ofcourse you can put all of the above together into a unique endpoint with a lot of details. Below is a small example:

    [HttpGet("GetWithParams/{requiredInput}")]
    public IActionResult GetWithParams(
        [FromRoute] string requiredInput,
        [FromQuery] string optionalInput)
    {
        return Ok($"Required Input: {requiredInput} | Optional Input: {optionalInput}");
    }

Or

    [HttpPost("UserId/{id}")]
    public IActionResult SubmitApplication(
        [FromRoute] int id,
        [FromBody] UserApplicationModel application)
    {

        return BadRequest();
    }

Upvotes: 4

Related Questions