DEV Community

Paul Delcogliano
Paul Delcogliano

Posted on

Squash Exceptions Thrown From CreatedAtRoute With These Tips

ASP.Net Core's CreatedAtRoute method facilitates returning an HTTP 201 response and a Location header from a RESTful API. Implementing the method is straightforward; call CreatedAtRoute with a few parameters and the API will return the appropriate response. But pass invalid values for those parameters and all hell breaks loose. Instead of returning a 201 response, the API will return an HTTP 500 error. Here are some useful tips for avoiding exceptions raised by CreatedAtRoute.

RESTful API best practices dictate that an HTTP POST resulting in the creation of a resource returns a 201 status code and includes a Location header that points to the new resource's URL.

ASP.Net Core's CreatedAtRoute method returns a CreatedAtRouteResult object which is an ActionResult that returns an HTTP 201 (Created) response with a Location header. The value returned as the Location header is specified via CreatedAtRoute's method parameters.

Tip One: Use nameof() Instead of a String Value

CreatedAtRoute's routeName parameter is used to generate the URL returned as the Location header. Its value is a pointer which refers to a GET method in the API. Here's how it works; pass the API's GET method name as the routeName value. CreatedAtRoute uses that value to create the URL, which is then returned as the Location header. Suppose the API, named "resources", has a GET method defined as follows:

[HttpGet("{resourceId}", Name = "MyGetMethod")]
public async ActionResult GetResource(string resourceId)
Enter fullscreen mode Exit fullscreen mode

Within the API's POST method, a call to CreatedAtRoute requires passing it "MyGetMethod" for the routeName parameter:

return CreatedAtRoute("MyGetMethod", ...);
Enter fullscreen mode Exit fullscreen mode

The resulting CreatedAtRouteResult object will return a URL similar to the following in the Location header:

https://localhost:8080/api/resources/123

Since routeName is a string, it is susceptible to fat-fingered spelling mistakes. There is no way the compiler is going to know the string provided is the wrong value, so no compile-time error is raised. Mistakes made with the routeName parameter will lead to an InvalidOperationException: No route matches the supplied values exception.

Avoid exceptions like these by using the nameof function instead of passing a string. Compile-time errors are raised if the nameof function is passed the wrong name of the API's GET method. The code snippet below shows the same call to CreatedAtRoute with the "MyGetMethod" string replaced with a call to nameof to return the GET method's name. In this case, nameof is taking the name of the GET method, GetResource, not the value from GET method's name attribute, "MyGetMethod":

return CreatedAtRoute(nameof(GetResource), ...);
Enter fullscreen mode Exit fullscreen mode

Tip Two: Double-check the routeValues Parameter

The routeValues parameter is described in the Microsoft documentation as

the route data to use for generating the URL.

Essentially route data is any data that needs to be supplied to the GET method as method parameters, i.e., an id value. Refer back to the code snippet above where GetResource is defined. GetResource takes a resourceId parameter. In order to create the correct Location header, CreatedAtRoute needs to provide a value for the resourceId parameter. It does this using the routeValues parameter.

string resourceId = "someUniqueValue";
return CreatedAtRoute(nameof(GetResource), new {resourceId}, ...);
Enter fullscreen mode Exit fullscreen mode

In the preceding code snippet, a local variable, named resourceId, is assigned a value and then passed as an anonymous object to the routeValue parameter. Since a property name isn't specified in the anonymous object, the property name defaults to the local variable's name, which is the same name as GetResource's resourceId parameter. The correct Location header is returned because the property name matches GetResource's resourceId parameter name.

Exceptions occur when the anonymous object's property name doesn't match the GET method's parameter name. The following code, which names the local variable resources instead of resourceId, throws an InvalidOperationException exception at runtime because there is no GET method with a route path that matches with the name resources in the API.

string resources = "someUniqueValue";
return CreatedAtRoute(nameof(GetResource), new {resources}, ...);
Enter fullscreen mode Exit fullscreen mode

Invalid parameter values passed to CreatedAtRoute throw an HTTP 500 exception of No route matches the supplied values. I have learned these tips the hard way. I have found that exceptions thrown from CreatedAtRoute can be difficult to troubleshoot and debug. The next time you get an InvalidOperationException from CreatedAtRoute, remember these tips, and your debugging session will go much smoother.

Discussion (0)