Ernesto98
Ernesto98

Reputation: 68

CS7069: Reference to type 'Route' claims it is defined in System.Web but it could not be found

I'm trying to add OAuth 2.0 to my .NET Core 3.0 Web Api using this tutorial. The following is the content of WebApiConfig class.

        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services  
            // Configure Web API to use only bearer token authentication.  
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API routes  
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute( // HERE IS THE ERROR
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // WebAPI when dealing with JSON & JavaScript!  
            // Setup json serialization to serialize classes to camel (std. Json format)  
            var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            formatter.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();

            // Adding JSON type web api formatting.  
            config.Formatters.Clear();
            config.Formatters.Add(formatter);
        }

I'm guessing it has to do with dependencies so here is csproj:

  <ItemGroup>
    <PackageReference Include="EntityFramework" Version="6.4.0" />
    <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
    <PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.7" />
    <PackageReference Include="Microsoft.AspNet.WebApi.Owin" Version="5.2.7" />
    <PackageReference Include="Microsoft.AspNet.WebApi.WebHost" Version="5.2.7" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
    <PackageReference Include="Microsoft.Owin.Cors" Version="4.1.0" />
    <PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.1.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  </ItemGroup>

I've seen a similar question with an answer suggesting to use app.UseMvc to map routes, but he was using a different .NET version(2.0). Also I tried using app.UseMvc but it doesn't do anything because I use EndpointRouting.

    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.AddIdentity<AppUser, AppRole>(options =>
             {
                 options.User.RequireUniqueEmail = true;
             }).AddEntityFrameworkStores<AppDbContext>();


            services.AddControllers();

            //+adding database and service/repository
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseAuthentication();



            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

        }
    }

So I'm not really sure what to do. I tried closing Visual Studio and deleting every obj, bin and vs folder in the project (as weird as these sound, some people suggested it works) but with no succes.

Any suggestions please?

Upvotes: 1

Views: 5112

Answers (1)

Pierre Roozmon
Pierre Roozmon

Reputation: 11

This was a tricky problem that I also experienced, but when I searched for resolutions to this error message nobody seemed to have an elegant solution ... so I found one myself.

There are several issues at play here:

  1. As pointed out by the error message, the method MapHttpRoute is defined but can't be found

    • the reason for this is simple: the file that defines the HttpRouteCollectionExtensions methods does not include the actual bodies of the methods !
    • this file is installed when we add the Microsoft.AspNet.WebApi package via NugGet and it's referenced by including "using System.Web.Http;" at the top of the file
    • hence the solution that worked for me was simply to get the source code for these extensions and include them directly in my project
    • you can get this source code here:

https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Http/HttpRouteCollectionExtensions.cs

  • incidentally, at GitHub you can also find the source code for other methods that are similarly defined, but not found (i.e. they appear to be provided by Microsoft)

So, that solves the problem of implementing the MapHttpRoute method ... but this doesn't necessarily mean that your restful service call will work!

That's because the HttpDelete service that's being implemented here has calling parameters, which must be defined via the [Route] and [HttpDelete] attributes ... which leads us to step 2.

  1. We need to explicitly declare the calling parameters via the [Route] and [HttpDelete] attributes

    • this is actually pretty simple, but it's essential
    • it will ensure that the client goes to the right place when it tries to access the restful service
    • in my case, I have two calling parameters for the delete service, which are both strings
    • hence the route template in my call to config.Routes.MapHttpRoute(name, routeTemplate, defaults) looks like this:

    routeTemplate = "api/{controller}/{userID}/{userName}";

    • then in the C# controller (i.e. the file where the service that's being called occurs), I modified the attributes and the service method as follows:

    [Route("[controller]/{userID}/{userName}")]

    [HttpDelete("{userID}/{userName}")]

    public bool Delete(string userID, string userName) => this.DeleteUser(userID, userName);

    • next I define the method where the parameters are actually being used:

    [Route("[controller]")]

    public bool DeleteUser(string userIDParam, string userNameParam)

    {

    int? userID = 0; string userName = "";

    if (userIDParam != null) userID = Int16.Parse(userIDParam);

    if (userNameParam != null) userName = userNameParam;

    return DeleteSpecifiedUser(userID, userName);

    }

    • in the method DeleteSpecifiedUser(userID, userName), which is not shown here, I perform the actual delete operation in the database
    • although in my case I use ADO.Net, it could be done via Entity Frameworks (but that's beyond the scope of this discussion)
    • of course, I could have just sent userID as an integer, but this example was meant to illustrate doing something with the two calling parameters
    • since the original question above only had one calling parameter (i.e. int id), then that case is even simpler

Upvotes: 1

Related Questions