OData 4 web api 2 "No routing convention was found" - asp.net-web-api

I'm trying to post object to odata action, here my code
public class DraftController : ODataController
{
public HttpResponseMessage Attachment([FromODataUri] string key, [FromBody] DraftApi d)
{
try
{
return Request.CreateResponse(HttpStatusCode.Created, "(POST ATTACHMENT) key: " + key + " - id: " + d.id + ", desc: " + d.description);
}
catch (Exception e)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}
}
}
this is my model
public class DraftApi
{
[Key]
public string id { get; set; }
public string description { get; set; }
}
this is my OData route
config.MapODataServiceRoute(
routeName: "ODataDraft",
routePrefix: "odata/{namespace}",
model: BuildModel<DraftApi>("draft")
);
private static IEdmModel BuildModel<T>(string EntityName) where T : class
{
ODataConventionModelBuilder ODataBuilder = new ODataConventionModelBuilder();
ODataBuilder.EntitySet<T>(EntityName).EntityType.Name = EntityName;
ActionConfiguration attachment = ODataBuilder.EntityType<T>().Action("Attachment");
ODataBuilder.EnableLowerCamelCase();
return ODataBuilder.GetEdmModel();
}
my call is this
Url
http://127.0.0.1/mpssapi/odata/v1/draft('hhh')/Attachment
Headers
Content-Type: application/json
Payload
{id:"a", description: "abc"}
This is my response
{
"error": {
"code": ""
"message": "No HTTP resource was found that matches the request URI 'http://127.0.0.1/mpssapi/odata/v1/draft('hhh')/Attachment'."
"innererror": {
"message": "No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'."
"type": ""
"stacktrace": ""
}-
}-
}
I have tried to add namespace to odata route but it doesn't work
any ideas?
thanks

The doc may help: http://odata.github.io/WebApi/#04-07-action-parameter-support
and call without namespace, you need to turn on UnqualifiedNameCall option like:
config.EnableUnqualifiedNameCall(true);

Related

Swagger Bearer Authentication isn't showing Authorization JWT token field for [GET] request

Authorization field is showing for HTTP POST request but it's not showing for GET request to add a token for authentication of Web API.
config.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "Mach.CharterPad.API");
c.OperationFilter<SwaggerAuthorizationFilter>();
c.RootUrl(req => $"{req.RequestUri.GetLeftPart(UriPartial.Authority)}{req.GetConfiguration().VirtualPathRoot.TrimEnd('/')}{appvirtualpath}/api");
}).EnableSwaggerUi();
public class SwaggerAuthorizationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters != null)
{
operation.parameters.Add(new Parameter
{
name = "Authorization",
#in = "header",
description = "access token",
required = false,
type = "string"
});
}
}
}
I have updated API parameters and it's working fine.
[Route("")]
public IHttpActionResult Get([FromUri] Paging paging)
{
var result = TripManager.Get(paging.Index, paging.Size);
return Ok(result != null ? new ApiResponse(true, "Results found", result) : new ApiResponse(false, "No record found", result));
}

MongoDB search nested objects in an array

I am using MongoDB to store all the events in my Eventbrite clone. So I have a collection called events then the objects in this collection consists of their name and and array of users that have rsvp to the event. I can query for any events that the current user has created but unable to figure out how to query both events the user has created and rsvp to.
Here is the compiled query that I am using to try to get all the users events.
events.find({"$and":[{"user_id":"5d335704802df000076bad97"},{"user_id":{"$ne":null}}],"$or":[{"checkins.user_id":"5d335704802df000076bad97"}]},{"typeMap":{"root":"array","document":"array"}})
I am using the Laravel MongoDB plugin to query my data in php it looks like this
$user->events()->orWhere(function ($query) use ($user){
return $query->where('checkins.user_id',new ObjectID($user->id));
})->get()
The event object looks something like this:
{
"name": "test",
"user_id": "1"
"rsvp": [
{"user_id": "12"}
]
}
An user can rsvp to other event that are not their own.
you need an $or filter and $elemMatch to get events that belong to a given user or events they've rsvp'd to.
db.events.find({
"$or": [
{
"user_id": "5d33e732e1ea9d0d6834ef3d"
},
{
"rsvp": {
"$elemMatch": {
"user_id": "5d33e732e1ea9d0d6834ef3d"
}
}
}
]
})
unfortunately i can't help you with laravel version of the query. in case it helps, below is the c# code that generated the above mongo query.
using MongoDB.Entities;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class user : Entity
{
public string name { get; set; }
}
public class Event : Entity
{
public string name { get; set; }
public string user_id { get; set; }
public rsvp[] rsvp { get; set; }
}
public class rsvp
{
public string user_id { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
var mike = new user { name = "mike" };
var dave = new user { name = "dave" };
mike.Save();
dave.Save();
(new[] {
new Event
{
name = "mike's event",
user_id = mike.ID,
rsvp = new[]
{
new rsvp { user_id = dave.ID }
}
},
new Event
{
name = "dave's event",
user_id = dave.ID,
rsvp = new[]
{
new rsvp { user_id = mike.ID }
}
}
}).Save();
var result = DB.Find<Event>()
.Many(e =>
e.user_id == mike.ID ||
e.rsvp.Any(r => r.user_id == mike.ID));
}
}
}

