Base solution for your next web application
Open Closed

How to create two service methods with the same route for different HTTP Methods #9359


User avatar
0
ebenzle created

ASP.Net Core MVC/jQuery

In our application service we need to be able to handle an API call to the same route, but process it differently based on the HTTP method. This call is coming from an external application and we are unable to change how the call is made, only how we handle it within our application.

the route: App/ActivityOnlineLesson/activities/state

[HttpPut] - should save the passed value
[HttpGet] - will return a saved value

Our current code returns an error: Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

How do we have the same route process the Get and Put methods separately?


Our AppService:

        /* Saves the state for xAPI activities */        [HttpPut]
        [Route("/App/ActivityOnlineLesson/activities/state")]
        public async Task<ActivityOnlineLessonXapiResumeOutput> State(ActivityOnlineLessonXapiResumeInput input)
        {
            // processes the xAPI state request
            var ActivityOnlineLessonXapiResumeOutput = new ActivityOnlineLessonXapiResumeOutput();
            ActivityOnlineLessonXapiResumeOutput.Success = true;

            return ActivityOnlineLessonXapiResumeOutput;
        }

        /* Gets the initial state for xAPI activities */
        [HttpGet]
        [Route("/App/ActivityOnlineLesson/activities/state")]
        public async Task<string> State()
        {
            return "24146070ji1001112a0101201112C10v_player_6BypgqIVLDo_6MymGJoBbaC1^1^0000";
        }

I thought that the [HttpGet] and [HttpPut] would determine which to use depending on the HTTP Method.


Our Interface looks like this:

    public interface IActivityOnlineLessonAppService : IActivityBaseAppService<ActivityOnlineLessonDto>, IApplicationService
    {
        Task<ActivityOnlineLessonXapiResumeOutput> State(ActivityOnlineLessonXapiResumeInput input);

        Task<string> State();
    }

