RESTful api versioning and grouping in doc from Swagger with multiple endpoints - c#

I am trying to implement the version options on a MVC dotnet Core app that has API endpoint on it.
The set up i am after is like this
--AiM api
|_v1
|_v2
--RMS api
|_v1
I have it mostly working but the items on v1 are not showing up on v2. The output is like so
But when we get to the version 2 on the AiM v2 endpoint I only the one item
Which is not what i was expecting
I have made a test to get each one showing on its different pages in swagger like this
In controller
[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Departments" })]
[HttpPost("departments/delete/{id}")]
public IActionResult departments(string foo)
{
return Json(new
{
results = "edited"
});
}
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/delete/{id}")]
public IActionResult contact_delete(string foo)
{
return Json(new
{
results = "edited"
});
}
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/activate/{id}")]
public IActionResult contact_activate(string foo)
{
return Json(new
{
results = "edited"
});
}
}
[ApiVersion("2.0")]
[ApiExplorerSettings(GroupName = "aim_v2")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v2Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/delete/{id}")]
public IActionResult contact_delete(string foo)
{
return Json(new
{
results = "edited"
});
}
}
[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/")]
public class aim_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Rooms" })]
[HttpPost("rooms")]
public IActionResult rooms(string foo)
{
return Json(new
{
results = "foo"
});
}
[SwaggerOperation(Tags = new[] { "AiM Buildings" })]
[HttpPost("buildings/rooms/{id}")]
public IActionResult building_rooms(string foo)
{
return Json(new
{
results = "foo"
});
}
[SwaggerOperation(Tags = new[] { "AiM Rooms" })]
[HttpPost("rooms/{id}")]
public IActionResult room(string foo)
{
return Json(new
{
results = "foo"
});
}
}
// set up as just a new endpoint (NOTE: in different controller)
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "rms_v1")]
[Route("rms/v{version:apiVersion}/")]
public class rms_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "RMS Orders" })]
[HttpPost("set_order/{id}")]
public IActionResult set_order(string foo)
{
return Json(new
{
results = "foo"
});
}
}
And in the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting(options => options.LowercaseUrls = true);
services.AddMvc();
services.AddApiVersioning(options => {
options.AssumeDefaultVersionWhenUnspecified = true ;
options.DefaultApiVersion = new ApiVersion(new DateTime(2016, 7, 1));
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("aim_v1", new Info
{
Version = "aim/v1",
Title = "WSU HTTP API"
});
c.SwaggerDoc("aim_v2", new Info
{
Version = "aim/v2",
Title = "WSU HTTP API v2"
});
c.SwaggerDoc("rms_v1", new Info
{
Version = "rms/v1",
Title = "WSU HTTP API"
});
//Set the comments path for the swagger json and ui.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var xmlPath = Path.Combine(basePath, "project.in.bin.def.xml");
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(o =>
{
o.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Host = httpReq.Host.Value);
o.RouteTemplate = "doc/{documentName}/scheme.json";
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "docs";
c.SwaggerEndpoint("/doc/aim_v1/scheme.json", "AiM v1.0.0");
c.SwaggerEndpoint("/doc/rms_v1/scheme.json", "Rms v1.0.0");
c.SwaggerEndpoint("/doc/aim_v2/scheme.json", "AiM v2.0.0");
});
}
And in the index.html for the swagger ui doc template file has
<script type="text/javascript">
window.JSConfig = JSON.parse('{"SwaggerEndpoints":[{"Url":"/doc/aim_v1/scheme.json","Description":"AiM v1.0.0"},{"Url":"/doc/aim_v2/scheme.json","Description":"AiM v2.0.0"},{"Url":"/doc/rms_v1/scheme.json","Description":"RMS v1.0.0"}],"BooleanValues":["false","true"],"DocExpansion":"list","SupportedSubmitMethods":["get","post","put","delete","patch"],"OnCompleteScripts":[],"OnFailureScripts":[],"ShowRequestHeaders":false,"JsonEditor":false,"OAuth2ClientId":"your-client-id","OAuth2ClientSecret":"your-client-secret-if-required","OAuth2Realm":"your-realms","OAuth2AppName":"your-app-name","OAuth2ScopeSeparator":" ","OAuth2AdditionalQueryStringParams":{}}');
$(function () {
hljs.configure({
highlightSizeThreshold: 5000
});
// Pre load translate...
if(window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
window.swaggerUi = new SwaggerUi({
url: "/doc/aim_v1/scheme.json",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
initOAuth({
clientId: "ffff==",
clientSecret: "bbbb",
realm: "wsu-api",
appName: "wsu-api-broker",
scopeSeparator: " ",
additionalQueryStringParams: {}
});
}
if(window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
_.each(JSConfig.OnCompleteScripts, function (script) {
$.getScript(script);
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: false,
jsonEditor: false,
defaultModelRendering: 'schema',
showRequestHeaders: false
});
window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
In order to get the items on the different endpoints I used the [ApiExplorerSettings(GroupName = "aim_v1")] on the classes and matched them up in the Startup.cs and index.html files. At this point I am unsure where to make my edit to get all of the [ApiVersion("1.0")] items show on the [ApiVersion("2.0")] as I think the ApiExplorerSettings GroupName is what it locking this up.

To integrate everything smoothly, you also need to add the official API Explorer package for API Versioning. This will collate all of the API version information for you in a way that Swagger will understand. The official Swagger/Swashbuckle integration wiki topic has additional details and examples.
The setup will look like:
public void ConfigureServices( IServiceCollection services )
{
// note: this option is only necessary when versioning by url segment.
// the SubstitutionFormat property can be used to control the format of the API version
services.AddMvcCore().AddVersionedApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
} );
services.AddMvc();
services.AddApiVersioning();
services.AddSwaggerGen(
options =>
{
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
foreach ( var description in provider.ApiVersionDescriptions )
{
options.SwaggerDoc( description.GroupName, CreateInfoForApiVersion( description ) );
}
options.IncludeXmlComments( XmlCommentsFilePath );
} );
}
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider )
{
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
foreach ( var description in provider.ApiVersionDescriptions )
{
options.SwaggerEndpoint( $"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant() );
}
} );
}
static string XmlCommentsFilePath
{
get
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var fileName = typeof( Startup ).GetTypeInfo().Assembly.GetName().Name + ".xml";
return Path.Combine( basePath, fileName );
}
}
static Info CreateInfoForApiVersion( ApiVersionDescription description )
{
var info = new Info()
{
Title = $"Sample API {description.ApiVersion}",
Version = description.ApiVersion.ToString(),
Description = "A sample application with Swagger, Swashbuckle, and API versioning.",
Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei#somewhere.com" },
TermsOfService = "Shareware",
License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" }
};
if ( description.IsDeprecated )
{
info.Description += " This API version has been deprecated.";
}
return info;
}

