-1

I'm writing a generic report web api in C# and I want to have an optional parameter because some reports require only a report id and primary id and sometimes I need report id, primary id and secondary id.

However currently this works: http://localhost:50505/api/report/4/9981/0

But this doesn't: http://localhost:50505/api/report/4/9981

I don't want to pass a zero because the parameter is not used for report of id 4.

This is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Description;
using IPD_Report.Api.Interfaces.Factories;
using IPD_Report.Dtos.RequestModels;
using IPD_Report.Dtos.ResponseModels;
using IPD_Report.SoapService.Attributes;
using IPD_Report.SoapService.Commands;
using IPD_Report.SoapService.Interfaces.Commands;

namespace IPD_Report.Api.Controllers
{
    /// <summary>
    /// This controller is used to generate reports based on report ID.
    /// </summary>
    [RoutePrefix("api/report")]
    public class ReportController : ApiController
    {
        private readonly IReportCommands _reportCommands;
        private readonly IDtoFactory _dtoFactory;

        public ReportController(IReportCommands reportCommands, IDtoFactory dtoFactory)
        {
            _reportCommands = reportCommands;
            _dtoFactory = dtoFactory;
        }

        /// <summary>
        /// Generic GET request for returning report.
        /// </summary>
        /// <param name="reportId"></param>
        /// <param name="primaryId"></param>
        /// <param name="secondaryId"></param>
        /// byte[]
        [Route("{reportId}/{primaryId}/{secondaryId}")]
        [ResponseType(typeof(byte[]))]
        [HttpGet]
        public IHttpActionResult Get(int reportId, int primaryId, int? secondaryId = 0)
        {
            var dto = _dtoFactory.GenerateModel(reportId, primaryId, secondaryId);

            var stuff = GetAttribute(reportId, dto);

            return Ok(stuff);
        }

        /// <summary>
        /// Returns a list of available methods as a string list containing the method ID, method name, and returned filetype
        /// </summary>
        /// <returns>List&lt;List&lt;string&gt;&gt;</returns>
        [Route("getReportList")]
        [ResponseType(typeof(IEnumerable<ReportTypeModel>))]
        [HttpGet]
        public IHttpActionResult GetReportList()
        {
            var methodInfo = typeof(ReportCommands).GetMethods()
                .Where(x => x.GetCustomAttributes(false).OfType<MethodId>().Any())
                .Select(x => x.GetCustomAttributesData().Select(y => y.ConstructorArguments)).ToList();


            var methodList = new List<ReportTypeModel>();
            for(var i =0;i<methodInfo.Count;i++)
            {
                var annotation = (methodInfo.ToList()[i]?.ToList().FirstOrDefault() ?? throw new InvalidOperationException()).ToList();

                methodList.Add(new ReportTypeModel
                {
                    Id = int.Parse(annotation[0].Value.ToString()),
                    Name = annotation[1].Value.ToString(),
                    Format = annotation[2].Value.ToString()

                });
            }

            return Ok(methodList);
        }

        private object GetAttribute(int id, BaseModel baseModel)
        {
            var methodInfo = typeof(ReportCommands).
                GetMethods()
                .Where(x => x.GetCustomAttributes(false).OfType<MethodId>().Any())
                .First(x => x.GetCustomAttributes(false).OfType<MethodId>().First().Id == id);


            return methodInfo.Invoke(_reportCommands, new object[] { baseModel });
        }
    }
}

I need some help advise for this:

public IHttpActionResult Get(int reportId, int primaryId, int? secondaryId = 0)

I've writing secondary ID as an optional parameter but if I try to call this url: http://localhost:50505/api/report/4/9981

I get a 404.

Any advice?

Nick

nick gowdy
  • 6,191
  • 25
  • 88
  • 157
  • 1
    Make it optional in the route template `[Route("{reportId}/{primaryId}/{secondaryId?}")]` – Nkosi Jan 03 '18 at 11:50
  • @Nkosi I didn't realise you could add a question mark to route parameter in the route attribute to denote it as optional. That worked. – nick gowdy Jan 03 '18 at 11:53
  • Reference [Attribute Routing in ASP.NET Web API 2](https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2) – Nkosi Jan 03 '18 at 11:54
  • More specifically [Optional URI Parameters and Default Values](https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#optional-uri-parameters-and-default-values) – Nkosi Jan 03 '18 at 11:55

1 Answers1

0

Just change the route parameter and make it nullable;

[Route("{reportId}/{primaryId}/{secondaryId?}")]
lucky
  • 12,734
  • 4
  • 24
  • 46