How to use higher version for web api controller - asp.net-web-api

I am following below:
https://www.hanselman.com/blog/ASPNETCoreRESTfulWebAPIVersioningMadeEasy.aspx
Is it possible to have directly higher version for a web api controller.
like:
ApiVersion("2.05")]
[RoutePrefix("api/v{version:apiVersion}/ger")]
public class caGerController
[Route("~/api/ger/getDetail")]
[Route("getDetail")]
GetGerData
when using above one, it only works when using URL as api/v2.05/ger/getDetail
But It fails working while using URL as api/ger/getDetail and getting message as "Code": "ApiVersionUnspecified",
If change version from 2.05 to 1.0 (as all other controller) then api/ger/getDetail works.
How to solve this, I need 2.05 for this controller and need to access api/ger/getDetail URL as well.
Thanks

Since you are versioning by URL segment, you'll have to do a few things. First in the options, you need to allow implicit versioning using:
options.AssumeDefaultVersionWhenUnspecified = true;
Your original API had some version that was never declared or named. The default configuration will use "1.0". If you want the default to be something else, specify:
options.DefaultApiVersion = new ApiVersion( 2, 0 );
The next step is that you have to float the route template on the controller that you want to have the default path. ASP.NET and all other stacks I know do not have a way to provide or fill-in default values in the middle of the route template.
If "2.0" is your initial, default version, then your controller will look like:
[ApiVersion( "2.0" )]
[ApiVersion( "2.05" )]
[RoutePrefix( "api" )]
public class GerController : ApiController
{
[Route( "ger/getDetails" )]
[Route( "v{version:apiVersion}/ger/getDetails" )]
public IHttpActionResult GetDetails() => Ok();
}
If you change the controller that maps to the default route, you have to move the route template to that new controller type.
This is an unfortunate consequence of versioning by URL segment. If you don't change the default route mapping, then it shouldn't be a big deal to manage; otherwise, you should consider disallowing implicit versioning or elect an alternate versioning method.
For more information, please refer to this wiki topic.

Related

Adding a query to GoToAsync URI Navigation in Xamarin Forms Shell causes it to stop working

So for this issue I have a pretty simple stack. Main Menu Screen > List Page > Detail Page. For each page I am getting to it using
Shell.Current.GoToAsync({name of page});
so basically a push and then going back in the stack with
Shell.Current.GoToAsync("../");
This is all working fine until I introduced queries to pass data.
So this works fine
Shell.Current.GoToAsync($"{nameof(Page)}");
But this
Shell.Current.GoToAsync($"{nameof(Page)}?Id={some id here}");
Throws this exception
Relative routing to shell elements is currently not supported.
Try prefixing your uri with ///: ///PageName?Id=3AC71D0B-D8E3-6C18-FFE3-6D41E154F000
Which makes no sense because the navigation clearly works without the query included. Where am I going wrong? Is this bug or is it expected behavior?
Relative routing to shell elements is currently not supported.
Try prefixing your uri with ///: ///PageName?Id=3AC71D0B-D8E3-6C18-FFE3-6D41E154F000
That means you are using Relative routes , however this not supports for passing data before Xamarin Forms 4.7.
If using the version of Xamarin Forms before 4.7 .you need to use Absolute routes to pass data , example as follow :
Shell.Current.GoToAsync($"//animals/elephants/elephantdetails?name={elephantName}");
And to receive data, the class that represents the page being navigated to, or the class for the page's BindingContext, must be decorated with a QueryPropertyAttribute for each query parameter:
[QueryProperty("Name", "name")]
public partial class ElephantDetailPage : ContentPage
{
public string Name
{
set
{
BindingContext = ElephantData.Elephants.FirstOrDefault(m => m.Name == Uri.UnescapeDataString(value));
}
}
...
}
More info can refer here : https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/shell/navigation#pass-data
=============================Update===============================
As Shane's said , from the version of Xamarin Forms above 4.7, Relative routes also supports passing paramaters now .
I had a similar issue and solved it by registering the page into the AppShell.xaml.cs
Routing.RegisterRoute(nameof(MyPage),
typeof(MyPage));

How to handle AJAX calls after globalizating an MVC5 Website?

