The API Call
I am making a REST API call with the following message body:
{"Method":{"Token":"0","Value":"0"}}
400 Response
I am getting a 400 Bad Request response from the api with the following body:
{"Message":"The request is invalid.","ModelState":{"request.Method.Token":["Could not create an instance of type Namespace.ActionMethod. Type is an interface or abstract class and cannot be instantiated. Path 'ActionMethod.Token'."]}}
Code Information
The method which is receiving the api call looks like this:
public MethodResponse MakeMethodCall([Required] [FromBody] MethodRequest request)
MethodRequest has a Method property which is an abstract type.
public class MethodRequest
{
public ActionMethod Method { get; set; }
}
public abstract class ActionMethod
{
public string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
}
Question
How can I call the REST API and have it recognize that the type of Method is FirstMethod, instead of it trying to instantiate the abstract type ActionMethod?
Note that I will need to have more implementations of ActionMethod in the future (ie. SecondMethod), so the solution will need to include an extensible ActionMethod (interface would also be fine).
EDIT
It would also be reasonable to include an enum to identify which implementation of ActionMethod was being targeted by the API call.
I'm currently using a solution which has an ActionMethodType enum and both FirstMethod and SecondMethod fields. I'm checking these fields based on the value of ActionMethodType. This works, but I would like to have a single [Required] field into which I could pass any implementation of ActionMethod.
Can't be done. How would the framework know to instantiate FirstMethod for this parameter? What if you had another subclass of ActionMethod that also had a Value property? Now it's even more ambiguous for the framework to figure out on it's own. You could do a bunch of work, creating a custom formatter (http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx) but ultimately it would be easier to just have a single class that includes all possible properties a client could send OR have separate API endpoints for the client to call using different concrete types as the parameter.
If I understand you correctly, you could implement this with a custom model binder and a factory pattern.
public class MethodRequestBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
//use the request object to make a call to your factory for the
//appropriate ActionMethod subtype you want to create, or however
//else you see fit.
var curActionMethod = MyFactory.Get(request.QueryString);
var boundObj = new MethodRequest()
{
Method = curActionMethod
}
return boundObj;
}
}
register your model binder in app_start:
ModelBinders.Binders.Add(typeof(MethodRequest), new MethodRequestBinder());
now, just decorate your controller action method:
public ActionResult Index([ModelBinder(typeof(MethodRequestBinder))] MethodRequest request)
{
//etc..
}
I used this as a starting point: http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder
Remove the abstract keyword from your ActionMethod, or mark the Token property abstract and override it in the inherited classes:
public abstract class ActionMethod
{
public abstract string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
public override string Token
{
get;
set;
}
}
Related
With .net core and newton JSON. Can I have an endpoint using generics? How should I request it to pass the generic type?
public async Task<JsonResult> SaveSetting<T>([FromBody] Filter<T> model)
{}
public class Filter<T>
{
public string GUID { get; set; }
public string Name { get; set; }
public FilterType FilterType { get; set; }
public T FilterRequestModel { get; set; }
}
It's unlikely that there's an easy way to make this work, if there's a way at all. More importantly, it would be highly inadvisable. Presumably you'd need to figure out what type T should be based on input from the UI, and then you'd be binding values to that type with JSON bindings. That means you'd be allowing the caller (who you cannot trust) to make your code instantiate and set properties on a C# type of their choosing, which represents a security vulnerability.
There's a good chance you can make this endpoint do what you want without generics: just use a JObject as the FilterRequestModel object. If you really do rely on actual C# types to accomplish what you want, you're still better off making your controller action use a JObject, and using some custom logic to translate it based on some user input, after checking that the type specified is something you want callers to be able to instantiate. Once you've created an object of a custom type, you can cast it to dynamic before passing it into a generic helper method, and the generic type will automatically be resolved at runtime to the type of the object you created.
public async Task<JsonResult> SaveSetting([FromBody] Filter<JObject> model)
{
Type filterRequestModelType = this.DetermineSafeModelType(model);
object typedFilterRequestModel = model.FilterRequestModel.ToObject(filterRequestModelType);
return SaveSettingHelper(model, (dynamic) typedFilterRequestModel);
}
public async Task<JsonResult> SaveSetting<T>([FromBody] Filter<JObject> model, T filterRequestModel) {...}
.NET CLI
dotnet new web --name "GenericApiExample"
cd GenericApiExample
dotnet add package SingleApi
Program.cs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// map generics endpoint
app.MapSingleApi("sapi",
// add your generic request handler
// for example, return the received data (already typed object)
x => Task.FromResult(x.Data),
// add assemblies for resolving received data types
typeof(List<>).Assembly, typeof(int).Assembly);
app.Run();
Example request, equivalent of Dictionary<string,int?[]>
POST /sapi/Dictionary(String-Array(Nullable(Int32)))
{"key1":[555,null,777]}
GitHub repository with examples
I'm trying to use a Generic Controller in my Web API. My goal, which I am currently failing at, is to pass in an object from my front end that will have say a typeId. Based on this typeId I was going to use a factory to inject the correct class implementation of a generic interface. I believe my Factory, Interface and Service is correct, but for some reason when I add a Generic to the API I am getting a 404. It works without a generic and just a test method. I am using autofac for my IoC registration.
API Controller:
public class ListItemsController<T> : ApiControllerBase
{
private readonly IListItemsService<T> _service;
public ListItemsController(int listItemTypeId)
{
_service = ListItemsFactory<T>.InitializeService(listItemTypeId);
}
[HttpGet]
[Route("{listItemTypeId: int}")]
public IEnumerable<T> GetAll()
{
return _service.GetAll();
}
[HttpGet]
[Route("test")]
public IHttpActionResult Test()
{
return Ok();
}
}
Factory:
public class ListItemsFactory<T>
{
public ListItemsFactory(IPrimaryContext context) : base()
{
}
public static IListItemsService<T> InitializeService(int listItemType)
{
switch (listItemType)
{
case 1: return (IListItemsService<T>)
new FloorTypeService(new PrimaryContext());
default: return null;
}
}
}
Interface:
public interface IListItemsService<T>
{
IEnumerable<T> GetAll();
void Save(T obj);
T GetById(int id);
void Delete(int id);
}
Error:
No HTTP resource was found that matches the request URI 'http://localhost:9000/api/v1/listitems/test'. No type was found that matches the controller named 'listitems'.
I'm not sure what piece I'm missing here. I'm using routing attributes but here is my API config:
private static void SetupRoutes(HttpConfiguration config)
{
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
config.Routes.MapHttpRoute("DefaultApi", "api/v{version}/{controller}/{id}",
new { id = RouteParameter.Optional });
}
Instead of resolving the type and trying to map to the right Controller, you also can create a Controller for each Type, which inherits from your GenericController. Then you don't have to copy the Code, but have a Controller for each Type, where you can route to by RouteAttribute.:
public class ListItemsController<T> : ApiControllerBase
{
//Properties/Fields should be protected to can be accessed from InstanceController.
protected readonly IListItemsService<T> _service;
// I think listItemTypeId is not necessary, if generic-type T is used?
public ListItemsController()
{
_service = ListItemsFactory<T>.InitializeService();
}
[HttpGet] // No need for RouteAttribute, because it will be in InstanceController.
public IEnumerable<T> GetAll()
{
return _service.GetAll();
}
[HttpGet]
[Route("test")] // This can rest here, because you want to use it.
public IHttpActionResult Test()
{
return Ok();
}
}
The implemented InstanceController can look like this:
[RoutePrefix("api/{controller}")]
public class FloorItemsController ListItemsController<Floor>
{
// delegate the Constructor-call to base()
public ListItemsController()
:base()
{
}
// No need to reimplement Methods.
}
The RouteConfiguration should be set back to default, because RouteAttributes are set for this.
Basically, what you'll need to do is to replace the controller activator, with a custom implementation.
First, createa class that implements the IHttpControllerSelector interface. Take a look at this link for some of the thing you should be aware before creating a custom activator. At the bottom there's a link to some code example of a custom implmentation.
Now, this depends on what your rules will actually be, but for perfomance reasons,you should try to build a solution that always map the same controller name to the same closed type of your generic controller type. A simple implementation for your case would look something like this:
public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
//get the generyc type of your controller
var genericControllerType = typeof(ListItemsController<>);
// Get the route value from which you'll get the type argument from your controller.
string typeParameterArgument = GetRouteVariable<string>(routeData, 'SomeKeyUsedToDecideTheClosedType');
Type typeArgument = //Somehow infer the generic type argument, form your route value based on your needs
Type[] typeArgs = { typeof(typeArgument) };
//obtain the closed generyc type
var t = genericControllerType.MakeGenericType(typeArgs);
//configuration must be an instance of HttpConfiguration, most likeley you would inject this on the activator constructor on the config phase
new HttpControllerDescriptor(_configuration, t.Name, t);
}
Finally, on your ApiConfig class you'll need to add this line:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
new MyOwnActivatior());
I can't test this code right now, so it might need some tweaking, but hopefully this will guide you on the right direction. Do take notice of the link i pasted above, since there are important considerations you'll need to take into account before implementing a custom activator. Also, check the code example linked on that post to see how to implement the GetControllerMapping method
I think I've discovered a webapi bug.
This is a simplified version of the problem I have this Controller, whose routes get list is suppose to link up to the employeeId
[RoutePrefix("/divisions/{parentId}/employees/{employeeId}/dependent")]
public class EmployeeDependentController : ParentBasedListController<EmployeeDependentDTO, GlobalEntityKey<IEmployee>>
{
[HttpGet]
[ReadRoute("")]
public override EmployeeDependentDTO[] GetList(GlobalEntityKey<IEmployee> employeeId, bool keysOnly = false)
{
return base.GetList(employeeId, keysOnly);
}
protected override EmployeeDependentDTO[] GetListImp(GlobalEntityKey<IEmployee> employeeKey)
{
return GlobalFactory<IEmployeeDependentService>.Instance.GetList(employeeKey);
}
}
However it's inheriting from a base class whose parameter name links up to the parentId normally
public abstract class ParentBasedListController<TEntityDTOHeader, TEntityDTOKey, TParentEntityKey> : ApiController
where TParentEntityKey : IKey
where TEntityDTOHeader : TEntityDTOKey
{
#region Public Methods
[HttpGet]
[ReadRoute("")]
public virtual TEntityDTOKey[] GetList(TParentEntityKey parentId, bool keysOnly = false)
{
if (false == keysOnly)
{
return this.GetListImp(parentId).Cast<TEntityDTOKey>().ToArray();
}
return this.GetKeyListImp(parentId);
}
#endregion
#region Protected Methods
protected abstract TEntityDTOHeader[] GetListImp(TParentEntityKey parentKey);
#endregion
}
When I Override the get list method, webapi returns an error for duplicate matching actions. What's even more interesting is that i've tried this with the new keyword which should hide that method in both case I receive this error.
Why is this happening? is this a bug in webapi?, I understand that they are using reflection for the controllers but they should be able to determine if a method was overridden and use the correct one.
I have several controllers tied to this route and several controller inheriting from this base class both in which would be a hassle to change, What is the best work around for this?
I am building a class library that interacts with various 3rd party API's. I have used an facade pattern to provide simplified access to complicated and confusing calls, and a factory pattern to return the correct implementation. I am now trying to build one of the implementation but cant think of an elegant design.
The implementation i am building requires a URL to be constructed (which i am doing via URIBuilder). I then need to "execute" the url. I then deserialize the Xml result into a class.
I am planning on using HttpClient to call the api with the URI i built, but am not sure on how to structure the class. The options i have thought of are:
A base class of my implementation so can call it via base.InvokeURI(Uri myUri).
A seperation class so it can be used by multiple implementations
I am also unsure where the deserialization should reside.
I think using Interface in this case is more suitable:
public interface IURLInvoke
{
string InvokeURI(Uri myUri);
}
// some implementation
public class YourURLInvoker: IURLInvoke
{
public string InvokeURI(Uri myUri)
{
// do something
}
}
public class YourClass
{
public IURLInvoke Invoker {get; set;}
public void InvokeURI(Uri myUri)
{
if(Invoker == null)
return;
string xml = Invoker.InvokeURI(Uri myUri);
// put your code for deserialization here
}
}
// here is an usage example:
YourClass a = new YourClass();
// set an Invoker, choose a strategy to invoke url
a.Invoker = new YourURLInvoker();
a.InvokeURI(url);
This approach is also called Strategy Pattern
Pls see dummy code using adapter pattern and dependency injection.
Idea is to create a interface and pass it around
public class Adapter{
public void processRequest(){
RequestProcessor processor = new RequestProcessor();
processor.processRequest();
}
}
public class RequestProcessor{
public void procesRequest(){
Irequest request = new HTTPRequest();
HTTPService service = new HTTPService();
// fetch the uri from builder class
URI url = URIBUIlder();
string response = service.sendRequest(request,url);
// now fetch type from just
Type t = Serializer.searialize<T>(response);
}
}
public Class Serializer{
public static T searialize<T>(string xml){
}
}
public interface IRequest{
public string sendRequest(uri url);
}
public class HTTPRequest:IRequest{
public string sendRequest(uri url){
// instantiate actual http request here and return response
}
}
//This will act as controller
public class HTTPService{
public string sendRequest(IRequest request,uri url) {
return request.sendRequest(url);
}
}
How to inherit all DTO resource in one service?.
Say for example ,
I Have Resource Class :
[RestService("/getstudentname", "GET,POST,PUT,OPTIONS")]
public class RestResourcename
{
public string Name { get; set; }
}
[RestService("/getstudentID", "GET,POST,PUT,OPTIONS")]
public class CNextRestResourceid
{
public string Name { get; set; }
}
I have my Service Class :
1.How to inherit another DTO Class in this Service ????????
2.Do i need to create seperate class for this ?????
public class CnextRestService : RestServiceBase<RestResourcename>
{
public override object OnGet(RestResourcename request)
{
return request;
}
}
Please suggest me on this issues.......
You can implement multiple HTTP Verbs on the same Resource (aka Request) DTO in the same web service, e.g:
public class CustomersService : Service
{
object Get(GetCustomer request){...}
object Post(CreateCustomer request){...}
object Put(UpdateCustomer request){...}
object Delete(DeleteCustomer request){...}
}
This allows you to provide multiple implementations for the following HTTP actions:
GET /customers
GET /customers/1
POST /customers
PUT /customers/1
DELETE /customers/1
Although if you use SOAP you're limited to 1 RPC method for each web service since SOAP only supports HTTP POST.
The best way to do this is to inherit from Service and implement the Any() method which will be called regardless of which HTTP Verb or endpoint was used to invoke the service.