Domain routes in Play Framework 2.3 - scala

I want to point multiple subdomains and/or root domains to a single Play Framework 2.3 (Scala) application, for example apples.com, bananas.com or buy.bananas.com.
Depending on which domain the request comes in, I want to have different routes.
Ideally, it should work something like this:
GET apples.com #controllers.ApplesController.home
GET bananas.com #controllers.BananasController.home
GET buy.bananas.com #controllers.BananasController.buy
Is there any way to do this in Play Framework 2.3?

I am working in java .Here is the way to do it in java maybe that could help
routes
GET / #controllers.ApplesController.index
GET /apples #controllers.ApplesController.home
GET /bananas #controllers.BananasController.home
GET /buybananas #controllers.BananasController.buy
controller
#With(CheckUrl.class)
public static Result index() {
return ok(index.render("Unable to resolve host."));
}
CheckUrl.java
public class CheckUrl extends play.mvc.Action.Simple {
public F.Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
String host = request().host();
System.out.println("HOST IS "+host);
if (host.equalsIgnoreCase("apples.com")) {
return F.Promise.pure(redirect("/apples"));
}else if (host.equalsIgnoreCase("bananas.com ")){
return F.Promise.pure(redirect("/bananas"));
}else if (host.equalsIgnoreCase("buy.bananas.com")){
return F.Promise.pure(redirect("/buybananas"));
}else{
return delegate.call(ctx);
}
}
I dont know if it the good way of doing it.I have tried it with request().uri() but not with request().host() and that worked for me.Maybe that could help.

Related

How to design multiple ways to invoke REST API

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.

Unit tests for simple REST client [duplicate]

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.

How to have different representations in Play based on an Accept header?

Using Play Framework 2.1, and I'd like to make small variations in behavior based on the request's Accept header. The only thing I can figure to do is this:
conf/routes
GET /widgets controllers.WidgetController.getWidgets()
WidgetController.java
public class LoginController extends Controller {
public static Result loginUser() {
if (ctx().request().headers().get("Accept")[0].equals("application/json")) {
// ... json-specific logic
}
// common processing code
if (ctx().request().headers().get("Accept")[0].equals("application/json")) {
return ok();
} else {
return redirect(...);
}
}
}
Seems very nasty and procedural. Any suggestions on the idiomatic "Play" way to do this?
You can test if request().accepts("application/json“) but checking with if-else for every content-type seems to be the standard way.

MVC5 : Attribute Routing Precedence Among Controllers

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);
}
}

Asp.Net Web API Error: The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'

Simplest example of this, I get a collection and try to output it via Web API:
// GET api/items
public IEnumerable<Item> Get()
{
return MyContext.Items.ToList();
}
And I get the error:
Object of type
'System.Data.Objects.ObjectQuery`1[Dcip.Ams.BO.EquipmentWarranty]'
cannot be converted to type
'System.Data.Entity.DbSet`1[Dcip.Ams.BO.EquipmentWarranty]'
This is a pretty common error to do with the new proxies, and I know that I can fix it by setting:
MyContext.Configuration.ProxyCreationEnabled = false;
But that defeats the purpose of a lot of what I am trying to do. Is there a better way?
I would suggest Disable Proxy Creation only in the place where you don't need or is causing you trouble. You don't have to disable it globally you can just disable the current DB context via code...
[HttpGet]
[WithDbContextApi]
public HttpResponseMessage Get(int take = 10, int skip = 0)
{
CurrentDbContext.Configuration.ProxyCreationEnabled = false;
var lista = CurrentDbContext.PaymentTypes
.OrderByDescending(x => x.Id)
.Skip(skip)
.Take(take)
.ToList();
var count = CurrentDbContext.PaymentTypes.Count();
return Request.CreateResponse(HttpStatusCode.OK, new { PaymentTypes = lista, TotalCount = count });
}
Here I only disabled the ProxyCreation in this method, because for every request there is a new DBContext created and therefore I only disabled the ProxyCreation for this case .
Hope it helps
if you have navigation properties and you do not want make them non virtual, you should using JSON.NET and change configuration in App_Start to using JSON not XML!
after install JSON.NET From NuGet, insert this code in WebApiConfig.cs in Register method
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
If you have navigation properties make them non virtual. Mapping will still work but it prevents the creation of Dynamic Proxy entities which cannot be serialized.]
Not having lazy loading is fine in a WebApi as you don't have a persistent connection and you ran a .ToList() anyway.
I just disabled proxy classes on a per needed basis:
// GET: ALL Employee
public IEnumerable<DimEmployee> Get()
{
using (AdventureWorks_MBDEV_DW2008Entities entities = new AdventureWorks_MBDEV_DW2008Entities())
{
entities.Configuration.ProxyCreationEnabled = false;
return entities.DimEmployees.ToList();
}
}
Add the following code in Application_Start function of Global.asax.cs:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters
.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
This instruct the API to serialize every response into JSON and remove XML responses.
In my case the object being returned had a property within it with a type that did not have an argumentless/default constructor. By adding a zero-argument constructor to that type the object could be serialized successfully.
I had the same problem and my DTO was missing an parameter less constructor.
public UserVM() { }
public UserVM(User U)
{
LoginId = U.LoginId;
GroupName = U.GroupName;
}
First constructor was missing.
I got this error message and it turns out the problem was that I had accidentally set my class to use the same serialized property name for two properties:
public class ResultDto
{
//...
[JsonProperty(PropertyName="DataCheckedBy")]
public string ActualAssociations { get; set; }
[JsonProperty(PropertyName="DataCheckedBy")]
public string ExpectedAssociations { get; set; }
//...
}
If you're getting this error and you aren't sending entities directly through your API, copy the class that's failing to serialize to LINQPad and just call JsonConvert.SerializeObject() on it and it should give you a better error message than this crap. As soon as I tried this it gave me the following error message: A member with the name 'DataCheckedBy' already exists on 'UserQuery+ResultDto'. Use the JsonPropertyAttribute to specify another name.
After disable Proxy Creation, use eager loading (Include()) to load the proxy object.
In my Project EntityCollection returned from the WebApi action method.
Configuration.ProxyCreationEnabled = false not applicable. I have tried the below approach it is working fine for me.
Control Panel.
2.Turn on Windows Features on or off
Choose Internet Information Service
Check all the World Wide Web Components it would be better to check all the components in IIS.
Install the components.
Go to (IIS) type inetmgr in command prompt.
select the published code in the Virtual directory.
Convert into application
Browse it the application.
The answer by #Mahdi perfectly fixes the issue for me, however what I noticed is that if my Newtonsoft.JSON is 11.0 version then it doesn't fix the issue, but the moment I update Newtonsoft.JSON to latest 13.0 it starts working.