A full working answer is in that question:
Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api
As the author said, the DocInclusionPredicate in AddSwaggerGen in the ConfigureServices is doing the trick to map the proper controller to the wanted swagger file.

Related

Customize version number in Swagger

I'm using the default swagger implementation with my dotnet core webapi.
I would like the actual version number to appear instead of the template whenever version number is changing from the "select a definition" dropdown on top right corner.
So, for example when v2 is selected from the dropdown, it should appear like this
instead of this,
Any clues how this can be done?
You can use Microsoft.AspNetCore.Mvc.Versioning and custom swagger filter.
Create custom document filter;
public class ReplaceVersionWithExactValueInPathFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var paths = swaggerDoc.Paths;
swaggerDoc.Paths = new OpenApiPaths();
foreach (var path in paths)
{
var key = path.Key.Replace("v{version}", swaggerDoc.Info.Version);
var value = path.Value;
swaggerDoc.Paths.Add(key, value);
}
}
}
Add versioning and swagger services to your service collection;
services.AddApiVersioning(config =>
{
// Specify the default API Version
config.DefaultApiVersion = new ApiVersion(1, 0);
// If the client hasn't specified the API version in the request, use the default API version number
config.AssumeDefaultVersionWhenUnspecified = true;
// Advertise the API versions supported for the particular endpoint
config.ReportApiVersions = true;
});
//Adding swagger services
services.AddSwaggerGen(options =>
{
//other configurations
options.SwaggerDoc("v1.0", new OpenApiInfo
{
Version = "v1.0",
Title = "Your Api Title",
Description = "Your Api Description",
TermsOfService = new Uri("https://yourcompany.com"),
Contact = new OpenApiContact { Name = "Your Company", Email = "info#yourcompany.com", Url = new Uri("https://yourcompany.com") },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
});
options.SwaggerDoc("v2.0", new OpenApiInfo
{
Version = "v2.0",
Title = "Your Api Title",
Description = "Your Api Description",
TermsOfService = new Uri("https://yourcompany.com"),
Contact = new OpenApiContact { Name = "Your Company", Email = "info#yourcompany.com", Url = new Uri("https://yourcompany.com") },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
});
options.DocumentFilter<ReplaceVersionWithExactValueInPathFilter>();
//other configurations
});
Register Swagger and SwaggerUI middleware;
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
c.RouteTemplate = "api/docs/{documentName}/docs.json";
}).UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/api/docs/v1.0/docs.json", "v1.0");
c.SwaggerEndpoint($"/api/docs/v2.0/docs.json", "v2.0");
});
Then configure your controller;
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class TestsController : ControllerBase
{
[HttpPost("Test")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "v1.0")]
public IActionResult TestV1()
{
return Ok();
}
[HttpPost("Test")]
[ApiVersion("2.0")]
[ApiExplorerSettings(GroupName = "v2.0")]
public IActionResult TestV2()
{
return Ok();
}
}
If you want to remove version header from your document you should use this;
public class VersionHeaderFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters != null)
{
var versionParameter = operation.Parameters.SingleOrDefault(p => p.Name == "version");
if (versionParameter != null)
operation.Parameters.Remove(versionParameter);
}
}
}
services.AddSwaggerGen(options =>
{
//other configurations
options.OperationFilter<VersionHeaderFilter>();
//other configurations
});
This should work.