Background
I've inherited an e-commerce website using the usual aspnet's MVC5 / razor / jquery. The goal is gobalizating the website and add a language selector, to support the two main country/language targeting strategies. I've just accomplished the first step, wich is routing the website so it handles domain.com/en/, domain.com/es/, domain.com/es-mx/... Using http://jittuu.com/2014/3/17/AspNet-localization-routing After this, entering the domain with a non-existant locale redirects to the default one. There's a custom IRouteHandler that get's the httprequest, checks the locale and puts it if it needs to:
Public Class LocalizationRedirectRouteHandler
Implements IRouteHandler
Public Function GetHttpHandler(requestContext As RequestContext) As IHttpHandler Implements IRouteHandler.GetHttpHandler
Dim routeValues = requestContext.RouteData.Values
Dim cookieLocale = requestContext.HttpContext.Request.Cookies("locale")
If cookieLocale IsNot Nothing Then
routeValues("culture") = cookieLocale.Value
Return New RedirectHandler(New UrlHelper(requestContext).RouteUrl(routeValues))
End If
Dim uiCulture = CultureInfo.CurrentUICulture
routeValues("culture") = uiCulture.Name
Return New RedirectHandler(New UrlHelper(requestContext).RouteUrl(routeValues))
End Function
End Class
There are thousands of "inline" AJAX calls to routes across the website written without an #Html helper because the project has separate, bundled js files, failing. There are hundreds of routes failing because the project controllers are using attribute routing and it's not specifying locale. For example, a simple link to a product features is failing because the controller has a route specifying
/product/{id}
but the URL looks like
/es-MX/product/{id}
Question:
What's the right way to proceed here?
I was expecting those AJAX and links without locale to be redirected, but they are not.
Should I rewrite all those files so they specify current locale? Is there any other "healthy" way to do this, like extending BaseController and add culture as a route prefix?
Thank you in advance for your time, I'm terribly lost here.
Turns out, this answer by #NightOwl888 is the closest I've found to a solution to the problem I was facing. After including this changes to my project, I have almost everything working out and sporting a wonderful culture prefix on the URL. There's still a few things to work out though:
My default route sports no culture in the URL, wich may be against one of our goals. I can work on that toying (or removing) the defaultCulture constraint.
There are hundreds of windows.location on js files targeting routes without culture. I may be able to work on that via JS global variable storing the culture.
I still need to find a nice way to handle URL localization. Hopefully this project will help me through (also directed there by #NightOwl888).

Routing Conventions in Can.js

So I’m looking to make some routes within my super cool can.js application. Aiming for something like this…
#!claims ClaimsController - lists claims
#!claims/:id ClaimController - views a single claim
#!claims/new ClaimController - creates a new claim
#!claims/:id/pdf - do nothing, the ClaimController will handle it
#!admin AdminController - loads my Administrative panel with menu
#!admin/users - do nothing, the AdminController will handle it
#!admin/settings - do nothing, the AdminController will handle it
So how might we do this?
“claims route”: function() { load('ClaimsController'); },
“claims/:id route”: function() { load('ClaimController'); },
“admin”: function() { load(‘AdminController’); },
Cool beans, we’re off. So what if someone sends a link to someone like...
http://myapp#!claims/1/pdf
Nothing happens! Ok, well let’s add the route.
“claims/:id/pdf route”: function() { load('ClaimController'); },
Great. Now that link works. Here, the router’s job is only to load the controller. The controller will recognize that the pdf action is wanted, and show the correct view.
So pretend I’ve loaded up a claim claims/:id and I edit one or two things. Then I click the Print Preview button to view the PDF and change my route to claims/:id/pdf.
What should happen… the Claim Controller is watching the route and shows the pdf view.
What actually happens… the router sees the change, matches the claims/:id/pdf route we added, and reloads the Claim Controller, displaying a fresh version of the claim pulled from the server/cache, losing my changes.
To try and define the problem, I need the router to identify when the route changes, what controller the route belongs to, and if the controller is already loaded, ignore it. But this is hard!
claims //
claims/:id // different controllers!
claims/:id //
claims/:id/pdf // same controller!
We could just bind on the "controller" change. So defining routes like can.route(':controller') and binding on :controller.
{can.route} controller
// or
can.route.bind('controller', function() {...})
But clicking on a claim (changing from ClaimsController to ClaimController) won't trigger, as the first token claim is the same in both cases.
Is there a convention I can lean on? Should I be specifying every single route in the app and checking if the controller is loaded? Are my preferred route urls just not working?
The following is how I setup routing in complex CanJS applications. You can see an example of this here.
First, do not use can.Control routes. It's an anti-pattern and will be removed in 3.0 for something like the ideas in this issue.
Instead you setup a routing app module that imports and sets up modules by convention similar to this which is used here.
I will explain how to setup a routing app module in a moment. But first, it's important to understand how can.route is different from how you are probably used to thinking of routing. Its difference makes it difficult to understand at first, but once you get it; you'll hopefully see how powerful and perfect it is for client-side routing.
Instead of thinking of urls, think of can.route's data. What is in can.route.attr(). For example, your URLs seem to have data like:
page - the primary area someone is dealing with
subpage - an optional secondary area within the page
id - the id of a type
For example, admin/users might want can.route.attr() to return:
{page: "admin", subpage: "users"}
And, claims/5 might translate into:
{page: "claims", id: "5"}
When I start building an application, I only use urls that look like #!page=admin&subpage=users and ignore the pretty routing until later. I build an application around state first and foremost.
Once I have the mental picture of the can.route.attr() data that encapsulates my application's state, I build a routing app module that listens to changes in can.route and sets up the right controls or components. Yours might look like:
can.route.bind("change", throttle(function(){
if( can.route.attr("page") == "admin" ) {
load("AdminController")
} else if(can.route.attr("page") === "claims" && can.route.attr("id") {
load("ClaimController")
} else if ( ... ) {
...
} else {
// by convention, load a controller for whatever page is
load(can.capitalize(can.route.attr("page")+"Controller")
}
}) );
Finally, after setting all of that up, I make my pretty routes map to my expected can.route.attr() values:
can.route(":page"); // for #!claims, #!admin
can.route("claims/new", {page: "claims", subpage: "new"});
can.route("claims/:id", {page: "claims"});
can.route("admin/:subpage",{page: "admin"});
By doing it this way, you keep your routes independent of rest of the application. Everything simply listens to changes in can.route's attributes. All your routing rules are maintained in one place.

ApiController within an area

I want to have BlaController : ApiController, with BlaController located in /Areas/XXX/ ( or namespace MySolution.Areas.XXX.Controllers )
The problem is that when I browse to http://localhost:1935/XXX/Bla/SomeAction I get 404.
Normal controllers (: Controller) do not throw 404.
Note: SomeAction would be for example "public string SomeAction() { return "hi"; }", within BlaController
*Note 2: Tried* http://localhost:1935/api/Bla/SomeAction and didn't work either
Based on this SO question, looks like you need to build your own HttpControllerFactory in order to support Areas with WebAPI.
The question references an article on how to do this: http://netmvc.blogspot.com/2012/03/aspnet-mvc-4-webapi-support-areas-in.html
Hope this helps.
UPDATE:
Thanks to Bertrand who pointed to an updated article about WebApi support for Areas (which is still doesn't have by default). The updated link is http://netmvc.blogspot.be/2012/06/aspnet-mvc-4-webapi-support-areas-in.html

Routing document-relative static urls in MVC3

I'm integrating a JavaScript library into an ASP.NET MVC3 web app. The library assumes it will be installed next to the page that references it, and so it uses document-relative URLs to find its components.
For example, the default directory layout looks like
container-page.html
jslibrary/
library.js
images/
icon.png
extensions/
extension.js
extension-icon.png
However, I want to reference the library from the view in /Home/edit. I install the library in the default Scripts\jslibrary\ When I reference the library in the view in Views\Home\edit.cshtml, the library's document-relative links like
images/icon.png
end up as requests to
http://localhost/Home/images/icon.png
which results in a File Not Found (404) error. How do I construct a route to look for
{anyControllerName}/images/{anyRemainingPathInfo}
and serve up
http://localhost/Scripts/jslibrary/images/{anyRemainingPathInfo}
?
(full disclosure: I'm still on IIS 6 in Production, and not much chance of going to IIS7 any time soon, so if this is better done at the IIS level, please account for IIS6. Thanks!)
You could create a controller for handling you redirect logic - for example an "Images"controller. Register a global route in your Global.asax file, using the pattern (more on this type of pattern here:
routes.MapRoute(
"Images", // Route name
"{xyz}/{controller}/{path}", // URL with parameters
new {controller = "Images", action = "Index", path= UrlParameter.Optional} // Parameter defaults);
In your controller:
public ActionResult Index(string path)
{
//format path, parse request segments, or do other work needed to Id file to return...
return base.File(path, "image/jpeg"); //once you have the path pointing to the right place...
}
Not sure if this solution will work for you, wish I could come up with something more elegant. Best of Luck!
Short of rewriting the library and having it check for the appropriate directory the only solution I can think of is to include the views, library and supporting files in a directory structure that the library can access. This of course would break MVC's convention over configuration way of finding views, so you would have to write a custom override of the way Razor looks for views, which is not too complex to do, but you might be making life more difficult for yourself down the road depending on your application. Your call which is the lesser of the two evils :) (I'd go for fixing the library)
Make a help function
#functions{
public string AbsoluteUrl(string relativeContentPath)
{
Uri contextUri = HttpContext.Current.Request.Url;
var baseUri = string.Format("{0}://{1}{2}", contextUri.Scheme,
contextUri.Host, contextUri.Port == 80 ? string.Empty : ":" + contextUri.Port);
return string.Format("{0}{1}", baseUri, VirtualPathUtility.ToAbsolute(relativeContentPath));
}
}
Calling
#AbsoluteUrl("~/Images/myImage.jpg") <!-- gives the full path like: http://localhost:54334/Images/myImage.jpg -->
This example are from
https://dejanvasic.wordpress.com/2013/03/26/generating-full-content-url-in-mvc/

Resources