Customise WebAPI response like Status, Data, message formate

How can i customise WebAPI 2 response like status, data, message in JSON format
Successful request:
{
"status": "success",
"data": {
/* Application-specific data would go here. */
},
"message": null /* Or optional success message */
}
Failed request:
{
"status": "error",
"data": null, /* or optional error payload */
"message": "Error xyz has occurred"
}
Define a new class like :
public class ResponseDto
{
public string status { get; set; }
public dynamic data { get; set; }
public string message { get; set; }
}
and then populate the properties with respective values and do :
var response = new ResponseDto()
{
response.status = " ",
response.data = obj,
response.message = " "
}
and then from the controller method(API),
return response;
Your JSON formatter will then convert the response object into JSON string.

Multiple actions were found that match the request - WebApi

I have added WebApi to an existing MVC app and create a controller with a post method. I have one route configured in the webapiconfig. When I use Fiddler to post to the controller, I am receiving the "Multiple Actions" error. my ApiConfig and Controller are posted below. There is some Ioc and DI going on with Ninject. Do I need to add different routes definitions, or is this about the data being posted?
webapiconfig.cs
public static void Register(HttpConfiguration config)
{
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Routes.MapHttpRoute(
name: "Materials",
routeTemplate: "api/materials/{id}",
defaults: new { controller = "materials", id = RouteParameter.Optional }
);
}
MaterialController.
using ????.Info.DAL;
using ????.Info.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using IdentitySample.Models;
namespace ????.Info.Controllers.Api
{
public class MaterialsController : BaseApiController
{
public MaterialsController(I????Repository repo)
:base(repo)
{
}
[Route("api/materials/")]
public IEnumerable<MaterialModel> Get()
{
IQueryable<MaterialEntities.Materials> query;
query = TheRepository.GetAllMaterials();
var results = query
.ToList()
.Select(s => TheModelFactory.Create(s));
return results;
}
[Route("api/materials/{id:int}")]
public HttpResponseMessage GetMaterial(int id)
{
try
{
var material = TheRepository.GetMaterial(id);
if (material != null)
{
return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(material));
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody] MaterialModel materialModel)
{
try
{
var entity = TheModelFactory.Parse(materialModel);
if (entity == null) Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not read MaterialType/Organization from body");
if (TheRepository.Insert(entity) && TheRepository.SaveAll())
{
return Request.CreateResponse(HttpStatusCode.Created, TheModelFactory.Create(entity));
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not save to the database.");
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
public MaterialEntities.Materials Parse(MaterialModel materialmodel)
{
try
{
var material = new MaterialEntities.Materials()
{
Name = materialmodel.Name,
Description = materialmodel.Description,
DateCreated = materialmodel.DateCreated,
};
return material;
}
catch (Exception)
{
return null;
}
}
}
}
Ok, thanks for all the help on this. This was fixed by adding config.MapHttpAttributeRoutes(); to the WebApiConfig. I removed the default route definitions since I am going to use AttributeRouting and it's working now.
update: It looks like it is possible to mix Attribute and Convention routing in the WebApiConfig as discussed here So my problem was solved by adding config.MapHttpAttributeRoutes(); to WebApiConfig

MVC3 Areas routing conflict

Question: i want my route to be like that
/admin/main/category/1 -> 1 == ?page=1
i don't want page=1 to be seen
My Controller
public class MainController : BaseController
{
private const int PageSize = 5; //pager view size
[Inject]
public ICategoryRepository CategoryRepository { get; set; }
public ActionResult Index()
{
return View();
}
public ActionResult Category(int page)
{
//int pageIndex = page.HasValue ? page.Value : 1;
int pageIndex = page != 0 ? page : 1;
return View("Category", CategoryViewModelFactory(pageIndex));
}
/*
*Helper: private instance/static methods
======================================================================*/
private CategoryViewModel CategoryViewModelFactory(int pageIndex) //generate viewmodel category result on pager request
{
return new CategoryViewModel
{
Categories = CategoryRepository.GetActiveCategoriesListDescending().ToPagedList(pageIndex, PageSize)
};
}
}
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRouteLowercase(
"AdminCategoryListView",
"admin/{controller}/{action}/{page}",
new { controller = "Category", action = "Category", page = "1" },
new { id = #"\d+" },
new[] { "WebUI.Areas.Admin.Controllers" }
);
}
}
My Exception:
The parameters dictionary contains a null entry for parameter 'page'
of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Category(Int32)' in
'WebUI.Areas.Admin.Controllers.MainController'. An optional parameter
must be a reference type, a nullable type, or be declared as an
optional parameter. Parameter name: parameters
Thank you all in advance.
Make sure that in your Admin area route registration you have defined the {page} route token instead of {id} which is generated by default:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{page}",
new { action = "Index", page = UrlParameter.Optional }
);
}
Now when you are generating links make sure you specify this parameter:
#Html.ActionLink(
"go to page 5", // linkText
"category", // actionName
"main", // controllerName
new { area = "admin", page = "5" }, // routeValues
null // htmlAttributes
)
will emit:
go to page 5
and when this url is requested the Category action will be invoked and passed page=5 parameter.

Resources