I have created a web api with two parameters. I want to test it from url that its working or not. I have tried but not able to call. My code is.
API routing...
public static void Register(HttpConfiguration config)
{
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
[HttpGet]
public List<Library> GetLibrary(string DataType, int DataId)
{
List<Library> LibraryList = new List<Library>();
if (DataType == "Course")
{
using (ICA.LMS.Service.Models.Entities dbCourse = new Models.Entities())
{
LibraryList = (from c in dbCourse.COURSELIBRARies
where c.LIBITEMID == DataId
select new Library { Id = c.LIBITEMID, Name = c.GROUPNAME, Desc = c.DESCRIPTION }).ToList();
}
}
return LibraryList;
}
Url which i am putting in browser.
http://localhost:1900/api/librarybyid/?DataType='Course'&DataId=1
Result i am getting...
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:1900/api/librarybyid/?DataType='Course'&DataId=1'.
</Message>
<MessageDetail>
No type was found that matches the controller named 'librarybyid'.
</MessageDetail>
</Error>
Related
I am using Owin to build a self hosted server which support both file requests and web api. But the output for web api requests are always in xml format. How can I configure owin to output in json?
The code is as below:
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseFileServer(new FileServerOptions()
{
RequestPath = PathString.Empty,
FileSystem = new PhysicalFileSystem(#".\files")
});
// set the default page
app.UseWelcomePage(#"/index.html");
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
app.UseWebApi(config);
}
}
I have found the answer myself. All have to do is to add a json formatter as below:
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
If need to convert enum to string add StringEnumConverter to the settings.
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter());
I inherited some ASP.Net MVC code and am tasked with adding some new features. I am a complete beginner using ASP.Net MVC and come from a background of mainly using Web Forms.
I added a new controller (ApiController) and I added the following actions to it:
// GET: /Api/Index
public string Index()
{
return "API Methods";
}
// GET: /Api/DetectionActivity
public JsonResult DetectionActivity()
{
var detections = from d in db.Detections
orderby DbFunctions.TruncateTime(d.CreationTime)
group d by DbFunctions.TruncateTime(d.CreationTime) into g
select new { date = g.Key, count = g.Count() };
ViewBag.DetectionCounts = detections.ToList();
return Json(detections, JsonRequestBehavior.AllowGet);
}
My RouteConfig.cs has the following registered routes.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
This looks like the tutorials I've been reading but it's not working and I'm probably missing something.
If I go to localhost:21574/api I see the output from the Index() action, "API Methods".
If I go to localhost:21574/api/DetectionActivity it throws a 404 with the following data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'DetectionActivity'."
}
I'm thinking there is something I need to do that I'm not.
Any suggestions on what to do next?
Update 1
I tried this with my RouteConfig.cs
routes.MapRoute(name: "ApiController",
url: "{controller}/{action}"
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
These were my results:
If I go to localhost:21574/api I see the output from the Index() action, "API Methods". Same as before.
If I go to localhost:21574/api/DetectionActivity it throws a 404 with the following data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'DetectionActivity'."
}
Same as before.
If I go to localhost:21574/Api/Api/DetectionActivity it throws a 404 with this data in the response:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:21574/Api/Api/DetectionActivity'.",
"MessageDetail":"No type was found that matches the controller named 'Api'."
}
Now it's saying it can't find a controller named "Api".
from your Route Config
the URL should be: localhost:21574/Api/Dashboard/DetectionActivity
or if you really need localhost:21574/Api/DetectionActivity (not recommended)
change your Register method in WebApiConfig class to
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = RouteParameter.Optional }
);
Using C# web api, i created a web api that update the information.
// Update
// URI PUT + api/Gender/id
[HttpPut]
public IHttpActionResult PutGender(int id, [FromBody] Gender g)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var query = (from gen in genders
where gen.Id == id
select gen).FirstOrDefault();
if (query == null)
{
return BadRequest("wrong");
}
query.Description = g.Description;
query.LanguageId = g.LanguageId;
query.InternalCode = g.InternalCode;
query.isActive = g.isActive;
query.UpdatedAt = DateTime.Now;
return StatusCode(HttpStatusCode.OK);
}
I am testing through fiddler and this is how i ran the thing:
URL http://localhost:49625/API/PutGender
Action PUT
Request Header
Content-Type: application/json
User-Agent: Fiddler
Host: localhost:49625
Content-Length: 86
RequestBody
{"Id":1,"Description":"Undecided","LanguageId":null,"InternalCode":0,"isActive":false}
The URL is missing the name of the controller. For example, if your controller is called GenderController then the URL should be http://localhost:49625/API/Gender/PutGender
For other people that might run into this problem (myself included). Also check your route configuration. If you are using the default route configuration:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
There is no need to specify the "action part" of your url (PutGender), "PUT http://myhost/api/gender/1" should be fine. Also, if your method is named Put...Something, I am pretty sure you can omit the [HttpPut] attribute from the method signature aswell.
I am creating my first ASP.NET web API. I am trying to follow the standard REST URLs. My API would return the search result records. My URL should be –
../api/categories/{categoryId}/subcategories/{subCategoryId}/records?SearchCriteria
I am planning to use oData for searching and Basic / Digest Authentication over IIS. My problem is in the nested resources. Before I return the search results, I need to check whether the user has access to this category and sub category.
Now I created my Visual Studio 2012 – MVC4 / Web API project to start with. In the App_Start folder, there are 2 files that I believe are URL and order of resource related.
1.RouteConfig.cs
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
2.WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
With this model, it works fine if my URL is ../api/records?SearchCriteria but it is not my URL design mentioned above. I understand that I have to do little more reading but so far not able to find the correct article. Need your advice on how to achieve my URL and what changes are needed in these 2 files. Alternatively, are there some other configuration that I am missing here? Thanks in advance.
Asp.net Web API 2 provides Attribute routing out of the box. You can define Route on individual action method or at global level.
E.g:
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
You can also set a common prefix for an entire controller by using the [RoutePrefix] attribute:
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET api/books
[Route("")]
public IEnumerable<Book> Get() { ... }
// GET api/books/5
[Route("{id:int}")]
public Book Get(int id) { ... }
}
You can visit this link for more information on Attribute routing in Web API 2.
Assuming you have a controller named categories, Your WebApiConfig.cs could have a route like this to match your desired url (I would personally leave the /records portion off):
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{categoryId}/subcategories/{subCategoryId}",
defaults: new { controller = "categories", categoryId = somedefaultcategory,
subCategoryId = RouteParameter.Optional }
);
and a method could look like this:
// search a single subcategory
public IQueryable<SearchRecord> Get(int categoryId, int subCategoryId = 0, string SearchCriteria = "")
{
// test subCategoryId for non-default value to return records for a single
// subcategory; otherwise, return records for all subcategories
if (subCategoryId != default(int))
{
}
}
But, what if you want to also return just the categories and not subcategories? You'd need an additional route after the first one that is more generic:
config.Routes.MapHttpRoute(
name: "Categories",
routeTemplate: "api/{controller}/{categoryId}",
defaults: new { controller = "categories", categoryId = RouteParameter.Optional }
);
with two methods like:
// search a single category
public IQueryable<SearchRecord> Get(int categoryId, string SearchCriteria = "")
{
}
// search all categories
public IQueryable<SearchRecord> Get(string SearchCriteria = "")
{
}
I get an "internal server error" (status code 500) when testing an ASP.NET Web API controller in an in-memory test.
[TestFixture]
public class ValuesControllerTest
{
private HttpResponseMessage response;
[TestFixtureSetUp]
public void Given()
{
var config = new HttpConfiguration
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
};
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller = typeof(ValuesController).Name.Replace("Controller", string.Empty), id = RouteParameter.Optional }
);
//This method will cause internal server error but NOT throw any exceptions
//Remove this call and the test will be green
ScanAssemblies();
var server = new HttpServer(config);
var client = new HttpClient(server);
response = client.GetAsync("http://something/api/values/5").Result;
//Here response has status code 500
}
private void ScanAssemblies()
{
PluginScanner.Scan(".\\", IsApiController);
}
private bool IsApiController(Type type)
{
return typeof (ApiController).IsAssignableFrom(type);
}
[Test]
public void Can_GET_api_values_5()
{
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
public static class PluginScanner
{
public static IEnumerable<Type> Scan(string directoryToScan, Func<Type, bool> filter)
{
var result = new List<Type>();
var dir = new DirectoryInfo(directoryToScan);
if (!dir.Exists) return result;
foreach (var file in dir.EnumerateFiles("*.dll"))
{
result.AddRange(from type in Assembly.LoadFile(file.FullName).GetTypes()
where filter(type)
select type);
}
return result;
}
}
I have configured Visual Studio to break when any .Net exception is thrown. Code is not stopped at any exception nor can I find any exception details in the response.
What should I do to see what's causing the "internal server error"?
The exception is in Response.Content
if (Response != null && Response.IsSuccessStatusCode == false)
{
var result = Response.Content.ReadAsStringAsync().Result;
Console.Out.WriteLine("Http operation unsuccessful");
Console.Out.WriteLine(string.Format("Status: '{0}'", Response.StatusCode));
Console.Out.WriteLine(string.Format("Reason: '{0}'", Response.ReasonPhrase));
Console.Out.WriteLine(result);
}
You need to add a route so that it looks something like this:
var config = new HttpConfiguration()
{
IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always
};
config.Routes.MapHttpRoute(
name: "default",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller = "Home", id = RouteParameter.Optional });
var server = new HttpServer(config);
var client = new HttpClient(server);
HttpResponseMessage response = client.GetAsync("http://somedomain/api/product").Result;
Btw, in the latest bits you get a 404 Not Found as you would expect.
Henrik
It sounds like you might have already found your answer, but that wasn't quite it for me so I want to add this for others with my issue.
To start out, it seems to be an issue with the new MVC 4 formatters. Setting any of the error policy flags will not work (IncludeErrorDetailPolicy, CustomErrors, etc), these formatters are ignoring them and just returning and empty "internal server error" 500.
I found this out by eventually overloading the formatters and checking their responses for errors:
public class XmlMediaTypeFormatterWrapper : XmlMediaTypeFormatter
{
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
{
var ret = base.WriteToStreamAsync(type, value, stream, contentHeaders, transportContext);
if (null != ret.Exception)
// This means there was an error and ret.Exception has all the error message data you would expect, but once you return below, all you get is a blank 500 error...
return ret;
}
}
For now I am using Xml and Json formatter wrappers that simply look for ret.Exception and capture it so I at least have the data if a 500 happens. I couldn't really find an elegant way to make the error actually show up in the html response since Task.Exception is already set and this SHOULD be all the is required to pass the exception along.