My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.
Related
I try to improve myself with .NET Web API now and I am trying to return a custom error in Swagger. But when returning this custom error, I can see the error is on which line. How can I do to prevent this?
public async Task<BookCreateDTO> CreateBook(BookCreateDTO bookCreateDto)
{
if (await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name))
{
throw new BookExistException("Book already exist");
}
var book= _mapper.Map<Book>(bookCreateDto);
_context.Books.Add(book);
await _context.SaveChangesAsync();
return book;
}
What should I do to see only this exception message in the Swagger response?
Thank you for your help.
Exceptions should be exceptional: Don't throw exceptions for non-exceptional errors.
I don't recommend specifying your web-service's response DTO type in the C# action method return type because it limits your expressiveness (as you're discovering).
Instead use IActionResult or ActionResult<T> to document the default (i.e. HTTP 2xx) response type and then list error DTO types in [ProducesResponseType] attributes with their corresponding HTTP status codes.
This also means that each response status code should only be associated with a single DTO type.
While Swagger is not expressive enough to allow you to say "if the response status is HTTP 200 then the response body/DTO is one-of DtoFoo, DtoBar, DtoQux", in-practice a well-designed web-service API should not exhibit that kind of response DTO polymorphism.
And if it didn't, how else is a client supposed to know what the type is just from the HTTP headers? (Well, you could put the full DTO type-name in a custom HTTP response header, but that introduces other problems...)
For error conditions, add the errors to ModelState (with the Key, if possible) and let ASP.NET Core handle the rest for you with ProblemDetails.
If you do throw an exception, then ASP.NET Core can be configured to automatically render it as a ProblemDetails - or it can show the DeveloperExceptionPage - or something else entirely.
I note that a good reason to not throw an exception inside a Controller for non-exceptional exceptions is because your logging framework may choose to log more details about unhandled exceptions in ASP.NET Core's pipeline, which would result in useless extraneous entries in your logs that make it harder to find "real" exceptions that you need to fix.
Document the DTOs used, and their corresponding HTTP status codes, with [ProducesResponseType]: this is very useful when using Swagger/NSwag to generate online documentation and client libraries.
Also: do not use EF entity types as DTOs or ViewModels.
Reason 1: When the response (with EF entity objects) is serialized, entities with lazy-loaded properties will cause your entire database object-graph to be serialized (because the JSON serializer will traverse every property of every object).
Reason 2: Security! If you directly accept an EF entity as an input request body DTO or HTML form model then users/visitors can set properties arbitrarily, e.g. POST /users with { accessLevel: 'superAdmin' }, for example. While you can exclude or restrict which properties of an object can be set by a request it just adds to your project's maintenance workload (as it's another non-local, manually-written, list or definition in your program you need to ensure is kept in-sync with everything else.
Reason 3: Self-documenting intent: an entity-type is for in-proc state, not as a communications contract.
Reason 4: the members of an entity-type are never exactly what you'll want to expose in a DTO.
For example, your User entity will have a Byte[] PasswordHash and Byte[] PasswordSalt properties (I hope...), and obviously those two properties must never be exposed; but in a User DTO for editing a user you might want different members, like NewPassword and ConfirmPassword - which don't map to DB columns at all.
Reason 5: On a related note to Reason 4, using Entity classes as DTOs automatically binds the exact design of your web-service API to your database model.
Supposing that one day you absolutely need to make changes to your database design: perhaps someone told you the business requirements changed; that's normal and happens all the time.
Supposing the DB design change was from allowing only 1 address per customer (because the street addresses were being stored in the same table as customers) to allowing customers to have many addresses (i.e. the street-address columns are moved to a different table)...
...so you make the DB changes, run the migration script, and deploy to production - but suddenly all of your web-service clients stop working because they all assumed your Customer object had inline Street address fields but now they're missing (because your Customer EF entity types' don't have street-address columns anymore, that's over in the CustomerAddress entity class).
If you had been using a dedicated DTO type specifically for Customer objects then during the process of updating the design of the application you would have noticed builds breaking sooner (rather than inevitably later!) due to C# compile-time type-checking in your DTO-to-Entity (and Entity-to-DTO) mapping code - that's a benefit right there.
But the main benefit is that it allows you to completely abstract-away your underlying database design - and so, in our example, if you have remote clients that depend on Customer address information being inline then your Customer DTO can still emulate the older design by inlining the first Customer Address into the original Customer DTO when it renders its JSON/XML/Protobuf response to the remote client. That saves time, trouble, effort, money, stress, complaints, firings, unnecessary beatings, grievous bodily harm and a scheduled dental hygienist's appointment.
Anyway, I've modified your posted code to follow the guidance above:
I added [ProducesResponseType] attributes.
I appreciate it is redundant to specify the default response type BookCreateDTO twice (in [ProducesResponseType] as well as ActionResult<BookCreateDTO> - you should be able to remove either one of those without affecting Swagger output.
I added an explicit [FromBody], just to be safe.
If the "book-name is unused" check fails, it returns the model validation message in ASP.NET's stock BadRequest response, which is rendered as an IETF RFC 7807 response, aka ProblemDetails instead of throwing an exception and then hoping that you configured your ASP.NET Core pipeline (in Configure()) to handle it as a ProblemDetails instead of, say, invoking a debugger or using DeveloperExceptionPage.
Note that in the case of a name conflict we want to return HTTP 409 Conflict and not HTTP 400 Bad Request, so the conflictResult.StatusCode = 409; is overwritten.
The final response is generated from a new BookCreateDTO instance via AutoMapper and Ok() instead of serializing your Book entity object.
[ProducesResponseType(typeof(BookCreateDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
public async Task< ActionResult<BookCreateDTO> > CreateBook( [FromBody] BookCreateDTO bookCreateDto )
{
// Does a book with the same name exist? If so, then return HTTP 409 Conflict.
if( await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name) )
{
this.ModelState.Add( nameof(BookCreateDTO.Name), "Book already exists" );
BadRequestObjectResult conflictResult = this.BadRequest( this.ModelState );
// `BadRequestObjectResult` is HTTP 400 by default, change it to HTTP 409:
conflictResult.StatusCode = 409;
return conflictResult;
}
Book addedBook;
{
addedBook = this.mapper.Map<Book>( bookCreateDto );
_ = this.context.Books.Add( book );
_ = await this.context.SaveChangesAsync();
}
BookCreateDTO responseDto = this.mapper.Map<BookCreateDTO >( addedBook );
return this.Ok( responseDto );
}
I'm new to .NET and to webservice development, so i'm not exactly sure how to implement the requirement i have.
My webservice gets a POST request with some data, which i need to
process to generate a pdf file: name_YYYYMMDDHHmmss.pdf.
For monitoring this i want to have a separate logfile for each request, named like the output file: name_YYYYMMDDHHmmss.log
I would like to avoid passing a config object into every class/function in which i need to add stuff to the log file
I've managed to install Serilog and it works for what i need, but not when i get concurrent requests. I'm also not exactly sure how simultaneous requests are handled in .NET (i have no thread specific code written so far), but as far as i can tell, when i change Global Logger file name, that object is shared across all threads so all of them write to the same file.
I've looked at a bunch of solutions, but i haven't managed to find nothing that suits this, and it seems most people have everything into 1 file...
Is there any clue or tips you can give me? I'm open to using something other than Serilog.
One way to have dynamic file names based on a specific context is by using the Serilog.Sinks.Map and then, via a middleware in the request pipeline, you can add a property to the log context that drives the file name to be used when writing to the log.
Examples of similar usage of Serilog.Sinks.Map to decide which file name to use at run-time:
Serilog - can not log to multiple files based on property
In Serilog dynamically changing log file path?
The best solution that I found to this problem was using Serilog.Sinks.Map. I configured my Logger something like this:
Log.Logger = new LoggerConfiguration()
.WriteTo.Map("Name", "Default", (name, wt) => {
var fileName = name == "Default" ? "log" : $"{log-{name}}"
wt.File($"./{fileName}-.txt");
}).CreateLogger();
Then on my controller, on each method where I needed this feature, I enclosed all the instructions inside a LongContext like this:
[HttpGet]
public IHttpActionResult Get() {
using (LogContext.PushProperty("Name", "theFileName") {
// ...
_myService.Method1();
// ...
}
}
public class MyService : IMyService {
// ...
public void Method1() {
// ...
Log.Information("This is what happened at this point…");
// ...
}
// ...
}
So all the Log's inside will use that context and it will write on a different file with the name you set for that context without having to modify any Log.Information/Error/Warning/etc that you already have on your code.
This is the ugly part... you have to define a context on a root place in order to make those Logs write on a different file. So for a controller method, the first thing you have to do is to enclose all with a LogContext.
I want to apply asp.net api-versioning to my web app (which didn't have versioning). However, the tricky issue is that I must ensure that APIs should work both with and without the api-version.
[ApiVersion("1.0")]
[Route("api/products/{productId}/[controller]")]
[Route("api/v{version:apiVersion}/products/{productId}/[controller]")]
[ValidateModel]
[Produces("application/json")]
public partial class ProductController : ControllerBase {
internal const string GetLatestRoute = "GET Product/GetLatestAsync";
[HttpGet(Name = GetLatestRoute)]
public async Task<IActionResult> GetLatestAsync() {
}
}
I have a controller with multiple actions, each of them is defined with a unique route name. When I add two routes (with and without versions) to the controller, there comes a route-name conflict error:
Attribute routes with the same name 'GET Products/GetLatestAsync' must have the same template:
Action: 'Service.Controllers.ProductController.GetLatestAsync (ProductFD)' - Template: 'api/products/{productId}/Product'
Action: 'Service.Controllers.ProductController.GetLatestAsync (ProductFD)' - Template: 'api/v{version:apiVersion}/products/{productId}/Product'
There are several answers on StackOverflow that say the issue can be solved by removing the route names defined for the action methods. However, in my scenario, the route names are used to create Url Links in several places in the project.
Is there an approach that I can get rid of the issue? I'm wondering whether I could append version to the route name variable or mapping the non-version api to the version/1.0 ...? On the other hand, there is a rare case that I update all the methods in a controller. So is it possible that I only define a route-prefix on the top-level of the controller and only apply the api-version on the method-level?
Route names and the route table are not API version aware. In order for this to work, you need to use double route registration like you have because you are versioning by URL segment (not recommended). If clients are properly following the links returned by the server, then always using the route generated with the explicit version in it will do. If the client doesn't honor that and just calls the APIs directly without the API version, the second template will handle that for you. If you are only generating links with the same controller, then I would suggest using CreatedAtAction instead because it will not rely on the route name. If memory serves me correct, you can specify the order of each [Route] for precedence. If unspecified, it will be the first attribute specified - which matters.
You'll also need to enable:
services.AddApiVersioning(options => options.AssumeDefaultVersionWhenUnspecified = true);
If you haven't already.
Last, but not least, beware the known, breaking change: Async suffix trimmed from controller action names. This has snared many people.
I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.
I am using Web APi, as I am new to this, I dont know much about it.
I am trying to implement search, as of now I am starting with only text search, but later there may be huge search criteria. for one text that is easy, as web api works good with
primitive data types. Now I want to create a class of filter, say the pagenumber , the pagesize also all the search criteria, so I created a class. I have created a MVC application which is communicating with the web api, the web api returns Json data, then I de-serialize it to model. I am stuck with the complex object part, also as of now I am using a list to get the data, later that will be replaced by data base. Following is the code.
public IEnumerable<Document> Get(PaggingDetails request) //public async Task<IEnumerable<Note>> GetNotes() for Async (DB)
{
return _repository.GetAll(pagedetails.PageNumber, pagedetails.PageSize, pagedetails.PageFilter);
//return await db.Notes.ToListAsync<Note>(); for async
}
public string GetPage(int pagenumber,int pagesize,string pagefilter)
{
try
{
PaggingDetails PageDetails = new PaggingDetails();
PageDetails.PageFilter = pagefilter;
PageDetails.PageSize = pagesize;
PageDetails.PageNumber = pagenumber;
return new System.Net.WebClient().DownloadString
("http://.../api/Document/?pagedetails=" +
PageDetails);
//new HttpClient().GetStringAsync("http://localhost:18545/api/Emails"); for async
//also pass parameters
}
catch (Exception ex)
{
}
return "";
}
By deafult, you cannot use a class as the type of parameter of a GET Web API action. You need to use individual parameters of single types.
If you want to use a class as parameter nothing stops you to use a POST action, in which you can include the data without any problem.
However you can force a complex parameter of a GET action to be read from the URI by decorating the comples attribute with [FromUri].
You can read this document to better understand Web API parameter binding:
Parameter Binding in ASP.NET Web API
By default, Web API uses the following rules to bind parameters:
If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
This is the standard way of working. If you use the [FromUri] attribute, the action selector won't be able to choose between different Get methods that receive different complex types. If you use a route with controller and action segments, you won't have that problem, becaus ethe actions selector will choose by action name, no matter what the aprameters are.
I don't like using the [FromUri] for this reason, and beacuse it's not the natural way to work with the GET action. But you can use it with the necessary precautions.