I am trying to generate and add a link to a RESTful resource from a Spring MVC controller. Our API requires the use of HTTP matrix variables. Unfortunately, the self link generated is missing the matrix variable from the URI.
#BasePathAwareController
#RequestMapping("/licenses")
public class LicenseController {
#Autowired
private LicenseRepository repository;
#RequestMapping(path = "/{licenseId}/violations", method = RequestMethod.GET)
#RestResource(rel = "violations")
#ResponseBody
#Transactional(readOnly = true)
public ResponseEntity<?> getViolations(#PathVariable String licenseId, #MatrixVariable(name = "state") String state) {
try {
StateContextHolder.setState(state);
List<ViolationEntity> violations = repository.findOne(licenseId).getViolations();
if (violations == null) {
return new ResponseEntity<Resources<?>>(HttpStatus.OK);
}
else {
Resources<?> entityResource = new Resources(violations);
entityResource.add(linkTo(methodOn(LicenseController.class).getViolations(licenseId, state)).withSelfRel());
return new ResponseEntity<Resources<?>>(entityResource, HttpStatus.OK);
}
}
finally {
StateContextHolder.clear();
}
}
}
For the HTTP GET /licenses/123456789;state=NY/violations, the returned self link is missing the state matrix parameter:
{
...,
"_links" : {
"self" : {
"href" : "http://localhost:8080/licenses/123456789/violations"
}
}
}
I don't want to hardcode this in, so I am trying to figure out how to do this with the Spring HATEOAS or Spring Data REST APIs.
I need to use matrix parameters in order to refine the licenses path element. You can read more about why here. Suffice it to say, there is some custom behavior we are doing prior to Spring Data retrieving the entities from the repository.
As I see it Spring HATEOAS has no support for matrix parameters at this time.
See this part of the UriTemplate. It uses different variable types to build the Uri. A matrix parameter isn't one of them.
See the VariableType enum, which was used to do the switch on. There is also no matrix param.
Related
I am using ASP.NET Web API. I want to REST uri to be
GET /api/v1/documents/1234/download or
GET /api/v1/documents/1234?act=download or
GET /api/v1/documents?id=1234&act=download
Is it possible to have multiple ways to call REST API Url? Is it recommended?
I am using Attribute Routes only
[RoutePrefix("api/v1")]
public class DocumentController : ApiController
{
private readonly DomainService _domainService;
public DocumentController(DomainService domainService)
: base(domainService)
{
_domainService = domainService ?? throw new ArgumentNullException(nameof(domainService));
}
[HttpGet]
[Route("documents/{id:int}")]
public async Task<IHttpActionResult> DownloadDocument([FromUri]int id, [FromUri]string act)
{
if (string.IsNullOrEmpty(act) || act.ToUpper() != "DOWNLOAD")
{
return BadRequest("Invalid action parameter.");
}
return await service.DownloadFile(id);
}
}
with above code only GET /api/v1/documents/1234?act=download works. Is it possible to configure route in a such way that all 3 routes will invoke same action method?
You can add as many Route attributes as required to each method.
So you could do this to your method:
[Route("documents")] // matches /documents?id=123&act=download
[Route("documents/{id:int}")] // matches /documents/123?act=download
[Route("documents/{id:int}/{act}")] // matches /documents/123/download
Personally I think this is quite long-winded, and would try to stick to a single style (the last one if I could choose), but I guess it could depend on your requirements.
This question already has answers here:
Need some advice for trying to mock a .NET WebClient or equivalent
(2 answers)
Closed 6 years ago.
Let's assumed that i've got simple method which gets some data from REST service. Method looks like:
public string GetDataFromRest(string uri) {
string result = String.Empty;
using(WebClient web = new WebClient()) {
result = web.DownloadString(uri);
}
return result;
}
So, now i want to create unit test for this method. I don't want to use external REST service but i want fake response from any URI without real conecting to service. Something like every execute of GetDataFromRest(uri) in Unit Test -> always returns some XML.
As the posted answer goes into some detail, part of your problem is you have a dependency on the WebClient class.
A sample wrapper for WebClient could look like:
public interface IWebClient
{
string DownloadString(string address);
}
public class WebClientWrapper : IWebClient
{
public string DownloadString(string address)
{
using(WebClient web = new WebClient()) {
return result = web.DownloadString(uri);
}
}
}
public class MyClass
{
private readonly IWebClient _webClient;
public MyClass(IWebClient webClient)
{
_webClient = webClient;
}
public string GetDataFromRest(string uri)
{
return _webClient.DownloadString(uri);
}
}
Now of course going this route means WebClientWrapper can be unit tested with a "less real" URI or what that you specifically control. I've only implemented one method of the WebClient, but this externalizes the dependency within GetDataFromRest from a real URI, as you can now mock the return data. This also helps in that anything else you need a WebClient for, you can now use the wrapper class, and easily mock the returned data, as you are now programming to an interface, rather than a concretion.
i have an application that is flexible, that the user can:
filter by any field
sort by any multiple of fields.
and because it will run in ASP.Net Site + some Xamarin C# Apps, i will also have paging in it.
For network performance, it will send projection on the required fields that will be shown.
So if i include in each "Service" method, a parameter "UQueryConstraints", that can send filter expression + oderBy expression + page numbers + Projection of the fields, to be used by the Repository, which will apply it to the DBContext, is this is going to be considered a Data leak to the domain services or not?
as seen in this Pic:
http://1drv.ms/1Ngi3Kn
e.g.:
notice:
"UQueryConstraints", it will not leak any "IQueryable".
The "AmbientDbContextLocator", from:
<http://mehdi.me/ambient-dbcontext-in-ef6/>
<https://github.com/mehdime/DbContextScope>
public class UIView
{
public static void Display()
{
object constraintsB = new UQueryConstraints<Car>().Filter(x => x.carNo <= 6).SortBy(x => x.eName).Page(1, 5);
//.Projection( field1, field2, field3)
Debug.WriteLine("---------------test CarModel -------------------");
CarModel carModel1 = new CarModel();
carModel1.printCars(constraintsB);
}
}
public class CarModel
{
private CarService _carService = new CarService();
void printCars(UQueryConstraints<Car> constraints)
{
foreach ( c in _carService.getCarsList("", constraints)) {
Debug.WriteLine("Reading from converted back: aName =" + c.aName + ", eName = " + c.eName);
}
}
}
public class CarService
{
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object dbContextScopeFactory = new DbContextScopeFactory();
object ambientDbContextLocator = new AmbientDbContextLocator();
using (dbContextScope == dbContextScopeFactory.Create()) {
//after creating the Scope:
//1. create the repository
//2. call repository functions
object carRep = new CarRepository(ambientDbContextLocator);
return carRep.getCarsList("", constraints);
}
}
}
public class CarRepository : URepositoryFramwork.URepository
{
public CarRepository(IAmbientDbContextLocator contextLocator)
{
base.New(contextLocator);
}
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object query = this.DataSet.Where(constraints.FilterExpression);
//.Select(constraints._projection2)
IList<Car> items;
if (constraints == null) {
items = query.ToList();
} else {
items = constraints.ApplyTo(query).ToList();
}
return items;
}
}
Regards.
Here are few points.
You don't need UQueryConstraints at all and you don't need to do any filtering in the UI at all.
I'd ague that the model is something that needs to be returned from the service so I wouldn't create CarModel in the UI layer and then pushed values to it, it doesn't make sense to me.
I'd have a method on the service that request some data and then returns it in some shape or form to the UI.
I'd inject the service to UIView.
I don't understand why there's so much noise around the context and why do you create it in getCarsList it seems like getCarList should be a class called RequestCars and both the repository and the service should be removed in favor of something like depicted in the command pattern.
I don't like the whole abstraction here at all, seems like over engineering to me and who says that IQueryable should be abstracted? it's like abstracting language/framework features whereas you should abstract domain features and only when necessary.
Abstracting 3rd-party frameworks can be fine to some extent but this isn't one of these cases.
I am using the Attribute Routing from MVC5 in my controllers.
Question:
Is there a way to control attribute routing precedence among controllers?
Consider the following
[Route("home/{action=index}/{username?}")]
public class HomeController : Controller
{
[Route("home/index/{username?}", Order = 1)]
[Route("home/{username?}", Order = 2)]
[Route("{username?}", Order = 3)]
public ActionResult Index()
{
// ... bunch of stuff
}
}
Base on the code above, HomeController.Index() action method should be invoked using the following requests:
domain/
domain/{username}
domain/home/
domain/home/{username}
domain/home/index/
domain/home/index/{username}
Second Controller:
[Authorize(Roles = "Member")]
[Route("profile/{action=index}")]
public class ProfileController : Controller
{
[Route("profile")]
public ActionResult Index()
{
}
}
The ProfileController.Index() should be invoked using the following request.
domain/profile
domain/profile/index
The problem
From the examples, if I send domain/profile in the url, an ambiguity exception is thrown. It seems that there is an ambiguity between domain/{username} and domain/profile.
Now, if I used convention-based routing, this would have worked (first match wins). But can it be done in MVC5 Attribute Routing? because I found that a third party library supports precedence among controllers
https://github.com/mccalltd/AttributeRouting/wiki/Controlling-Route-Precedence
routes.MapAttributeRoutes(config =>
{
config.AddRoutesFromController<ProfileController>();
config.AddRoutesFromController<HomeController>();
});
No, it is not possible in ASP.Net MVC 5.2.3 to prioritise controller routes over each other. If multiple match, then the order of the actions is ignored and an exception is thrown.
I have verified this by downloading the source from https://aspnetwebstack.codeplex.com/SourceControl/latest and checking the function GetControllerTypeFromDirectRoute (below). None of the calls made out of this function do anything to prioritise the routes, they are just found and reported back. As you can see, GetControllerTypeFromDirectRoute just throws on a multiple match.
Not great at all, but hopefully this will save someone else some time.
I put a manually mapped route in to avoid this issue.
private static Type GetControllerTypeFromDirectRoute(RouteData routeData)
{
Contract.Assert(routeData != null);
var matchingRouteDatas = routeData.GetDirectRouteMatches();
List<Type> controllerTypes = new List<Type>();
foreach (var directRouteData in matchingRouteDatas)
{
if (directRouteData != null)
{
Type controllerType = directRouteData.GetTargetControllerType();
if (controllerType == null)
{
// We don't expect this to happen, but it could happen if some code messes with the
// route data tokens and removes the key we're looking for.
throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType);
}
if (!controllerTypes.Contains(controllerType))
{
controllerTypes.Add(controllerType);
}
}
}
// We only want to handle the case where all matched direct routes refer to the same controller.
// Handling the multiple-controllers case would put attribute routing down a totally different
// path than traditional routing.
if (controllerTypes.Count == 0)
{
return null;
}
else if (controllerTypes.Count == 1)
{
return controllerTypes[0];
}
else
{
throw CreateDirectRouteAmbiguousControllerException(controllerTypes);
}
}
I'm building a RESTful interface on a Grails 2.1.1 application. How should I implement search operations? I don't want to repeat huge amounts of code, which my current thinking would require.
The server structure is quite normal Grails-MVC: domain classes represent data, controllers offer the interface and services have the business logic. I use command objects for data binding in controllers but not on service methods. The client is a web UI. My goal is to have search URLs like this:
/cars/?q=generic+query+from+all+fields
/cars/?color=red&year=2011
(I'm aware of the debate on the RESTfulness of this kind of URLs with query strings: RESTful URL design for search. While I think this is the best model for my purpose, I'm open to alternatives if they make the API and the implementation better.)
As you can see from the code examples below my problem is with the second kind of URL, the field-specific search. In order to implement this kind of search operation for several domain classes with lots of fields my method signatures would explode.
There probably is a "Groovy way" to do this but I'm still a bit of a n00b in finer Groovy tricks :)
Domain:
class Car {
String color
int year
}
Controller:
class CarsController {
def carService
def list(ListCommand cmd) {
def result
if (cmd.q) {
result = carService.search(cmd.q, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
else {
result = carService.search(cmd.color, cmd.year, cmd.max, cmd.offset, cmd.order, cmd.sort)
}
render result as JSON
}
class ListCommand {
Integer max
Integer offset
String order
String sort
String q
String color // I don't want this field in command
int year // I don't want this field in command
static constraints = {
// all nullable
}
}
// show(), save(), update(), delete() and their commands clipped
}
Service:
class CarService {
List<Car> search(q, max=10, offset=0, order="asc", sort="id") {
// ...
}
List<Car> search(color, year, max=10, offset=0, order="asc", sort="id") {
// ...
}
}
UrlMappings:
class UrlMappings {
static mappings = {
name restEntityList: "/$controller"(parseRequest: true) {
action = [GET: "list", POST: "save"]
}
name restEntity: "/$controller/$id"(parseRequest: true) {
action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
}
}
}
You can get all this parameters from params, like:
result = carService.search(params.color, params.year as Integer, cmd.max, cmd.offset, cmd.order, cmd.sort)
All values of params map are strings, so you should convert it to appropriate data structures in controller (and it's better to check that params.year is actual number)
Update
If you don't want to writer field names, you can pass it as a Map:
resutl = carService.search(params)
where
List<Car> search(Map params)