api cannot accept XML - xml-parsing

I'm using .netCore 3.1 and this is my first time dealing with XML.
I'm trying to read xml data, and parse them then save them to the db.
I have a model named ExternalAppointment that I want to map data to.
here's my controller :
[HttpPost("xml")]
public async Task<ActionResult<Appointment>> XMLContent([FromBody] ExternalAppointment xmldata)
{
//stuff.
}
here's my startup.cs :
services.Configure<RequestLocalizationOptions>(options =>
{
//some code
services.AddMvc()
.AddXmlSerializerFormatters();
//rest of code
}
here's the xml I'm passing in postman:
<?xml version="1.0" encoding="UTF-8"?>
<event>
<business_owner_id>1</business_owner_id>
<business_owner_name>Doctor's lab</business_owner_name>
<business_owner_phone_number>44556699887</business_owner_phone_number>
<customer_email>example#example.com</customer_email>
<customer_phone_number>1122336655</customer_phone_number>
<date_and_time>16/12/2021 20:45:00</date_and_time>
<location>21 jump street</location>
<service_name>PCR</service_name>
</event>
and I can't seem to get through to the request, I keep getting 405 response.
I tried adding [consumes("application/xml")] decorator before the controller but it did not help.
any ideas ?

Here is my demo with XElement,and it can work:
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddMvc()
.AddXmlSerializerFormatters();
}
ApiController:
[Route("api/[controller]")]
[ApiController]
public class ApiController : ControllerBase
{
[HttpPost("xml")]
public IActionResult XMLContent([FromBody] XElement xmldata)
{
return Ok();
//stuff.
}
}
postman(You need to make sure you use post request):
result:

Related

Swagger not loading - Failed to load API definition: error undefined message

As I tried to change one public method to private. The issue will be gone.
But when I defined the public method on both method as the below controller code, it will bring up the error.
Anyone help/advice if possible would be really appreciate
WEBLABController.cs
[Route("api/SearchLABResults")]
[HttpPost]
public async Task<IActionResult> SearchLABResults([FromBody] SP_GET_LABResult_Overview.input data)
{
IActionResult response = Unauthorized();
......
return response;
}
AuthenticationController.cs
[Route("api/GenerateToken")]
[HttpPost]
public async Task<IActionResult> GenerateToken([FromBody] AuthenticationModel.input data)
{
IActionResult response = Unauthorized();
......
return response;
}
If you scroll down the swagger view, You see that every model you are using is shown there. Swagger needs that those names be unique.
SP_GET_LABResult_Overview.input and AuthenticationModel.input are both generate input schema and that cause InvalidOperationException.
Try to change one of the input classes to something else or change their schema on the fly like this.
services.AddSwaggerGen(options =>
{
options.CustomSchemaIds(type => type.ToString());
});

Cannot generate swagger with multiple HttpPut commands

