Developer forum

Forum » Dynamicweb 10 » Issue building my own APiController

Issue building my own APiController

Jan Sangill
Reply

Hi,
I am trying to build my own API Controller in DW10 - since I am porting I am hitting the DW9 to DW10.
No matter what I try, I get an "HTTP ERROR 400".
I am hitting my method: "/api/test/get" - but always the error above. I am not sure why or how to fix it.

I have attached two simplified screenshots where it also gives the same error. Can anyone let me know what I am doing wrong?

 


Replies

 
Imar Spaanjaars Dynamicweb Employee
Imar Spaanjaars
Reply
This post has been marked as an answer

Are you testing it in a browser? If so, switch to a tool like Postman or Bruno. I think you're getting an error because the browser sends an Accept header (like: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7) by default. DynamicWeb then doesn't know what to return and returns a BadRequest instead.

In a tool like Bruno there's no default Accept header which will make your request succeed. You can also be explicit and send application/json as the Accept header.

Hope this helps,

Imar

Votes for this answer: 1
 
Jan Sangill
Reply

Hi Imar, I am almost a shamed now:) I did try with postman, but had another error that threw me off there. I just assumed it should also work in the browser too. To much time wated here:>
Thought it was code related.
Thank you.:)

 
Imar Spaanjaars Dynamicweb Employee
Imar Spaanjaars
Reply

:-) It threw me off a bit as well; most APIs I work with return JSON in the browser also.

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Hi

We have been looking into this.

This is due to a new feature for the /dwapi that has recently rolled out so that /dwapi can be used easier with tools like htmx.

What that feature does is that if you request "accept: text/html" together with "x-dw-template: MyModule/item.cshtml", the response object will be serialized to html using the passed template.

The problem is that if you "accept: text/html" but no x-dw-template, you get a bad request. We are now looking at to solve that so if x-dw-template is missing, it will serialize to json.

There is a workaround to disable the behavior by setting RespectBrowserAcceptHeader = False (which is default by MVC and Dynamicweb sets it to true)

using Dynamicweb.Host.Core;
using Microsoft.AspNetCore.Mvc;
 
public class PipelineExtensions : IPipeline
{
    public int Rank => 200;
    public void RegisterApplicationComponents(IApplicationBuilder app)
    {
        app.MapWhen(isMyApiPath, app =>
        {
            app.UseRouting();
 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        });
 
        static bool isMyApiPath(HttpContext context) => context.Request.Path.StartsWithSegments("/myapi", StringComparison.OrdinalIgnoreCase);
    }
 
    public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
    {
        services.Configure<MvcOptions>(options =>
        {
            options.RespectBrowserAcceptHeader = false;
        });
    }
 
    public void RunInitializers()
    {
    }
}
 
using Microsoft.AspNetCore.Mvc;
 
[ApiController]
[Route("myapi/test")]
public class MyApiController : ControllerBase
{
    public class GreetingResponse
    {
        public string? Message { get; set; }
    }
 
    [HttpGet, Route("hi")]
    public IActionResult Hi([FromQuery] string name = "World")
    {
        var response = new GreetingResponse
        {
            Message = $"Hello {name}!"
        };
        return Ok(response);
    }
}
 
Anders Ebdrup
Reply

Hello,

I am having problems with this as well in 10.20.6, where I get a 404 when running in the cloud, but it is working locally. This i my code:
 

public class NotificationApiPipeline : IPipeline
{
    public int Rank => 200;

    public void RegisterApplicationComponents(IApplicationBuilder app)
    {
        app.MapWhen(isMyApiPath, app =>
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        });

        static bool isMyApiPath(HttpContext context) => context.Request.Path.StartsWithSegments($"/{Config.ROUTE_PREFIX}", StringComparison.OrdinalIgnoreCase);
    }

    public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
    {
        services.Configure<MvcOptions>(options =>
        {
            options.RespectBrowserAcceptHeader = false;
        });

        // Register Favorites services
        services.AddScoped<IFavoriteProductRepository, FavoriteProductRepository>();
        services.AddScoped<IFavoriteSortOrderService, FavoriteSortOrderService>();
    }

    public void RunInitializers()
    {
        // Optional initialization logic here
    }
}

