I am very new to breeze. I have downloaded the template for AngularBreeze and trying to create a sample application but i am stuck on Breeze WebApi Controller.
BreezeController]
public class ProductsBreezeController : ApiController
{
private readonly ProductRepository context;
public ProductsBreezeController()
{
context = new ProductRepository();
}
[HttpGet]
public string Metadata()
{
return context.Metadata();
}
//// GET api/productsbreeze
public IQueryable<Product> GetAllProducts()
{
return context.TodoLists;
}
}
public class ProductRepository : EFContextProvider<SampleEntities>
{
public DbQuery<Product> TodoLists
{
get { return Context.Products; }
}
}
Exception Message
Multiple actions were found that match the request: System.String Metadata() on type AngularWebApi.ApiControllers.ProductsBreezeController System.Linq.IQueryable`1[AngularWebApi.DataAccess.Model.Product] GetAllProducts() on type AngularWebApi.ApiControllers.ProductsBreezeController
ExceptionType: "System.InvalidOperationException"
You need to set your breezewebapiconfig.cs up to accept an action parameter as we'll. currently you have a controller only probably.
Open appstart folder and BreezeWebApiConfig.cs and add it there (should see something like ) -
Breeze/{controller}/{action}/{id}
And you need to add the action part in there
Edit
In your question it clearly shows the route for that controller action is api/productsbreeze. If that is the route you are hitting then you need to adjust that route to accept an action as well. If it is the Breeze route you are trying to hit then add an HttpGet controller attribute on the action
//// GET api/productsbreeze
[HttpGet]
public IQueryable<Product> GetAllProducts()
{
return context.TodoLists;
}
You need to make sure that your BreezeWebApiConfig is also registered in the Global.asax, of course.
Requesting URL should be matched with Breeze Api Configuration.
Server Side Configuration
GlobalConfiguration.Configuration.Routes.MapHttpRoute("BreezeApi", "breeze/{controller}/{action}");
Client Side
var manager = new breeze.EntityManager("/breeze/ProductsBreeze");
Related
Is it "legal" to have a controller inherit a route from its BaseController ? It seems it's not allowed for Attribute Routing , but how about normal route registration via RouteCollection?
The reason is I currently have a bunch of controllers, each representing some kind of file converter. Each of them has a common set of methods to upload the file to be converted. These method are endpoints on each controller not just private methods. I'd like for the following routes to be valid:
/api/controller1/uploadfile
/api/controller2/uploadfile
/api/controller3/uploadfile
Can I get an example how this could be done inside a BaseController and if it's not possible, an alternative.
Here's what works:
public abstract class BaseUploaderController : ApiController
{
[HttpGet, Route("uploadfile")] //Needs both because HttpGet("uploadfile") currently only supported in MVC attribute routing
public string UploadFile()
{
return "UploadFile";
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseUploaderController
{
[Route("{id:int}")]
public string Get(int id)
{
return "value";
}
}
Are you looking to place this UploadFile action in the base controller and other controllers inheriting from them should still be able to hit UploadFile from their respective routes like you mentioned in your post? If yes, you could create an abstract base api controller and place this UploadFile action in it and your requests to the individual controllers should work as expected.
Example:
public abstract class BaseApiController : ApiController
{
// POST /api/Values
// POST /api/Test
public string UploadFile()
{
return "UploadFile";
}
}
public class TestController : BaseApiController
{
// GET /api/test/10
public string GetSingle(int id)
{
return "Test.GetSingle";
}
}
public class ValuesController : BaseApiController
{
// GET /api/values/10
public string GetSingle(int id)
{
return "Values.GetSingle";
}
}
As per this answer https://stackoverflow.com/a/21610390/122507 attribute routes are not inherited.
I am currently debating between introducing unnecessary method in 30 controllers just so I can add an attribute route or add a fake parameter to the base class method to let the default routing disambiguate between Get(int id) and GetHistory(int id, bool history) where I don't need the second parameter.
in Asp.Net MVC if I decorate an action method with attribute NonAction then it wont be allowed to be called by the user visiting the site.
same happens when I make it private
So whats the difference between the two and is there a special purpose for which NonAction attribute has been made?
For example whats the difference between
[NonAction]
public ActionResult SomeAction(){}
And
private ActionResult SomeAction(){}
in the context of asp.net MVC of course I know one is public and the other one is private
That's the only difference. The attribute is used when you want a method that has a signature that would make it an action, but that you don't want to be an action.
An example for a use for that is a method that action methods call to produce the ActionResult for them:
[NonAction]
public JsonResult JsonInfo(string id, string value) {
return Json(new { id = id, value = value });
}
public JsonResult GetBusInfo() {
return JsonInfo("4", "Bus");
}
public JsonResult GetCarInfo() {
return JsonInfo("8", "Car");
}
The reason to make it public instead of private would be so that actions in other controllers could also use it.
Both works same with action method,you can use them seperately or together.
[NonAction]
private ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FEED_TBL fEED_TBL = db.FEED_TBL.Find(id);
if (fEED_TBL == null)
{
return HttpNotFound();
}
return View(fEED_TBL);
}
If declare it like the above code then when we will try to go to details action method it will not go to it.Rather it will show the error.
{{ HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.}}
This shows that our detail link on view does found any reference to details action method and our controller to.
With Web API, I need to redirect the following two Restful routes to two different action methods:
/products/2 -> Get info for product id 2
/products?someOptionalId=456 -> Get info for all products. Use someOptionalId as a filter if provided.
Unfortunately using the standard routing and model binding scheme, since both URLs point to the same products controller and have one id as a parameter, I either run into a compile time issue creating two Get methods with same int parameter, or a run time issue with MVC not able to pick a particular action method
Compile time error
public IQueryable<Product> Get(int someOptionalIdQs)
{
}
public Product Get(int id)
{
}
Run time error (Note hack to use a string for someOptionalIdQs and then convert to int)
public IQueryable<Product> Get(string someOptionalIdQs)
{
}
public Product Get(int id)
{
}
Please suggest a fix ideally without having to make any routing config changes given that I would like to keep the routing as clean as possible. Thanks.
As your Method has an optional Id parameter you can simply use a nullable int for the Get for the collection.
The code below will support the following urls:
http:// server /api/products
http:// server /api/products?someOptionalIdQs=3
http:// server /api/products/2
Code example
public class Product
{
public string Name { get; set; }
}
public class ProductsController : ApiController
{
public IQueryable<Product> Get([FromUri] int? someOptionalIdQs = null)
{
if(someOptionalIdQs.HasValue)
{
//apply the filter
}
return new List<Product>().AsQueryable();
}
public Product Get(int id)
{
return new Product();
}
}
Use your first approach but try renaming one of your Get methods. Note that if the action name doesn't have a prefix of 'Get', make sure the [HttpGet] attribute is used.
// [HttpGet]
public IQueryable<Product> Get2(int someOptionalIdQs)
{
}
public Product Get(int id)
{
}
What you can do it probly see if the this.HttpContext.Request.QueryString.AllKeys.Contains("someOptionalIdQs") then do the processing according to Option Id Qs else your normal work flow would work.
But this would be a cryptic implementation an ideally you should create a new URL all together for a different work flow for the app.
Surprised I'm not finding this answer anywhere, how can I determine what Controller/Action will be invoked for a given URL in MVC 3?
Update
What I really want to know:
"how can I determine what ControllerAction will be invoked for a given URL in MVC 3?" ....yeah
So, either I'm not aware of the magic method that does this:
ControllerActionInfo GetControllerActionInfo(string url)
Or, I will have to create it myself doing whatever MVC does when it gets an http request.
My purpose of asking about this on StackOverflow is that I can save some time reverse engineering this behavior. The correct answer should resemble:
Here's how you can do it: and some code would follow.
You have to use a dummy HttpContext and HttpRequest classes as follows:
public class DummyHttpRequest : HttpRequestBase {
private string mUrl;
public DummyHttpRequest(string url) {
mUrl = url;
}
public override string AppRelativeCurrentExecutionFilePath {
get {
return mUrl;
}
}
public override string PathInfo {
get {
return string.Empty;
}
}
}
public class DummyHttpContext : HttpContextBase {
private string mUrl;
public DummyHttpContext(string url) {
mUrl = url;
}
public override HttpRequestBase Request {
get {
return new DummyHttpRequest(mUrl);
}
}
}
Edit: Also, you can extend the DefaultControllerFactory and add a simple method to get the desired information instead of an instance of Controller. (Note: It's merely a sample, you have to support other aspects like ActionNameAttribute and so on)
public class ControllerActionInfo {
public ControllerActionInfo(Type controllerType, MethodInfo action) {
ControllerType = controllerType;
Action = action;
}
public Type ControllerType { get; private set; }
public MethodInfo Action { get; private set; }
}
public class DefaultControllerFactoryEx : DefaultControllerFactory {
public ControllerActionInfo GetInfo(RequestContext requestContext, string controllerName) {
Type controllerType = GetControllerType(requestContext, controllerName);
if (controllerType == null) {
return null;
}
MethodInfo actionMethod = controllerType.GetMethod(requestContext.RouteData.GetRequiredString("action"), BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
return new ControllerActionInfo(controllerType, actionMethod);
}
}
Then, use following code snippet to get access to the controller:
DummyHttpContext httpContext = new DummyHttpContext("~/home/index");
RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
// IController controller = new DefaultControllerFactory().CreateController(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
DefaultControllerFactoryEx controllerFactory = new DefaultControllerFactoryEx();
var result = controllerFactory.GetInfo(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
The logic for this is in the System.Web.Mvc.MvcHandler class, the System.Web.Mvc.DefaultControllerFactory class, and the System.Web.Mvc.ControllerActionInvoker class. .NET Reflector is your friend.
Basically, the MVC framework:
Uses reflection to get all the controllers in the application project.
Then it does something like IEnumerable<string> controllerNames = controllerTypes.Select(controllerType => controllerType.Name.Replace("Controller",string.Empty));. It then tries to match the first path segment, {controller}, to one of these sanitized controller type names (case-insensitive).
Then, it looks at this controller's public methods that have a return type that is of type ActionResult or some derivative. It matches the method name to the second path segment, {action}, as the action method to be called.
If the selected method has a parameter that is named id, then it matches the third path segment {id} to that value, and passes it to the method. Otherwise, the optional id parameter is ignored.
If the ActionResult type that is returned is a derivative of ViewResultBase then the IViewEngine tries to locate a corresponding view in the project using whatever conventions have been specified for that view engine. The WebFormViewEngine, for example, looks in the project for ~/Views/{controller}/{action}.ascx, ~/Views/{controller}/{action}.aspx, ~/Views/Shared/{action}.ascx, ~/Views/Shared/{action}.aspx by default.
If you want to further understand how routing works in MVC, I would highly suggest Scott Gu's article on MVC Routing.
aspnet mvc has the HandleError filter that will return a view if an error occurs, but if an error occurs when calling a JsonResult Action how can I return a JSON object that represents an error?
I don't want to wrap the code in each action method that returns a JsonResult in a try/catch to accomplish it, I'd rather do it by adding a 'HandleJsonError' attribute or using the existing HandleError attribute to the required action methods.
Take a look at the MVC implementation of HandleErrorAttribute. It returns a ViewResult. You could write your own version (HandleJsonErrorAttribute) that returns a JsonResult.
In short, the way to go can be to extend the HandleErrorAttribute, like this:
public class OncHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext context)
{
// Elmah-Log only handled exceptions
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
if (context.HttpContext.Request.IsAjaxRequest())
{
// if request was an Ajax request, respond with json with Error field
var jsonResult = new ErrorController { ControllerContext = context }.GetJsonError(context.Exception);
jsonResult.ExecuteResult(context);
context.ExceptionHandled = true;
}
else
{
// if not an ajax request, continue with logic implemented by MVC -> html error page
base.OnException(context);
}
}
}
Remove Elmah logging code line if you don't need it. I use one of my controllers to return a json based on an error and context. Here is the sample:
public class ErrorController : Controller
{
public ActionResult GetJsonError(Exception ex)
{
var ticketId = Guid.NewGuid(); // Lets issue a ticket to show the user and have in the log
Request.ServerVariables["TTicketID"] = ticketId.ToString(); // Elmah will show this in a nice table
ErrorSignal.FromCurrentContext().Raise(ex); //ELMAH Signaling
ex.Data.Add("TTicketID", ticketId.ToString()); // Trying to see where this one gets in Elmah
return Json(new { Error = String.Format("Support ticket: {0}\r\n Error: {1}", ticketId, ex.ToString()) }, JsonRequestBehavior.AllowGet);
}
I add some ticket info above, you can ignore this. Due to the way the filter is implemented (extends the default HandleErrorAttributes) we can remove then HandleErrorAttribute from the global filters:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAuthorise());
filters.Add(new OncHandleErrorAttribute());
//filters.Add(new HandleErrorAttribute());
}
This is basically it. You can read my blog entry for more detailed info, but for the idea, above should suffice.
Maybe you could create your own Attribute and have a constructor value that takes an enum value of View or Json. Below is what Im using for a custom Authorization Attribute to demonstrate what I mean. This way when authentication fails on a json request it responds with a json error and the same with if it returns a View.
public enum ActionResultTypes
{
View,
Json
}
public sealed class AuthorizationRequiredAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public ActionResultTypes ActionResultType { get; set; }
public AuthorizationRequiredAttribute(ActionResultTypes actionResultType)
{
this.ActionResultType = ActionResultType;
}
}
//And used like
[AuthorizationRequired(ActionResultTypes.View)]
public ActionResult About()
{
}