3 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    hi

    What is your zero version? net core 2.x or 3.x?

    Can you provide the full code to reproduce the problem?

  • User Avatar
    0
    ebenzle created

    Hi. It's net core 3.x

    Here is ActivityOnlineLessonAppService.cs

    using System.Threading.Tasks;
    using Abp.Domain.Repositories;
    using NextGenLms.Courses.Dtos;
    using Abp.Application.Services.Dto;
    using Microsoft.EntityFrameworkCore;
    using NextGenLms.Storage;
    using Abp.UI;
    using System;
    using NextGenLms.ScormCloud;
    using NextGenLms.Grading.Dtos;
    using NextGenLms.Grading;
    using System.IO;
    using Abp.Authorization;
    using NextGenLms.Authorization;
    using Microsoft.Extensions.Configuration;
    using Microsoft.AspNetCore.Hosting;
    using NextGenLms.Configuration;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using System.Web;
    
    namespace NextGenLms.Courses
    {
        [AbpAuthorize]
        public class ActivityOnlineLessonAppService : NextGenLmsAppServiceBase, IActivityOnlineLessonAppService
        {
            private readonly ITempFileCacheManager _tempFileCacheManager;
            private readonly IRepository<ActivityOnlineLesson> _activityOnlineLessonRepository;
            private readonly IRepository<ActivityOnlineLessonUserResult> _activityOnlineLessonUserResultRepository;
            private readonly ICourseActivityAppService _courseActivityAppService;
            private readonly IGradesAppService _gradeAppService;
            private readonly IScormCloudManager _scormCloudManager;
            private readonly IConfigurationRoot _appConfiguration;
            private readonly IAppFolders _appFolders;
    
            public ActivityOnlineLessonAppService(
                ITempFileCacheManager tempFileCacheManager,
                IRepository<ActivityOnlineLesson> activityOnlineLessonRepository,
                IRepository<ActivityOnlineLessonUserResult> activityOnlineLessonUserResultRepository,
                ICourseActivityAppService courseActivityAppService,
                IGradesAppService gradeAppService,
                IScormCloudManager scormCloudManager,
                IWebHostEnvironment env,
                IAppFolders appFolders)
            {
                _tempFileCacheManager = tempFileCacheManager;
                _activityOnlineLessonRepository = activityOnlineLessonRepository;
                _activityOnlineLessonUserResultRepository = activityOnlineLessonUserResultRepository;
                _courseActivityAppService = courseActivityAppService;
                _gradeAppService = gradeAppService;
                _scormCloudManager = scormCloudManager;
                _appConfiguration = env.GetAppConfiguration();
                _appFolders = appFolders;
            }
    
    
            /* Saves the state for xAPI activities */
            [HttpPut]
            [Route("/App/ActivityOnlineLesson/activities/state")]
            public async Task<ActivityOnlineLessonXapiResumeOutput> State(ActivityOnlineLessonXapiResumeInput input)
            {
                // processes the xAPI state request
                var ActivityOnlineLessonXapiResumeOutput = new ActivityOnlineLessonXapiResumeOutput();
    
                if (input.Value == "123") {
                    ActivityOnlineLessonXapiResumeOutput.Success = true;
                } else
                {
                    ActivityOnlineLessonXapiResumeOutput.Success = false;
                }
    
                return ActivityOnlineLessonXapiResumeOutput;
            }
    
            /* Gets the initial state for xAPI activities */
            [HttpGet]
            [Route("/App/ActivityOnlineLesson/activities/state")]
            public async Task<string> State()
            {
    
                return "24146070ji1001112a0101201112C10v_player_6BypgqIVLDo_6MymGJoBbaC1^1^0000";
            }
    
        }
    }
    

    And the interface:

    using Abp.Application.Services;
    using Abp.Application.Services.Dto;
    using NextGenLms.Courses.Dtos;
    using NextGenLms.Courses.Dtos.Activities;
    using NextGenLms.Courses.Dtos.Quiz;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace NextGenLms.Courses
    {
        public interface IActivityOnlineLessonAppService : IActivityBaseAppService<ActivityOnlineLessonDto>, IApplicationService
        {
    
            Task<string> State();
        }
    }
    
    

    This is the error when trying to call the service at the URL:

    Request URL: https://localhost:44312/App/ActivityOnlineLesson/activities/state?stateId=resume&activityId=68&agent=%7B%22objectType%22%3A%22Agent%22%2C%22mbox%22%3A%22mailto%3A%20admin%40defaulttenant.com%22%2C%22name%22%3A%22admin%20admin%22%7D&course_id=7&base_url=https%3A%2F%2Flocalhost%3A44312%2F

    Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches: 
    
    NextGenLms.Web.Areas.App.Controllers.ActivityOnlineLessonController.state (NextGenLms.Web.Mvc)
    NextGenLms.Courses.ActivityOnlineLessonAppService.State (NextGenLms.Application)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ProcessFinalCandidates(HttpContext httpContext, CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.Select(HttpContext httpContext, CandidateState[] candidateState)
       at Microsoft.AspNetCore.Routing.Matching.DfaMatcher.MatchAsync(HttpContext httpContext)
       at Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher.MatchAsync(HttpContext httpContext)
       at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
       at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
    
    HEADERS
    =======
    Accept: */*
    Accept-Encoding: gzip, deflate, br
    Accept-Language: en-US,en;q=0.9
    Authorization: null
    Connection: keep-alive
    Cookie: .AspNetCore.Antiforgery.LvCbYuYZ0jQ=CfDJ8MZ5iXL2Cq9JmHEp0VtZuygW3VNV9s4pgXA7uVXZcJmrDDW_Ki_4MJAbWrcHA_8izlDoWJtLV6EjAGbv7qowvvdTwtoo7Gtxpb3GZVKJaSX6mrQ9JbuTD7hA7rA8BjzWqh9BpYZ3uPj3rsxVhVjE7XU; idsrv.session=HI_7vLf0gvPNxgCYcQM6mw; googtrans=/en/en; XSRF-TOKEN=CfDJ8MZ5iXL2Cq9JmHEp0VtZuyg0LT35YHGojvJWUMTda0XGMmm-WY7GwoiabCuR0MK-4_85-5yta9-xA62uFrgLDVphnH3tRAtGqxUid-KzH4YDlPZ-Fc9eQgFaFnMfaWgKELUkF-tjMHd_qRQTMgb8OvNKcjjSmvmxRvEB2iisN029C-_fl_dS9pCPgZZgqYAuUQ; .AspNetCore.Identity.Application=CfDJ8MZ5iXL2Cq9JmHEp0VtZuyjiKQwKR2Y7qQ1RgaiNDQmoVvpimfIBPPpJcd_6PCijFwK5qbeOVLOkKP0dyhZcGuOq9Doa_Wz2U87mqnMB23zZQi88JyyOrIBTE6q2C-QewbT57SzX2Tn_8UZkzupWNZ8Lh9aTtw_AQY9sU39-uyEWFOjWwOd3qsobIlbzapItCUJAb6DdVFzAvuuWZlucKXIeNtBJ7p6lJc6f8Z60QGLWPmvTzH2NVhybsTGeqWny4K7jA_IHxFUSX9X1DE7JMWJOY0AB1hqWPtcCe6bAAx9VMRAra_xyNCfJxoGjYkwC1zC4EjAuZHmlSdxcKajNeRay-hyMD96PvJYIcHiipocEmJdNJR8FjC7w2ha6TvXxD0OeTOCG6N0o8TX7yL6-qNt-__s9c4wkhjIlHYIHDFhI-LGaY6yD6qLg1SqUrSEjxlHAg1iPtgXiJmgLgzHl3w0HuI7PNuFra_KQDzr4lYgQWANtVUpofmbVa6YmnhGlOHc-1ACIHD1mg28TND4YDAjxpBmZ860Kgwob_7tpf1yvVqnzoMbluiEhb9ci3MDcV0988b_NyXdp5RxGD-7ZdmvHbgbcODit0Xrnn0hbG41FekWbFFq0ONJTABu_khvM_ALnaUd1zx20dFt12124BkGppl60eJOYq_aGL9YJgWsbch_UWL8O6d7SpHKaLhLtZOkB1ZeW_kSo1v5TGF6x7srtInRJYgO4Vb0U_RIJlU8m1BijBBq6Z2niiBFkT30CqB_meLgQnIXM_afSy9bUg-fclsUnm12rFNlRBZkjAvXZAXNBWXlHvY14cJ9TsKTQ2j7tgppTQEj_RH4zr3BNORfQoSwoxeEkGelIQ3xEaYEdkp8p6bQ9tb3p-LwTZzchN04JdAhn9P_MvNEun8yaN4Ld2fh6zv5GeAoCm8ij9WpDYXe2BuvtO_zU8YRyQJe9y94HHVo-JZvdWzC8UM4u3tg
    Host: localhost:44312
    Referer: https://localhost:44312/ActivityFiles/OnlineLessons/99df21dd-4238-432d-8eb0-6dd2722dc621/story.html?endpoint=https://localhost:44312/App/ActivityOnlineLesson/&course_id=7&actor=%7B%22name%22%3A%20%5B%22admin%20admin%22%5D%2C%20%22mbox%22%3A%20%5B%22mailto%3A%20admin%40defaulttenant.com%22%5D%7D&activity_id=68&base_url=https://localhost:44312/
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
    X-Experience-API-Version: 1.0.1
    DNT: 1
    Sec-Fetch-Site: same-origin
    Sec-Fetch-Mode: cors
    Sec-Fetch-Dest: empty
    

    I believe those are all the related code files.

    Thanks.

  • User Avatar
    0
    ebenzle created

    Hi, I figured this out. Code as posted was correct. I had another route called "State" in a different area that was causing the conflict.