[ApiController]
[Route(Config.ROUTE_PREFIX)]
public class FavoritesController : ControllerBase
{

    /// <summary>
    /// Ping endpoint to verify API accessibility
    /// </summary>
    [HttpGet, Route("ping")]
    [PermissionFilter]
    public IActionResult Ping()
    {
        return Ok(new { 
            message = "Favorites API is reachable", 
            timestamp = DateTime.UtcNow 
        });
    }
}

Any ideas why this is not working in the DW Cloud?

Best regards, Anders

 
Kevin Steffer
Kevin Steffer
Reply

Hi Anders,

Where is your Config.ROUTE_PREFIX coming from?

 
Anders Ebdrup
Reply

Hi Kevin,

 

My constant is defined in this class:

 
Kevin Steffer
Kevin Steffer
Reply

Could it be because you have the [PermissionFilter] attribute on, and don't send a JWT token?

 
Anders Ebdrup
Reply

Hi Kevin,

 

Thanks for the suggestions - I have now tried with hardcoding the route and removing the PermissionFilter, but I still get a 404. Any other suggestions? :-)

So it seems like my pipeline is not registered.

 

Best regards, Anders

 
Kevin Steffer
Kevin Steffer
Reply

Try to add this line wihtin the RegisterServices
mvcBuilder.AddApplicationPart(typeof(FavoritesController).Assembly);

And use a different vairable name within your MapWhen()

public void RegisterApplicationComponents(IApplicationBuilder app)
    {
        app.MapWhen(isMyApiPath, myapp =>
        {
            myapp.UseRouting();
            myapp.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        });

 
Anders Ebdrup
Reply

Adding this line:

            mvcBuilder.AddApplicationPart(typeof(FavoritesController).Assembly);

Did the trick.

 

Thank you so much! :-)

 
Kristian Elmholt Kjær Dynamicweb Employee
Kristian Elmholt Kjær
Reply

Hi Anders,

We have an interface "IWebApi" which you can also implement. This is just a marker-interface, which we use to discover by.

See the snippet below of how we use it:

 

var assemblies = AddInManager.GetTypes<IWebApi>().Select(type => type.Assembly).Distinct().ToList();
foreach (var assembly in assemblies)
{
    mvcBuilder.AddApplicationPart(assembly);
}

 

You can of course keep calling the "AddApplicationPart" yourself, if that works. But it would be interesting if simply adding the "IWebApi" interface to your pipeline class would do the same trick :)

 

/Kristian

 
Anders Ebdrup
Reply

Dear Kristian,

 

Yes, adding the "IWebApi" also did the magic and I can remove the line suggested by Kevin - thanks to both of you guys for solving this issue!

 

Best regards, Anders

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

Hi Anders,

Sorry for being late to the party :)

I'd suggest keeping the application part registration and removing the IWebApi interface. There are some consequences that you may not want when implementing that interface. Since it's meant for the DynamicWeb Delivery API, it adds your endpoints -- at least the ones it can understand -- to the Swagger (/dwapi/docs) which can be a good thing if you know and want that. However, the endpoint doesn't work for testing there due to the expectation that the routes start with /dwapi. If this is not an issue for you, you can of course keep it as the endpoint otherwise works normally.

Generally, you shouldn't use IWebApi unless you specifically want to extend the delivery API documentation with your own endpoints.

- Jeppe

 
Anders Ebdrup
Reply

Hi Jeppe,

 

I am just glad that you made it! :-)

Thanks for your 5 cents - I will remove the IWebApi interface and reimplement the application part registration.

 

Thanks, Anders

 
Jeppe Eriksson Agger Dynamicweb Employee
Jeppe Eriksson Agger
Reply

I've updated the documentation to clarify what IWebApi actually does and give some pointers as to when to use it and when not to. It's being reviewed by the documentation team and will hopefully be published soon.

Also, it seems the issue with the endpoints not working for testing under Swagger UI has been fixed, at least for the ones that don't require authentication. That means that my assertion to the contrary is no longer true for all currently supported versions of DW10. I just wanted to make that clear.

- Jeppe

 

You must be logged in to post in the forum