I'm having hectic last couple of days due to this problem. I'm trying to pass route data to the view as a matter of navigation. However routeInfo contains no route information. i.e. routeInfo.RouteData.Values.Count = 0. I have another application with the same code which is working fine.
I'm not sure what i'm missing here.
Any help would be really appreciated!!
public ActionResult Index(int type)
{
UrlHelper u = new UrlHelper(this.ControllerContext.RequestContext);
string url = u.Action("Action", "Controller", new { type = type }, Request.Url.Scheme);
Uri uri = new Uri(url);
RouteInfo routeInfo = new RouteInfo(uri, HttpContext.Request.ApplicationPath);
Session["_ReturnURL"] = routeInfo.RouteData.Values;
ViewBag.ReturnURL = Helpers.GetSessionKey("_ReturnURL");
return View();
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true;
routes.IgnoreRoute("elmah.axd");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Controller",
url: "Controller/{type}/{action}/{id}",
defaults: new { controller = "Controller", action = "Action", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "CBlah", action = "ABlah", id = UrlParameter.Optional, returnUrl = "~/Blah2/Blah2" }
);
}
public class RouteInfo
{
public RouteData RouteData { get; private set; }
public RouteInfo(RouteData data)
{
RouteData = data;
}
public RouteInfo(Uri uri, string applicationPath)
{
RouteData = RouteTable.Routes.GetRouteData(new InternalHttpContext(uri, applicationPath));
}
private class InternalHttpContext : HttpContextBase
{
private readonly HttpRequestBase _request;
public InternalHttpContext(Uri uri, string applicationPath)
{
_request = new InternalRequestContext(uri, applicationPath);
}
public override HttpRequestBase Request { get { return _request; } }
}
private class InternalRequestContext : HttpRequestBase
{
private readonly string _appRelativePath;
private readonly string _pathInfo;
public InternalRequestContext(Uri uri, string applicationPath)
{
_pathInfo = uri.Query;
if (String.IsNullOrEmpty(applicationPath) || !uri.AbsolutePath.StartsWith(applicationPath, StringComparison.OrdinalIgnoreCase))
_appRelativePath = uri.AbsolutePath.Substring(applicationPath.Length);
else
_appRelativePath = uri.AbsolutePath;
}
public override string AppRelativeCurrentExecutionFilePath { get { return String.Concat("~", _appRelativePath); } }
public override string PathInfo { get { return _pathInfo; } }
}
}
Route.GetRouteData Definition
public override RouteData GetRouteData(HttpContextBase httpContext)
{
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
if (values == null)
{
return null;
}
RouteData data = new RouteData(this, this.RouteHandler);
if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
{
return null;
}
foreach (KeyValuePair<string, object> pair in values)
{
data.Values.Add(pair.Key, pair.Value);
}
if (this.DataTokens != null)
{
foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
{
data.DataTokens[pair2.Key] = pair2.Value;
}
}
return data;
}
The problem lies in this line:
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
You are appending a query string as the httpContext.Request.PathInfo via:
_pathInfo = uri.Query;
I know for a fact that the Route class does not consider the query string, so appending it to the path is a mistake. There is also some information here that backs up the fact that PathInfo always should return an empty string.
Per MSDN:
For the URL http://www.contoso.com/virdir/page.html/tail, the PathInfo value is /tail.
So, adding a query string is making this line fail (when there is a query string) because _parsedRoute.Match isn't expecting one.
RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
My guess is that one of your applications "works" because it is not being passed a URL with a query string, and therefore matches correctly. But whatever the case, you should return an empty string from PathInfo in your InternalRequestContext class to make it work 100% of the time (unless you have crazy URLs with dots in them, then you may need to do some extra work).
Related
I have a complex record SearchProductsRequest in a GET request that receives the parameters by query (
/v1/products?ids=1,2,3&name=hombre&page=3&pageItems=4&sortField=name&sort=asc ).
app.MapGet(
$"/{ProductCatalogueApi.Version}/products",
(SearchProductsRequest request)
=> ProductApiDelegates.SearchProducts(
request));
In the record, I've implemented the bind async
public static ValueTask<SearchProductsRequest?> BindAsync(HttpContext httpContext, ParameterInfo parameter); and now the parameters from the URL automatically convert the parameters to SearchProductsRequest.
The request is working as intended, but we are using (Swashbuckle -> ) Swagger UI for development.
Swagger UI does not recognize the members from SearchProductsRequest to display them as input boxes. Is there a way to make swagger UI know them and display them so a user consulting the swagger endpoint can pass value through it?
I was hoping to get the following:
Until now, I've only managed to have the fields displayed in swagger if I have all of them in the Map.Get() explicitly.
EDIT:
Adding asked content
Record:
public record SearchProductsRequest
{
public IEnumerable<int>? Ids { get; private set; }
public string? Name { get; private set; }
public PaginationInfoRequest? PaginationInfo { get; private set; }
public SortingInfoRequest? SortingInfo { get; private set; }
public SearchProductsRequest(
IEnumerable<int>? ids,
string? name,
PaginationInfoRequest? PaginationInfo,
SortingInfoRequest? SortingInfo)
{
this.Ids = ids;
this.Name = name;
this.PaginationInfo = PaginationInfo;
this.SortingInfo = SortingInfo;
}
public static ValueTask<SearchProductsRequest?> BindAsync(
HttpContext httpContext,
ParameterInfo parameter)
{
var ids = ParseIds(httpContext);
var name = httpContext?.Request.Query["name"] ?? string.Empty;
PaginationInfoRequest? pagination = null;
SortingInfoRequest? sorting = null;
if (int.TryParse(httpContext?.Request.Query["page"], out var page)
&& int.TryParse(httpContext?.Request.Query["pageItems"], out var pageItems))
{
pagination = new PaginationInfoRequest(page, pageItems);
}
var sortField = httpContext?.Request.Query["sortField"].ToString();
if (!string.IsNullOrEmpty(sortField))
{
sorting = new SortingInfoRequest(
sortField,
httpContext?.Request.Query["sort"].ToString() == "asc");
}
return ValueTask.FromResult<SearchProductsRequest?>(
new SearchProductsRequest(
ids,
name!,
pagination,
sorting));
}
#pragma warning disable SA1011 // Closing square brackets should be spaced correctly
private static int[]? ParseIds(HttpContext httpContext)
{
int[]? ids = null;
var commaSeparatedIds = httpContext?.Request.Query["ids"]
.ToString();
if (!string.IsNullOrEmpty(commaSeparatedIds))
{
ids = commaSeparatedIds
.Split(",")
.Select(int.Parse)
.ToArray() ?? Array.Empty<int>();
}
return ids;
}
#pragma warning restore SA1011 // Closing square brackets should be spaced correctly
}
Delegate:
internal static async Task<IResult> SearchProducts(
ILogger<ProductApiDelegates> logger,
IMapper mapper,
SearchProductsRequest request,
IValidator<SearchProductsRequest> validator,
IProductService productService)
{
using var activity = s_activitySource.StartActivity("Search products");
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
var errors = validationResult.GetErrors();
logger.LogError("Bad Request: {Errors}", errors);
return Results.BadRequest();
}
try
{
logger.LogInformation("Searching product details by name");
var filtersContainer = mapper.Map<SearchProductsFiltersContainer>(request);
var products = await productService.SearchProductsAsync(filtersContainer);
if (products == null)
{
return Results.NotFound();
}
var searchProducts = BuildSearchProducts(mapper, products);
var paginationInfo = await BuildPaginationInfo(filtersContainer, productService);
var response = new SearchProductsResponse(searchProducts, paginationInfo);
return Results.Ok(response);
}
catch (Exception ex)
{
logger.LogError(ex, "Error searching the products");
return Results.Problem();
}
}
In my API I have a Create method in my controller that accepts all of the models fields, but in the method I'm excluding the ID field since on a create it's generated. But in Swagger it's showing the following.
Is there a way for it not to show the following part?
"id": 0
Is a viewmodel how I should go about this?
I tried the following, but can't get it to work.
public class PartVM
{
public string Name { get; set; }
}
public interface IPartService
{
Task<Part> CreatePart(PartVM part);
Task<IEnumerable<Part>> GetParts();
Task<Part> GetPart(int partId);
}
public class PartService : IPartService
{
private readonly AppDbContext _appDbContext;
public PartService(AppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
public async Task<Part> CreatePart(PartVM part)
{
var _part = new Part()
{
Name = part.Name
};
var result = await _appDbContext.Parts.AddAsync(_part);
await _appDbContext.SaveChangesAsync();
return result.Entity;
}
}
Here's my controller.
[Route("api/[controller]")]
[ApiController]
public class PartsController : ControllerBase
{
private readonly IPartService _partService;
public PartsController(IPartService partService)
{
_partService = partService;
}
[HttpPost]
public async Task<ActionResult<Part>> CreatePart(PartVM part)
{
try
{
if (part == null)
return BadRequest();
var _part = new Part()
{
Name = part.Name
};
var createdPart = await _partService.CreatePart(_part);
return CreatedAtAction(nameof(GetPart),
new { id = createdPart.Id}, createdPart);
}
catch (Exception /*ex*/)
{
return StatusCode(StatusCodes.Status500InternalServerError, "Error creating new record in the database");
}
}
I'm getting a build error saying "CS1503 Argument 1: cannot convert from 'MusicManager.Shared.Part' to 'MusicManager.Server.Data.ViewModels.PartVM'".
It's refering to "_part" in this line "var createdPart = await _partService.CreatePart(_part);".
Any help is appreciated, thank you!
you have a CreatePart method which receives a PartVM model, but you are sending a Part Model to it
change your method to this :
public async Task<Part> CreatePart(Part part)
{
var result = await _appDbContext.Parts.AddAsync(_part);
await _appDbContext.SaveChangesAsync();
return result.Entity;
}
How to make this
- user1.domain.com goes to user1/index (not inside area)
- user2.domain.com goes to user2/index (not inside area)
I mean's the
user1.domain.com/index
user2.domain.com/index
Are same view but different data depending on user{0}
using MVC Core 2.2
There're several approaches depending on your needs.
How to make this - user1.domain.com goes to user1/index (not inside area) - user2.domain.com goes to user2/index (not inside area)
Rewrite/Redirect
One approach is to rewrite/redirect the url. If you don't like do it with nginx/iis, you could create an Application Level Rewrite Rule. For example, I create a sample route rule for your reference:
internal enum RouteSubDomainBehavior{ Redirect, Rewrite, }
internal class RouteSubDomainRule : IRule
{
private readonly string _domainWithPort;
private readonly RouteSubDomainBehavior _behavior;
public RouteSubDomainRule(string domain, RouteSubDomainBehavior behavior)
{
this._domainWithPort = domain;
this._behavior = behavior;
}
// custom this method according to your needs
protected bool ShouldRewrite(RewriteContext context)
{
var req = context.HttpContext.Request;
// only rewrite the url when it ends with target doamin
if (!req.Host.Value.EndsWith(this._domainWithPort, StringComparison.OrdinalIgnoreCase)) { return false; }
// if already rewrite, skip
if(req.Host.Value.Length == this._domainWithPort.Length) { return false; }
// ... add other condition to make sure only rewrite for the routes you wish, for example, skip the Hub
return true;
}
public void ApplyRule(RewriteContext context)
{
if(!this.ShouldRewrite(context)) {
context.Result = RuleResult.ContinueRules;
return;
}
var req = context.HttpContext.Request;
if(this._behavior == RouteSubDomainBehavior.Redirect){
var newUrl = UriHelper.BuildAbsolute( req.Scheme, new HostString(this._domainWithPort), req.PathBase, req.Path, req.QueryString);
var resp = context.HttpContext.Response;
context.Logger.LogInformation($"redirect {req.Scheme}://{req.Host}{req.Path}?{req.QueryString} to {newUrl}");
resp.StatusCode = 301;
resp.Headers[HeaderNames.Location] = newUrl;
context.Result = RuleResult.EndResponse;
}
else if (this._behavior == RouteSubDomainBehavior.Rewrite)
{
var host = req.Host.Value;
var userStr = req.Host.Value.Substring(0, host.Length - this._domainWithPort.Length - 1);
req.Host= new HostString(this._domainWithPort);
var oldPath = req.Path;
req.Path = $"/{userStr}{oldPath}";
context.Logger.LogInformation($"rewrite {oldPath} as {req.Path}");
context.Result = RuleResult.SkipRemainingRules;
}
else{
throw new Exception($"unknow SubDomainBehavoir={this._behavior}");
}
}
}
(Note I use Rewrite here. If you like, feel free to change it to RouteSubDomainBehavior.Redirect.)
And then invoke the rewriter middleware just after app.UseStaticFiles():
app.UseStaticFiles();
// note : the invocation order matters!
app.UseRewriter(new RewriteOptions().Add(new RouteSubDomainRule("domain.com:5001",RouteSubDomainBehavior.Rewrite)));
app.UseMvc(...)
By this way,
user1.domain.com:5001/ will be rewritten as (or redirected to) domain.com:5001/user1
user1.domain.com:5001/Index will be rewritten as(or redirected to) domain.com:5001/user1/Index
user1.domain.com:5001/Home/Index will be rewritten as (or redirected to) domain.com:5001/user1//HomeIndex
static files like user1.domain.com:5001/lib/jquery/dist/jquery.min.js won't be rewritten/redirected because they're served by UseStaticFiles.
Another Approach Using IModelBinder
Although you can route it by rewritting/redirecting as above, I suspect what your real needs are binding parameters from Request.Host. If that's the case, I would suggest you should use IModelBinder instead. For example, create a new [FromHost] BindingSource:
internal class FromHostAttribute : Attribute, IBindingSourceMetadata
{
public static readonly BindingSource Instance = new BindingSource( "FromHostBindingSource", "From Host Binding Source", true, true);
public BindingSource BindingSource {get{ return FromHostAttribute.Instance; }}
}
public class MyFromHostModelBinder : IModelBinder
{
private readonly string _domainWithPort;
public MyFromHostModelBinder()
{
this._domainWithPort = "domain.com:5001"; // in real project, use by Configuration/Options
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var req = bindingContext.HttpContext.Request;
var host = req.Host.Value;
var name = bindingContext.FieldName;
var userStr = req.Host.Value.Substring(0, host.Length - this._domainWithPort.Length - 1);
if (userStr == null) {
bindingContext.ModelState.AddModelError(name, $"cannot get {name} from Host Domain");
} else {
var result = Convert.ChangeType(userStr, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
return Task.CompletedTask;
}
}
public class FromHostBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
var has = context.BindingInfo?.BindingSource == FromHostAttribute.Instance;
if(has){
return new BinderTypeModelBinder(typeof(MyFromHostModelBinder));
}
return null;
}
}
Finally, insert this FromHostBinderProvider in your MVC binder providers.
services.AddMvc(otps =>{
otps.ModelBinderProviders.Insert(0, new FromHostBinderProvider());
});
Now you can get the user1.domain.com automatically by:
public IActionResult Index([FromHost] string username)
{
...
return View(view_model_by_username);
}
public IActionResult Edit([FromHost] string username, string id)
{
...
return View(view_model_by_username);
}
The problem after login the Identity cookie not shared in sub-domain
Here my Code where's wrong !!!
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder dataProtectionBuilder;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("ConnectionDb")));
services.AddIdentity<ExtendIdentityUser, IdentityRole>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireLowercase = false;
}).AddEntityFrameworkStores<ApplicationDbContext>(); // .AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options => options.CookieManager = new CookieManager());
services.AddHttpContextAccessor();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IExtendIdentityUser, ExtendIdentityUserRepository>();
services.AddScoped<IItems, ItemsRepository>();
services.AddMvc(otps =>
{
otps.ModelBinderProviders.Insert(0, new FromHostBinderProvider());
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
And this class to sub-domain like that https://user1.localhost:44390/Home/Index
internal class FromHostAttribute : Attribute, IBindingSourceMetadata
{
public static readonly BindingSource Instance = new BindingSource("FromHostBindingSource", "From Host Binding Source", true, true);
public BindingSource BindingSource { get { return FromHostAttribute.Instance; } }
}
public class MyFromHostModelBinder : IModelBinder
{
private readonly string _domainWithPort;
public MyFromHostModelBinder()
{
this._domainWithPort = "localhost:44390"; // in real project, use by Configuration/Options
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var req = bindingContext.HttpContext.Request;
var host = req.Host.Value;
var name = bindingContext.FieldName;
var userStr = req.Host.Value.Substring(0, host.Length - this._domainWithPort.Length);
if (string.IsNullOrEmpty(userStr))
{
bindingContext.ModelState.AddModelError(name, $"cannot get {name} from Host Domain");
}
else
{
var result = Convert.ChangeType(userStr, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
return Task.CompletedTask;
}
}
public class FromHostBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
var has = context.BindingInfo?.BindingSource == FromHostAttribute.Instance;
if (has)
{
return new BinderTypeModelBinder(typeof(MyFromHostModelBinder));
}
return null;
}
}
Using ICookieManager
public class CookieManager : ICookieManager
{
#region Private Members
private readonly ICookieManager ConcreteManager;
#endregion
#region Prvate Methods
private string RemoveSubdomain(string host)
{
var splitHostname = host.Split('.');
//if not localhost
if (splitHostname.Length > 1)
{
return string.Join(".", splitHostname.Skip(1));
}
else
{
return host;
}
}
#endregion
#region Public Methods
public CookieManager()
{
ConcreteManager = new ChunkingCookieManager();
}
public void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
{
options.Domain = RemoveSubdomain(context.Request.Host.Host); //Set the Cookie Domain using the request from host
ConcreteManager.AppendResponseCookie(context, key, value, options);
}
public void DeleteCookie(HttpContext context, string key, CookieOptions options)
{
ConcreteManager.DeleteCookie(context, key, options);
}
public string GetRequestCookie(HttpContext context, string key)
{
return ConcreteManager.GetRequestCookie(context, key);
}
#endregion
}
I want to take a few post query parameters from an API i have and create a new entry. I wanted to do this with in the method with out needing to load context or something.
namespace fais.printing_services.Controllers
{
[Produces("application/json")]
[Route("api/[controller]/[action]")]
public class printController : Controller
{
private readonly IHostingEnvironment _appEnvironment;
public printController(IHostingEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
/**/
[HttpPost]
public IActionResult request(string id="test_default", string url = "", string html = "")
{
print_job _print_job = new print_job();
_print_job.html = html;
_print_job.options = options; //json object string
_print_job.url = url;
using (ApplicationDbContext db = new ApplicationDbContext())
{
db.print_job.Add(_print_job);
db.SaveChanges();
}
return Json(new
{
save = true
});
}
}
}
I just want to be able create a new print_job entry and save it when the API is called and return a json response.
Add ApplicationDbContext to controller constructor, it will be injected automatically (if your Startup.cs is like recommeneded):
private readonly IHostingEnvironment _appEnvironment;
private readonly ApplicationDbContext _db;
public printController(IHostingEnvironment appEnvironment, ApplicationDbContext db)
{
_appEnvironment = appEnvironment;
_db = db;
}
[HttpPost]
public IActionResult request(string id="test_default", string url = "", string html = "")
{
var _print_job = new print_job()
{
html = html,
options = options,
url = url,
}
_db.print_job.Add(_print_job);
_db.SaveChanges();
return Json(new { save = true });
}
I'm attempting to POST an object to a WebAPI controller that accepts a complex type as the parameter but the parameter recived is null. Any ideas why? The request is hitting the WebAPI method properly and the parameter is null.
Model :
namespace DMAX.BLL.MASReports.Models
{
public class StatsCriteria
{
#region Constructors and Methods
public StatsCriteria()
{
}
#endregion
#region Properties and Fields
private string _masnum;
private string _notchosen;
private int _currentPage = 1;
private bool _isPrint = false;
private bool _isEmail = false;
private bool _isAjax = false;
public string Masnums { get {
if (!string.IsNullOrEmpty(_masnum)) {
_masnum = _masnum.Replace("'", "");
if (!string.IsNullOrEmpty(NotChosen)) {
string[] notchosenlist = NotChosen.Split(',');
foreach (var notchosen in notchosenlist) {
_masnum = this.RemoveNotChosen(_masnum, notchosen);
}
}
return _masnum;
}
return null;
}
set { _masnum = value; }
}
public string AgentId { get; set; }
public string LicenseNum { get; set; }
public string AgentFullName { get; set; }
public string HeaderName { get; set; }
#endregion
}
}
}
Here's the code at client : [ The StatsCriteria is part of the project BLL and I am referencing it in MASReports project]
namespace MASReports.Controllers
{
public ActionResult Reports(StatsCriteria criteria)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria.Masnums.ToString()).Result;
return View("CMAReport", response);
}
}
Here's the signature for my controller in Webapi.
[ The StatsCriteria is part of the project BLL and I have a reference to that project in ReportsAPI project]
[ The CMAReportVM, CMAReport are part of the project BLL and I have a reference to BLL project in ReportsAPIproject]
namespace ReportsAPI.Controllers
{
public class ReportsController : ApiController
{
[HttpPost]
public CMAReportVM Reports([FromBody] StatsCriteria criteria)
{
var cmaReport = Service3.GetCMAReport(criteria.Masnums);
//Create Map to enable mapping business object to View Model
Mapper.CreateMap<CMAReport, CMAReportVM>();
// Maps model to VM model class
var cmaVM = Mapper.Map<CMAReport, CMAReportVM>(cmaReport);
reutn cmaVM;
}
}
}
// and here's my routing:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
and here's my Golbal.asax of Web api
namespace ReportsAPI
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new QueryStringMapping("json", "true", "application/json"));
}
}
}
You are posting a string:
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria.Masnums.ToString()).Result;
Your controller method expect a StatsCriteria object. Either change the signature of your controller method to accept a string or change the post call.
Assuming that the controller method signature is correct the post should be something like this:
var response = client.PostAsJsonAsync("http://localhost:52765/api/reports", criteria).Result;
If this doesn't help I recommend to use fiddler to check what the message looks like when you post it.