OData ASPNetCore CamelCase

I am struggling to find documentation on camelCase feature for .net 6 vs 8.0.6 of Microsoft.AspNetCore.OData
https://github.com/OData/AspNetCoreOData/issues/13#issuecomment-1013384492
The issue is when you query directly it's fine.
But when you use any functionality it breaks
Any ideas?
Code for Config in Program.cs
builder.Services.AddControllersWithViews().AddOData(options =>
{
options.Select().Filter().Expand().Count().SetMaxTop(100).OrderBy();
});
Code for Endpoint
[HttpGet]
[EnableQuery()]
public async Task<IEnumerable<Warehouse>> Warehouses()
{
return _context.Warehouses;
}
Tired
static IEdmModel GetModel()
{
var builder1 = new ODataConventionModelBuilder();
builder1.EnableLowerCamelCase();
builder1.EntitySet<Warehouse>("warehouses");
builder1.EntitySet<Company>("companies");
return builder1.GetEdmModel();
}
builder.Services.AddControllersWithViews().AddOData(options =>
{
options.Select().Filter().Expand().Count().SetMaxTop(100).OrderBy();
options.AddRouteComponents(GetModel());
});
Until this is fix here's what I did with typescript/javascript to transform the response so that it can be mapped to swagger open api objects
return this.httpClient.request<any[]>('get', `${window.origin}/odata/${path}/?${odataQuery}`,
{
headers: this.headers
}
).pipe(map((things) => {
return things.map((thing) => {
return this.util.objectToCamel(thing);
});
}));
objectToCamel(thing: any): any{
const obj: any = {};
if (!thing)
return null;
Object.keys(thing).forEach((key) => {
if (Array.isArray(thing[key])) {
thing[key] = (<any[]>thing[key]).map((thx) => {
return this.objectToCamel(thx);
});
} else if (typeof thing[key] === "object")
thing[key] = this.objectToCamel(thing[key])
obj[key.substr(0, 1).toLowerCase() + key.substr(1)] = thing[key];
});
return obj;
}

How to get in Asp.Net Core a request POST from Angular

