8

I'm trying to create my first Alchemy plugin and I keep getting a 404 error when calling a method on the controller. I'm using the JavaScript proxy that gets generated automatically, but the URL that it contacts returns a File Not Found error (status 404).

This is my controller:

[AlchemyRoutePrefix("AddUserController")]
public class AddUserController : AlchemyApiController
{
    [HttpPost]
    [Route("NewUser")]
    public string NewUser(string name, string description)
    {
        ...
    }
}

And this is the command that calls it:

Alchemy.command("${PluginName}", "AddUserByName", 
{
    execute: function()
    {
        ....
        Alchemy.Plugins["${PluginName}"].Api.AddUserController.newUser(username, fullName)
                .success(function(response) {
                    console.log("Added new user: ", response);
                })
                .error(function(response) {
                    console.log("Error: ", response);
                });
        ....
    }
});

It ends up requesting the URL '/Alchemy/Plugins/Add_User_By_Name/api/AddUserController/NewUser' which then returns the 404 status code. That URL seems correct, according to the documentation.

What I've tried so far:

  1. Changing the controller to use HttpGet instead of HttpPost.
  2. Removed the AlchemyRoutePrefix attribute. The URL that it tried to contact changed as expected, but still gave a 404.
  3. Double-checked that I'm using the right JavaScript method (verified using code completion from within the browser console).
  4. Looked at the Big Box o' Samples to find an example that uses a controller. It doesn't seem to have one.
  5. Adding the parameters as route variables (e.g. Route("NewUser/{name}/{description})"). Still got a 404 but I also noticed that backslashes were changed to forward-slashes. Update: It turns out that this does work if no parameters are empty and you don't use any backslashes. Unfortunately, I need to support both of those things.
  6. Changing the method arguments to a single POCO class (UserModel with 2 string properties). This results in a 500 error instead (suggesting that the routing perhaps does work) but there were no details available anywhere (in the error message or in the server logs). It just said "An error has occurred.".
  7. Added the [FromBody] attribute to the name and description arguments in the NewUser method. This resulted in the same generic error as #6.

Does anyone have any ideas on why the routing seems to fail?

  • Is Alchemy creating a post or a get? Is WebApi expecting NewUser/{name} maybe? – Rob Stevenson-Leggett Dec 1 '15 at 11:24
  • It is generating a post, but interestingly with 0 content in the body. I expected my parameters to be included in the message. I'll try adding the route variables and see if that helps. – Peter Kjaer Dec 1 '15 at 11:59
  • Adding route variables still gave me the 404 (and also doesn't seem to escape backslashes correctly, which is a problem when entering domain users :)) If I change the input parameters to a POCO, though, I start getting a 500 error instead. But there are no details about the error anywhere (not even the logs on the server). – Peter Kjaer Dec 1 '15 at 12:07
5

Your #6 was on the right track, you will want a POCO class to bind to. I believe your JavaScript call is where things went wrong though. I recreated your method, but with the following signature:

[HttpPost]
[Route("NewUser")]
public string NewUser(NewUserModel model)
{
    return "Test " + model.Name + " and " + model.Description;
}

And NewUserModel looks like:

public class NewUserModel
{
    public string Name { get; set; }
    public string Description { get; set; }
}

I have the JavaScript call matching yours (I just plugged in dummy data):

var username = "DOMAIN\\SOMEUSER",
    fullName = "This is just a test of the national emergency broadcast system. Had this been an actual emergency, you'd be dead.";

Alchemy.Plugins["${PluginName}"].Api.AddUserController.newUser(username, fullName)
...

That produced a 500 error just like yours (only my response body had details, there's a setting in WebAPI that disables the exceptions from being out, I wonder if that is disabled in yours?)

Error of 500.

The issue is with the JavaScript call... since this is a POST we'll want to ensure we're sending an object that matches the POCO's fields. So the model parameter of our action wasn't able to bind correctly, and model ends up being null. The [FromBody] can be used too if you didn't want a POCO, but you'd still have to pass the data as a JSON object. (I remember having some issues trying to get [FromBody] to work on a normal WebAPI project though... I didn't spend too much time with it though so was probably just doing something wrong.)

After changing the JavaScript to the following...

Alchemy.Plugins["${PluginName}"].Api.AddUserController.newUser({ name: username, description: fullName })
...

My execute method now works as expected and I can see the following logged to my console:

Successful console log!

Hope that helps!

  • That is what I tried in step #6. I just didn't mention that I updated the JavaScript to match but I did. The source code is on GitHub, by the way: github.com/pkjaer/alchemy-plugins/tree/master/AddUser – Peter Kjaer Dec 2 '15 at 15:19
  • Aha! I had made the POCO with just fields instead of properties with getters and setters. That was the missing link. Thanks :) – Peter Kjaer Dec 2 '15 at 15:24
2

You have to remove the word 'Controller' when calling the js proxy. This should work:

//...
Alchemy.Plugins["${PluginName}"].Api.AddUser.newUser(username, fullName)
            .success(function(response) {
//...
  • No luck. I believe I am calling the correct JavaScript method. Removing "Controller" from it just results in a JavaScript error ("Cannot read property 'newUser' of undefined"). – Peter Kjaer Dec 2 '15 at 9:11
1

To use backslashes you can use Regular Expressions in Route attribute as explained here.

I am assuming description could be blank and don't have backslash in it. Please try something like below:

[AlchemyRoutePrefix("AddUserController")]
public class AddUserController : AlchemyApiController
{
    [HttpGet]
    [Route("NewUser/{name:string:regex(\\w+\\\\w+)}/{description}")]
    public string NewUser(string name, string description = "")
    {
        ...
    }
}
  • That didn't work either. What's worse, it seems to have broken Alchemy :) It can no longer load the list of installed plugins. – Peter Kjaer Dec 2 '15 at 12:13
  • That's strange, I guess Alchemy breaks after you call the controller. You can do a hack though, replace the backslash with some special string while passing it as parameter in request and in controller just replace it back to backslash. It should work and continue you on the plugin until you get a proper solution. – Saurabh Gangwar Dec 2 '15 at 14:02
1

You have different cases in the functions -- newUser vs NewUser -- Have you tried it with them being the same?

  • Yeah, that's normal. The JavaScript proxy object gets created with camelCase even if the .NET method is in Pascal case. – Peter Kjaer Dec 2 '15 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.