Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to generate Urls with WebAPI?

POST EDITED BELOW

We can't figure out why UrlHelper is returning null strings when used from the WebApi controller's context.

We've done the neccesary debugging but we can't find out why this happens, the RouteData has the routes in it but it doesn't seem to be working.

For the most part, we use a RenderViewToString function, which loads views that consist of calls to Url.RouteUrl(routeName).

Something that's been tried is creating a custom UrlHelper (but to no avail) and debugging with either UrlHelper's (MVC / HTTP).

Attribute routing is used everywhere with route names.

Example usage code:

    public class WebApiController : BaseApiController
    {
        [HttpPost]
        [ResponseType(typeof(string))]
        [Route("cart/get/checkout", Name = "api.cart.get.checkout")]
        public IHttpActionResult GetCheckOutShoppingCart([FromBody] string data)
        {
               return Ok(RenderViewToString("CartController", "_CheckOutCartPartial", new ShoppingCartModel(Auth.IsAuthenticated ? Auth.GetCustomer().DefaultShippingInfo.CountryId : 148)
               {
                   AddInsurance = false,
                   InsuredShipping = insuredShipping,
                   CurrentDeliveryMethodId = deliveryMethodId,
                   CurrentPaymentMethodId = paymentMethodId
               }));
        }
   }

BaseApiController class:

 public class BaseApiController : ApiController
    {
        public static string RenderViewToString(string controllerName, string viewName)
        {
            return RenderViewToString(controllerName, viewName, new Dictionary<string, object>());
        }

        public static string RenderViewToString(string controllerName, string viewName, object model)
        {
            using (var writer = new StringWriter())
            {
                var routeData = new RouteData();
                routeData.Values.Add("controller", controllerName);
                var fakeControllerContext =
                    new ControllerContext(
                        new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null),
                            new HttpResponse(null))), routeData, new FakeController());
                var razorViewEngine = new RazorViewEngine();
                var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);
                var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View,
                    new ViewDataDictionary(model), new TempDataDictionary(), writer);
                razorViewResult.View.Render(viewContext, writer);
                return writer.ToString();
            }
        }

        public static string RenderViewToString(string controllerName, string viewName, Dictionary<string, Object> data)
        {
            using (var writer = new StringWriter())
            {
                var viewData = new ViewDataDictionary();
                foreach (var kv in data)
                {
                    viewData[kv.Key] = kv.Value;
                }

                var routeData = new RouteData();
                routeData.Values.Add("controller", controllerName);
                var fakeControllerContext =
                    new ControllerContext(
                        new HttpContextWrapper(new HttpContext(new HttpRequest(null, "http://google.com", null),
                            new HttpResponse(null))), routeData, new FakeController());
                var razorViewEngine = new RazorViewEngine();
                var razorViewResult = razorViewEngine.FindView(fakeControllerContext, viewName, "", false);
                var viewContext = new ViewContext(fakeControllerContext, razorViewResult.View, viewData,
                    new TempDataDictionary(), writer);
                razorViewResult.View.Render(viewContext, writer);
                return writer.ToString();
            }
        }

        private class FakeController : ControllerBase
        {
            protected override void ExecuteCore()
            {
            }
        }
    }

EDIT

We've put together a class that should in theory work, but it doesn't. The RouteData has both the MVC and API routes in the collection.

 public static class Url
    {
        public static bool IsWebApiRequest()
        {
            return
                HttpContext.Current.Request.RequestContext.HttpContext.CurrentHandler is
                    System.Web.Http.WebHost.HttpControllerHandler;
        }

        public static string RouteUrl(string routeName, object routeValues = null)
        {
            var url = String.Empty;
            try
            {
                if (IsWebApiRequest())
                {
                    var helper = new System.Web.Http.Routing.UrlHelper();
                    url = helper.Link(routeName, routeValues);             
                }
                else
                {
                    var helper = new System.Web.Mvc.UrlHelper();
                    url = helper.RouteUrl(routeName, routeValues);              
                }

                return url;
            }
            catch
            {
                return url;
            }
        }

        public static string HttpRouteUrl(string routeName, object routeValues = null)
        {
            var url = String.Empty;
            try
            {
                if (IsWebApiRequest())
                {
                    var helper = new System.Web.Http.Routing.UrlHelper();
                    url = helper.Link(routeName, routeValues);
                }
                else
                {
                    var helper = new System.Web.Mvc.UrlHelper();
                    url = helper.HttpRouteUrl(routeName, routeValues);
                }

                return url;
            }
            catch
            {
                return url;
            }
        }
    }
like image 302
NullBy7e Avatar asked Nov 13 '15 08:11

NullBy7e


People also ask

How do I get an api URL?

1. Through the dataset URL: You can get the API endpoint by simply taking the dataset's UID and replacing it in this string: https://domain/resource/UID.extension *where the extension is the data format you's like to pull the data as. For a full list of extension formats please go here.

How do I give a website api URL?

UrlHelper which allows you to construct urls for api controllers. Example: public class ValuesController : ApiController { // GET /api/values public IEnumerable<string> Get() { // returns /api/values/123 string url = Url.

What class can be used to generate links to routes in Web API?

Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes.

What is URL in Web API?

The URL API is a component of the URL standard, which defines what constitutes a valid Uniform Resource Locator and the API that accesses and manipulates URLs.


2 Answers

This issue has been resolved by building a custom UrlHelper class, which looks into the RouteTable and then returns the url with the patterns replaced.

public static class Link
{
    public static string RouteUrl(string routeName, object routeValues = null)
    {
        var url = String.Empty;
        try
        {
            var route = (Route)RouteTable.Routes[routeName];
            if (route == null)
                return url;

            url = "~/".AbsoluteUrl() + route.Url;
            url = url.Replace("{culture}", System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.ToLower());

            if (routeValues == null) 
                return url;

            var values =  routeValues.GetType().GetProperties();
            Array.ForEach(values, pi => url = Regex.Replace(url, "{" + pi.Name + "}", pi.GetValue(routeValues, null).ToString()));

            return url;
        }
        catch
        {
            var newUrl = RouteUrl("403");
            if(newUrl == String.Empty)
                throw;

            return newUrl;
        }
    }

    public static string HttpRouteUrl(string routeName, object routeValues = null)
    {
       return RouteUrl(routeName, routeValues);
    }
}
like image 50
NullBy7e Avatar answered Oct 07 '22 13:10

NullBy7e


Try Uri.Link(routeName, object)

var uri = Url.Link("api.cart.get.checkout", new { id = 1 });

This will inject the given object's properties into the route if the property name matches a route parameter.

like image 27
ManOVision Avatar answered Oct 07 '22 14:10

ManOVision