In Asp.NET MVC I was using Filters according below: (It works fine)
public class TracerAttribute : ActionFilterAttribute
{
public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
var path = ConfigurationManager.AppSettings["QueueAddress"];
var queue = new MessageQueue(path);
queue.DefaultPropertiesToSend.Recoverable = true;
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var body = TraceMessageHelper.BuildBody(actionContext, assembly);
try
{
var label = ConfigurationManager.AppSettings["MessageLabel"] + body.Timestamp.ToString("yyyyMMddHHmmss");
var message = new Message
{
Formatter = new XmlMessageFormatter(new Type[] { typeof(TraceMessage) }),
Label = label,
Body = body
};
queue.Send(message);
}
catch (Exception e)
{
var logger = LogManager.GetLogger("LogInFile");
logger.Warn(e, LogMessageHelper.FormatRequest("TRACE SEND FAILED", actionContext.Request));
if (body != null)
{
var tracerlogger = LogManager.GetLogger("TracerInFile");
tracerlogger.Info(JsonConvert.SerializeObject(body));
}
}
queue.Close();
});
}
}
}
In Asp.NET CORE I'm using Filters according below: (It doesn't Work)
public class TracerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
{
var path = "FormatName:Direct=TCP:013cdnt2305\\private$\\TracerQueue";
var queue = new MessageQueue(path);
queue.DefaultPropertiesToSend.Recoverable = true;
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var body = TraceMessageHelper.BuildBody(actionContext, assembly);
try
{
var label = "APR_USER_20191018132324";
var message = new Message
{
Formatter = new XmlMessageFormatter(new Type[] { typeof(TraceMessage) }),
Label = label,
Body = body
};
queue.Send(message);
}
catch (Exception e)
{
HttpRequestMessageFeature hreqmf = new HttpRequestMessageFeature(actionContext.HttpContext);
var logger = LogManager.GetLogger("LogInFile");
logger.Warn(e, LogMessageHelper.FormatRequest("TRACE SEND FAILED", hreqmf.HttpRequestMessage));
if (body != null)
{
var tracerlogger = LogManager.GetLogger("TracerInFile");
tracerlogger.Info(JsonConvert.SerializeObject(body));
}
}
queue.Close();
}
}
}
}
In ASP.net Core I need to do change using only one parameter
In Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc(config =>
{
config.Filters.Add(new TracerAttribute());
});
//use our filter as a service type on the Action or Controller level
//services.AddScoped<TracerAttribute>();
}
In ASP.NET MVC (Works Fine)
In ASP.NET CORE(Doesn't work)
Angular return for both:
Anyone help me how to fix it?
If the angular app post json data(Content-Type: application/json) to .net core server side , you can get the values via [FromBody] :
[TracerAttribute]
[HttpPost]
public IActionResult MyAction([FromBody]Mymodel mymodel)
{
}
When action filter is calling , the body stream already has been read and [FromBody] model has been populated. Then you can get the data via :
public class TracerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
var modelValues = actionContext.ActionArguments["mymodel"] as Mymodel ;
}
}

How to configure MultipleApiVersions in Swashbuckle using aspnet ApiVersioning