I can't get swashbuckle to generate the swagger.json when I have more than one HttpPut command. I am using OData4 and ASP.NET Core 2.2. If I comment one of either of the two HttpPut methods, it generates just fine. I have tried adding [SwaggerOperation(OperationId = '')] attribution and that was unsuccessful. I have tried adding [ODataRoute(nameof())] attribution and that was unsuccessful. I have tried editing the [HttpPut(nameof())] attribute and it didn't work. I am at my wits end trying to figure out the key combination to allow this to work. Any suggestions are eagerly welcomed.
[ApiVersion("1.0")]
[ODataRoutePrefix("Items")]
public class ItemsController : ODataController {
[HttpGet]
[Produces("application/json")]
[ProducesResponseType(typeof(IQueryable<Item>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[EnableQuery(AllowedQueryOptions = Select | OrderBy | Top | Skip | Count,
AllowedFunctions = AllowedFunctions.None,
AllowedArithmeticOperators = AllowedArithmeticOperators.None,
AllowedLogicalOperators = AllowedLogicalOperators.None,
AllowedOrderByProperties = "name,location",
MaxOrderByNodeCount = 2,
MaxTop = 100)]
public async Task<IActionResult> Get() {
...
}
[HttpGet]
[ODataRoute("({key})")]
[ProducesResponseType(typeof(Item), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task<IActionResult> GetById(string key) {
...
}
[HttpPost]
[ODataRoute]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Post([FromBody] AddItemCommand command) {
...
}
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> StopMonitoring([FromBody] StopMonitoringCommand command) {
...
}
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> ChangeMonitoring([FromBody] ChangeMonitoringCommand command) {
...
}
}
With both HttpPut methods in there, it fails with an "Fetch error undefined /swagger/v1/swagger.json" error message.
#HelderSepulveda and the Actions and Functions in OData v4 Using ASP.NET Web API 2.2 gave me the clues to resolve this issue.
Regardless of whether they are HttpPut or HttpPost on the controller, actions can be added by adding them to both the controller and OData model configuration.
e.g.
public class ODataModelConfiguration : IModelConfiguration {
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion) {
builder.EntitySet<Item>("Items");
builder.EntityType<Item>()
.Action("StopMonitoring")
.Parameter<StopMonitoringCommand>("command");
builder.EntityType<Item>()
.Action("ChangeMonitoring")
.Parameter<ChangeMonitoringCommand>("command");
}
}
and in the controller:
[HttpPut]
[ODataRoute("StopMonitoring")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> StopMonitoring([FromBody] StopMonitoringCommand command) {
...
}
[HttpPut]
[ODataRoute("ChangeMonitoring")]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> ChangeMonitoring([FromBody] ChangeMonitoringCommand command) {
...
}
Combined with the code in the original post, this generates the following in swagger:
GET /api/Items
POST /api/Items
GET /api/items({key})
PUT /api/items/StopMonitoring
PUT /api/items/ChangeMonitoring

asp.net core validation after filters

I want to run some custom logic for all APIs (asp.net core) that we have in our service before model validation but after model binding. Is this possible? I tried an ActionFilter but it gets called after validation. Resource filter also does not work for us. Appreciate your help.
Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. In that case, an automatic HTTP 400 response containing issue details is returned when model state is invalid.
One way to achieve what you want is to suppress this behavior.
Add the following code to ConfigureServices:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
Then you can add your code to the filter - eg:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
if(context.ActionArguments != null && context.ActionArguments.Count > 0)
{
//WARNING - you should add "safe" code to access the dictionary
//I have hardcoded the parameter name (data) here for sample only.
var model = context.ActionArguments["data"];
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// do something after the action executes
}
}
of course you need to apply the filter as well - in the example case below, I have applied it globally. You can be more specific if you want.
services.AddMvc(
options => options.Filters.Add(new SampleActionFilter())
).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
In your controller code, you can also further use the TryValidateModel method if you want, like so:
[Route("api/[controller]")]
[ApiController]
public class ProcessController : ControllerBase
{
[HttpPost]
public IActionResult Contact(FormDataModel data)
{
bool validated = TryValidateModel(data);
if (!ModelState.IsValid)
{
ModelState.AddModelError("", "Id cannot be empty..");
return Ok();
}
return Ok();
}
}
Hope this helps to solve your problem.

Debugging Swashbuckle Error - Failed to load API Definition

Is there any way to get a stack trace or inner exceptions on Swashbuckle/Swagger errors? At some point, it stopped working. I'm not sure if it was when I upgraded from .Net Core 2.0 to 2.1, but I'm pretty sure it was still working after that. When I navigate to myapidomain/swagger/index.html I get this error:
Which is not very helpful. It was working 2 or so weeks ago... I didn't change any Swagger configuration. It's the same as it's always been:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "My.API",
Description = "Test"
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "PropWorx API V1");
});
...
}
I'm using Swashbuckle.AspNetCore 3.0.0 on an ASP.Net Core 2.1 Web API.
Agree that the UI debugging isn't really useful, however the full exception trace can be scraped by opening up your browser's debugging tools (e.g. F12 on Chrome), refreshing your /swagger endpoint, and then examining the swagger.json payload - this is an XHR request which will fail with a 500 status code.
(I would suggest on a big project that you bookmark the link, so that in future you can just go straight to the json file, e.g. https://MyHost/swagger/v1/swagger.json)
e.g. in the below contrived error, I've duplicated the same route between two methods:
[HttpGet]
[Route("parseenum")]
public ActionResult CheckEnum([FromQuery] MyEnum someEnum)
...
[HttpGet]
[Route("parseenum")]
public ActionResult CheckEnum2([FromQuery] MyEnum someEnum)
...
Which produces the error:
Which you should now be able to track down and fix.
If your api have same two or more [HttpGet] its not working swagger.
You should be specify [HttpGet] , [HttpGet ("{id}")]
[HttpGet]
`[HttpGet ("{id}")]`
I found that the SwaggerFunctions need to by Anonymous for the Swagger/UI to complete.
public static class SwaggerFunctions
{
[SwaggerIgnore]
[FunctionName("Swagger")]
public static Task<HttpResponseMessage> Swagger(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "swagger/json")]
HttpRequestMessage req,
[SwashBuckleClient] ISwashBuckleClient swashBuckleClient)
{
return Task.FromResult(swashBuckleClient.CreateSwaggerDocumentResponse(req));
}
[SwaggerIgnore]
[FunctionName("SwaggerUi")]
public static Task<HttpResponseMessage> SwaggerUi(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "swagger/ui")]
HttpRequestMessage req,
[SwashBuckleClient] ISwashBuckleClient swashBuckleClient)
{
return Task.FromResult(swashBuckleClient.CreateSwaggerUIResponse(req, "swagger/json"));
}
}
i have this problem today and try so much to resolve it , you should must remove all [Route] before [http] tag`s in C# controller code like this code
for example:
[Route("~/api/getAll")]
[HttpGet]
public ActionResult<List<asteriksModel>>GetAll()
{
return _context.asterList.ToList();
}
and your route code mustbe like this
[HttpGet]
public ActionResult<List<asteriksModel>>GetAll()
{
return _context.asterList.ToList();
}
in my project it works fine

ASP.NET MVC 4 Application Calling Remote WebAPI

I've created a couple ASP.NET MVC applications in the past, but I've never used WebAPIs before. I'm wondering how I could create a simple MVC 4 app that does simple CRUD stuff via WebAPI instead of through a normal MVC controller. The trick is that the WebAPI should be a separate solution (and, in fact, could very well be on a different server/domain).
How do I do that? What am I missing? Is it just a matter of setting up routes to point to the WebAPI's server? All the examples I've found showing how to consume WebAPIs using an MVC application seem to assume the WebAPI is "baked in" to the MVC application, or at least is on the same server.
Oh, and to clarify, I'm not talking about Ajax calls using jQuery... I mean that the MVC application's controller should use the WebAPI to get/put data.
You should use new HttpClient to consume your HTTP APIs. What I can additionally advise you to make your calls fully asynchronous. As ASP.NET MVC controller actions support Task-based Asynchronous Programming model, it is pretty powerful and easy.
Here is an overly simplified example. The following code is the helper class for a sample request:
public class CarRESTService {
readonly string uri = "http://localhost:2236/api/cars";
public async Task<List<Car>> GetCarsAsync() {
using (HttpClient httpClient = new HttpClient()) {
return JsonConvert.DeserializeObject<List<Car>>(
await httpClient.GetStringAsync(uri)
);
}
}
}
Then, I can consume that through my MVC controller asynchronously as below:
public class HomeController : Controller {
private CarRESTService service = new CarRESTService();
public async Task<ActionResult> Index() {
return View("index",
await service.GetCarsAsync()
);
}
}
You can have a look at the below post to see the effects of asynchronous I/O operations with ASP.NET MVC:
My Take on Task-based Asynchronous Programming in C# 5.0 and ASP.NET MVC Web Applications
Thanks everyone for the responses. #tugberk led me down the right path, I think. This worked for me...
For my CarsRESTService helper:
public class CarsRESTService
{
readonly string baseUri = "http://localhost:9661/api/cars/";
public List<Car> GetCars()
{
string uri = baseUri;
using (HttpClient httpClient = new HttpClient())
{
Task<String> response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<List<Car>>(response.Result).Result;
}
}
public Car GetCarById(int id)
{
string uri = baseUri + id;
using (HttpClient httpClient = new HttpClient())
{
Task<String> response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<Car>(response.Result).Result;
}
}
}
And then for CarsController.cs:
public class CarsController : Controller
{
private CarsRESTService carsService = new CarsRESTService();
//
// GET: /Cars/
public ActionResult Index()
{
return View(carsService.GetCars());
}
//
// GET: /Cars/Details/5
public ActionResult Details(int id = 0)
{
Car car = carsService.GetCarById(id);
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
}
You can use WCF to consume the service. Like so:
[ServiceContract]
public interface IDogService
{
[OperationContract]
[WebGet(UriTemplate = "/api/dog")]
IEnumerable<Dog> List();
}
public class DogServiceClient : ClientBase<IDogService>, IDogService
{
public DogServiceClient(string endpointConfigurationName) : base(endpointConfigurationName)
{
}
public IEnumerable<Dog> List()
{
return Channel.List();
}
}
And then you can consume it in your controller:
public class HomeController : Controller
{
public HomeController()
{
}
public ActionResult List()
{
var service = new DogServiceClient("YourEndpoint");
var dogs = service.List();
return View(dogs);
}
}
And in your web.config you place the configuration for your endpoint:
<system.serviceModel>
<client>
<endpoint address="http://localhost/DogService" binding="webHttpBinding"
bindingConfiguration="" behaviorConfiguration="DogServiceConfig"
contract="IDogService" name="YourEndpoint" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="DogServiceConfig">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
http://restsharp.org/ is the answer to your questions. I am currently using it in an application which has similar structure.
But more generally using WebAPI is just posting and requesting data how to process is up to you. You can even use standard WebRequest and JavascriptSerializer.
Cheers.
In this case, you can use HttpClient to consume Web API from your controller.

Resources