modelscope.cn

xaf-web-api

XAF Backend Web API Service - OData v4 REST endpoints, Program.cs setup with JWT auth, business object exposure with [AllowedAction]/[IgnoreDataMember], OData query options ($filter/$expand/$select/$orderby/$top/$skip/$count), JWT authentication flow, custom OData actions/functions via [Action] attribute, Swagger integration, security integration, file upload, v24.2 vs v25.1 differences.

Installation

$ npx skills add https://modelscope.cn

Summary

  • XAF Backend Web API Service - OData v4 REST endpoints, Program.cs setup with JWT auth, business object exposure with [AllowedAction]/[IgnoreDataMember], OData query options ($filter/$expand/$select/$orderby/$top/$skip/$count), JWT authentication flow, custom OData actions/functions via [Action] attribute, Swagger integration, security integration, file upload, v24.2 vs v25.1 differences.
  • Use when building or consuming the XAF Web API backend service.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from modelscope.cn · top by installs.

npx skills add https://modelscope.cn

Browse all from modelscope.cn

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,165 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

XAF: Backend Web API Service

Overview

XAF Web API Service provides OData v4 REST endpoints for XAF business objects. It can run standalone (no UI) or alongside a Blazor app. All XAF security, validation, and ObjectSpace features work the same way.


Project Setup

NuGet Packages

<PackageReference Include="DevExpress.ExpressApp.WebApi" Version="25.1.*" />
<PackageReference Include="DevExpress.ExpressApp.WebApi.Jwt" Version="25.1.*" />
<PackageReference Include="DevExpress.ExpressApp.WebApi.Swashbuckle" Version="25.1.*" />

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddXaf(builder.Configuration, b => {
    b.UseApplication<MyWebApiApplication>();

    b.AddObjectSpaceProviders(providers => {
        providers.UseEntityFramework(ef => {
            ef.DefaultDatabaseConnection("Default", p =>
                p.UseDbContext<MyDbContext>());
        });
        providers.AddNonPersistent();
    });

    b.Security
        .UseIntegratedMode(options => {
            options.RoleType = typeof(PermissionPolicyRole);
            options.UserType = typeof(ApplicationUser);
        })
        .AddPasswordAuthentication();

    b.AddWebApi(options => {
        options.BusinessObject<Contact>();      // expose specific type
        options.BusinessObject<Order>();
        // or expose all: options.BusinessObjects.AddFromAssembly(typeof(Contact).Assembly);
    });
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters {
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
        };
    });

builder.Services.AddSwaggerGen();

var app = builder.Build();
app.UseXaf();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.UseSwagger();
app.UseSwaggerUI();
app.Run();

Controlling Business Object Exposure

// Allow/deny specific CRUD operations
[AllowedAction(Operations.Read)]            // read-only
[AllowedAction(Operations.Create | Operations.Read | Operations.Write)]
public class Contact : BaseObject { ... }

// Exclude property from API response
[IgnoreDataMember]
public virtual string InternalNotes { get; set; }

// Programmatic exposure in Program.cs:
b.AddWebApi(options => {
    options.BusinessObject<Order>()
           .AllowedActions(Operations.Read | Operations.Create);
});

OData Queries

GET collection

GET /api/odata/Contact
GET /api/odata/Contact?$filter=City eq 'Warsaw'
GET /api/odata/Contact?$select=FirstName,LastName,Email
GET /api/odata/Contact?$expand=Department
GET /api/odata/Contact?$orderby=LastName asc,FirstName asc
GET /api/odata/Contact?$top=10&$skip=20&$count=true

GET single object

GET /api/odata/Contact(guid'00000000-0000-0000-0000-000000000001')

POST (create)

POST /api/odata/Contact
Content-Type: application/json
Authorization: Bearer <token>

{ "FirstName": "Jan", "LastName": "Kowalski", "Email": "[email protected]" }

PATCH (update)

PATCH /api/odata/Contact(guid'...')
Content-Type: application/json
Authorization: Bearer <token>

{ "Email": "[email protected]" }

DELETE

DELETE /api/odata/Contact(guid'...')
Authorization: Bearer <token>

OData Filter Operators

Operator Example
Equals $filter=Status eq 'Active'
Not equals $filter=Status ne 'Deleted'
Greater/Less $filter=Amount gt 100
Contains $filter=contains(Name, 'Smith')
StartsWith $filter=startswith(Name, 'Jan')
Null check $filter=Department eq null
AND/OR $filter=City eq 'Warsaw' and Active eq true
Date $filter=CreatedDate gt 2024-01-01

Authentication

Get Token

POST /api/Authentication/Authenticate
Content-Type: application/json

{ "userName": "Admin", "password": "" }

Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Use Token

GET /api/odata/Contact
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

HttpClient Example (C#)

var httpClient = new HttpClient { BaseAddress = new Uri("https://myapp.com") };

// Get token
var tokenResponse = await httpClient.PostAsJsonAsync(
    "/api/Authentication/Authenticate",
    new { userName = "Admin", password = "" });
var token = await tokenResponse.Content.ReadAsStringAsync();

// Use token
httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", token.Trim('"'));

// Query data
var contacts = await httpClient.GetFromJsonAsync<ODataResult<Contact>>(
    "/api/odata/Contact?$filter=Active eq true");

Custom Endpoints

Custom OData Action (POST, modifies data)

// In a controller (or XAF business logic class):
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase {
    private readonly IObjectSpaceFactory _objectSpaceFactory;

    public OrderController(IObjectSpaceFactory objectSpaceFactory) {
        _objectSpaceFactory = objectSpaceFactory;
    }

    [HttpPost("api/odata/Order({key})/ProcessOrder")]
    public IActionResult ProcessOrder([FromRoute] Guid key) {
        using var os = _objectSpaceFactory.CreateObjectSpace(typeof(Order));
        var order = os.GetObjectByKey<Order>(key);
        if (order == null) return NotFound();
        order.Status = OrderStatus.Processed;
        os.CommitChanges();
        return Ok();
    }
}

Or via XAF's [Action] attribute on a non-persistent class:

[DomainComponent]
public class OrderActions {
    [Action(Caption = "ProcessOrder")]
    public void ProcessOrder(Order order, IObjectSpace objectSpace) {
        order.Status = OrderStatus.Processed;
        objectSpace.CommitChanges();
    }
}

Swagger / OpenAPI

Automatically available at /swagger when configured. All OData endpoints are documented.


Security Integration

All OData endpoints automatically apply XAF security:

  • Permissions filter which records are returned (object-level security)
  • Member-level permissions hide specific properties
  • DenyAllByDefault → explicit grants required for API access

No additional configuration needed — security works transparently via ObjectSpace.


Pagination Pattern

GET /api/odata/Contact?$top=10&$skip=0&$count=true

Response includes @odata.count with total count. Client paginates by incrementing $skip.


v24.2 vs v25.1 Differences

Feature v24.2 v25.1
OData version v4 v4 (same)
JWT auth Available Available (enhanced config)
Swagger Available Available
Batch requests Limited Improved
.NET target .NET 8 .NET 8 / .NET 9

Source Links