How do I configure swashbuckle to work with Aspnet API verisoning?
https://github.com/Microsoft/aspnet-api-versioning
In my Startup.cs I have the following code to initialize attribute based routing, api versioning, and swagger.
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
config.AddApiVersioning();
config.EnableSwagger(c =>
{
c.MultipleApiVersions(
(apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
(vc) =>
{
vc.Version("v1", "Swashbuckle Dummy API V1");
vc.Version("v2", "Swashbuckle Dummy API V2");
});
}
public static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
{
var versionConstraint = (apiDesc.Route.Constraints.ContainsKey("apiVersion"))
? apiDesc.Route.Constraints["apiVersion"] as RegexRouteConstraint
: null;
return (versionConstraint == null)
? false
: versionConstraint.Pattern.Split('|').Contains(targetApiVersion);
}
When the ResolveVersionSupportByRouteConstraintmethod fires the route template includes the literal api string "api/v{version}/users" My users controller is decorated with [ApiVersion("1.0")] and I have the following route defined [Route("api/v{version:apiVersion}/users")]. When I hit api/v1/users with postman the call works, but i cannot figure out how to get this working with Swashbuckle/Swagger.
I want my swagger documentation to look like the example for the asp.net core api boilerplate, except I am using Owin with the owin startup class instead of .net core: https://github.com/ASP-NET-Core-Boilerplate/Templates/blob/master/MVC%206%20API.md
You can find examples here
This is how I done this in startup of self hosted owin app:
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
//configure your app
config.AddApiVersioning(o =>
{
o.ReportApiVersions = true;
o.ApiVersionReader = new UrlSegmentApiVersionReader();
});
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap = { ["apiVersion"] = typeof(ApiVersionRouteConstraint) }
};
config.MapHttpAttributeRoutes(constraintResolver);
SwaggerConfiguration.Configure(config);
appBuilder.UseWebApi(config);
}
Configuration of swagger is very simple, main part here VersionedApiExplorer(ensure, that you passed right groupnameformat of your api, my format was v1, v2, etc):
public static class SwaggerConfiguration
{
public static void Configure(HttpConfiguration config)
{
var apiExplorer = config.AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'V");
config.EnableSwagger(
swagger =>
{
swagger.MultipleApiVersions(
(apiDesc, targetApiVersion) => apiDesc.GetGroupName() == targetApiVersion,
versionBuilder =>
{
foreach (var group in apiExplorer.ApiDescriptions)
{
var description = "";
if (group.IsDeprecated) description += "This API deprecated";
versionBuilder.Version(group.Name, $"Service API {group.ApiVersion}")
.Description(description);
}
});
swagger.DocumentFilter<VersionFilter>();
swagger.OperationFilter<VersionOperationFilter>();
})
.EnableSwaggerUi(cfg => cfg.EnableDiscoveryUrlSelector());
}
In controller add attributes ApiVersion and RoutePrefix
[ApiVersion("1")]
[RoutePrefix("api/v{version:apiVersion}/history")]
public class HistoryController: ApiController
If you confused about VersionFilter and VersionOperationFilter there is code for that. This filters modifies resulting routes and parameters in swagger(without that your route will look like /v{version}/{actionName} and contain required parameter version)
public class VersionFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
swaggerDoc.paths = swaggerDoc.paths
.ToDictionary(
path => path.Key.Replace("v{version}", swaggerDoc.info.version),
path => path.Value
);
}
}
public class VersionOperationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var version = operation.parameters?.FirstOrDefault(p => p.name == "version");
if (version != null)
{
operation.parameters.Remove(version);
}
}
}
I think the ResolveVersionSupportByRouteConstraint method might be wrong, see: https://github.com/domaindrivendev/Swashbuckle/issues/197#issuecomment-75288894
Be aware you can achieve version substition by appropriate configuration of ApiExplorer.
See code fragment:
var apiExplorer = config.AddVersionedApiExplorer(config =>
{
config.SubstituteApiVersionInUrl = true;
This will replace version in paths and remove version parameter from operation parameters.

ASP.NET Self hosted Web API with IE – This page cannot be displayed

I am trying to invoke my service locally but IE and Edge are not able to find it.
Below is the code snippet I have and my console app is working without any error.
Program.cs
public class Program
{
static void Main()
{
string baseAddress = "http://127.0.0.1:8080/";
// Start OWIN host
using (WebApp.Start(url: baseAddress))
{
Console.WriteLine("Service Listening at " + baseAddress);
System.Threading.Thread.Sleep(-1);
}
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
WebController.cs
public class Web
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class WebController : ApiController
{
Web[] websites = new Web[]
{
new Web { Id = 1, Name = "XYZ", Description = "XYZ"},
new Web { Id = 2, Name = "ABC", Description = "ABC"}
};
// GET api/Web
public IEnumerable Get()
{
return websites;
}
// GET api/Web/5
public Web Get(int id)
{
try
{
return websites[id];
}
catch (Exception e)
{
return new Web();
}
}
// POST api/values
public void Post([FromBody]string value)
{
Console.WriteLine("Post method called with value = " + value);
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
Console.WriteLine("Put method called with value = " + value);
}
// DELETE api/values/5
public void Delete(int id)
{
Console.WriteLine("Delete method called with id = " + id);
}
}
I am invoking my service on IE like everyone does: http://127.0.0.1:8080/api/web to GET entire Web Object.
I have installed two additional packages, OWIN and CORS.
Could someone help find a solution for this issue.
I have just tried and your api works correctly in Edge and Chrome. It can be that IE don't send correct Accept header which causes to either server to return wrong result or error or IE cannot interpret the response. Actually, I have check and IE offers to save json file because it cannot display it correctly.
To show it explicitly I have modified you code a bit:
public IHttpActionResult Get(string type = null)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
if (type == "json")
{
response.Content = new StringContent(JsonConvert.SerializeObject(websites));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
else if (type == "xml")
{
response.Content = new StringContent("<xmlTag>Value</xmlTag>");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
}
return ResponseMessage(response);
}
Try follow http://127.0.0.1:8080/api/web?type=json and http://127.0.0.1:8080/api/web?type=xml urls and check yourself.

Categories