gRPC Introduktion

Avidentifiera API via gRPC erbjuder ett högpresterande, typsäkert alternativ till REST för server-till-server-kommunikation. API:et tar emot text, PDF eller DOCX och returnerar en avidentifierad version baserat på regelstyrd behandling av personuppgifter.

Tjänsten RedactionService har två huvudsakliga RPC-metoder:

  • RedactFile – tar emot en PDF eller DOCX (Word) som bytes eller Base64 och returnerar den avidentifierade filen på samma sätt.
  • RedactText – tar en textsträng och returnerar den avidentifierade texten.

Endpoint (Native & Web):

https://api.avidentifiera.se:50051

Direkt in, direkt ut – inga känsliga dokument lagras

Precis som med vårt REST-API har vi valt en synkron modell för datasäkerhet och enkelhet. Dokumentet skickas i din begäran, bearbetas helt i minnet och det avidentifierade resultatet skickas tillbaka i samma anrop. Vi lagrar aldrig ditt originaldokument på våra servrar.

För verksamheter som hanterar sekretessreglerade handlingar betyder detta att du skickar ett dokument och får tillbaka en avidentifierad version direkt – utan att vi behöver spara ditt original.

Proto-definition

Du kan alltid hämta den senaste kontraktsdefinitionen (.proto) här:

https://api.avidentifiera.se/grpc/v1/redaction.proto


Klient-setup

För att komma igång behöver du gRPC-verktyg för ditt språk för att generera klientkod från vår .proto-fil. Här är de vanligaste paketen och kommandona.

<!-- Lägg till i din .csproj-fil -->
<ItemGroup>
  <PackageReference Include="Google.Api.CommonProtos" Version="2.17.0" />
  <PackageReference Include="Grpc.Net.Client" Version="2.17.0" />
  <PackageReference Include="Grpc.Net.Client.Web" Version="2.17.0" />
  <PackageReference Include="Google.Protobuf" Version="3.33.1" />
  <PackageReference Include="Grpc.Tools" Version="2.17.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
  <Protobuf Include="Protos\redaction.v1.proto" GrpcServices="Client" />
</ItemGroup>
# 1. Installera paket
pip install grpcio grpcio-tools googleapis-common-protos

# 2. Ladda ner .proto-filen och generera kod
# Se till att ha google/rpc/status.proto och google/rpc/bad_request.proto i samma mapp
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. redaction.v1.proto google/rpc/status.proto google/rpc/bad_request.proto
# För native gRPC (backend)
npm install @grpc/grpc-js @grpc/proto-loader google-protobuf

# För gRPC-Web (webbläsare)
npm install grpc-web google-protobuf

Autentisering

Varje anrop måste innehålla en giltig API-nyckel som metadata med nyckeln authorization och värdet Bearer <din-nyckel>. Om nyckeln saknas eller är ogiltig returneras statuskoden UNAUTHENTICATED.

var metadata = new Grpc.Core.Metadata { { "Authorization", "Bearer live_xxx..." } };
var response = await client.RedactTextAsync(request, metadata);
metadata = [('authorization', 'Bearer live_xxx...')]
response = stub.RedactText(request, metadata=metadata)
const md = new grpc.Metadata();
md.set('authorization', 'Bearer live_xxx...');
client.RedactText(request, md, (err, resp) => { /* ... */ });

Avidentifiera fil

RPCRedactFile

Skicka en PDF eller DOCX och få tillbaka en avidentifierad version. Filen kan skickas som råa bytes eller som en Base64-kodad sträng.

Request: RedactFileRequest

FältBeskrivning
fileName
string
Filnamn, t.ex. "dokument.pdf". Används för att avgöra filtyp.
fileBytes
bytes
Filens innehåll som en byte-array. Använd antingen detta fält eller fileBase64.
fileBase64
string
Filens innehåll som en Base64-kodad sträng. Använd antingen detta fält eller fileBytes.
options
Options
Valfri konfiguration. Se Regelkonfiguration.

Response: RedactFileResponse

FältBeskrivning
took
int64
Total bearbetningstid på servern i millisekunder.
result
FileResult
Innehåller den avidentifierade filen som redactedFileBytes eller redactedFileBase64 (beroende på request), samt funna entities.
using Google.Protobuf;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
using Grpc.Redaction.V1;

const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler());
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpHandler = grpcWebHandler });
var client = new RedactionService.RedactionServiceClient(channel);

var fileBytes = await File.ReadAllBytesAsync("dokument.pdf");
var req = new RedactFileRequest
{
    FileName = "dokument.pdf",
    FileBytes = ByteString.CopyFrom(fileBytes),
    Options = new Options { Entities = { EntityType.Name, EntityType.Address } }
};

var resp = await client.RedactFileAsync(req, metadata);
await File.WriteAllBytesAsync("redacted.pdf", resp.Result.RedactedFileBytes.ToByteArray());
using Google.Protobuf;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Redaction.V1;
using System.Net;

const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
var client = new RedactionService.RedactionServiceClient(channel);

var fileBytes = await File.ReadAllBytesAsync("dokument.pdf");
var req = new RedactFileRequest
{
    FileName = "dokument.pdf",
    FileBytes = ByteString.CopyFrom(fileBytes),
    Options = new Options { Entities = { EntityType.Name, EntityType.Address } }
};

var resp = await client.RedactFileAsync(req, metadata);
await File.WriteAllBytesAsync("redacted.pdf", resp.Result.RedactedFileBytes.ToByteArray());
import { RedactionServiceClient } from './generated/RedactionServiceClientPb.js';
import { RedactFileRequest, Options, EntityType } from './generated/redaction_v1_pb.js';

const client = new RedactionServiceClient('https://api.avidentifiera.se:50051');
const req = new RedactFileRequest();

// I webbläsare är det vanligast att hantera filen som Base64
const base64String = "JVBERi0xLjQKJ..."; // Din Base64-kodade PDF/DOCX
req.setFileName("dokument.pdf");
req.setFilebase64(base64String);

const options = new Options();
options.addEntities(EntityType.NAME);
options.addEntities(EntityType.ADDRESS);
req.setOptions(options);

const metadata = { 'authorization': 'Bearer live_xxx...' };
client.redactFile(req, metadata, (err, resp) => {
  if (err) return console.error(err);
  
  // Svaret kommer som Base64, som kan användas för att skapa en nedladdningslänk
  const redactedBase64 = resp.getResult().getRedactedfilebase64();
  console.log('Mottog avidentifierad fil som Base64.');
});
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
import fs from 'fs';

const def = protoLoader.loadSync('./Protos/redaction.v1.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
const proto = grpc.loadPackageDefinition(def).grpc.redaction.v1;
const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());

const md = new grpc.Metadata();
md.set('authorization', 'Bearer live_xxx...');

const bytes = fs.readFileSync('dokument.pdf');
const req = { 
    fileName: 'dokument.pdf', 
    fileBytes: bytes, 
    options: { entities: ['name','address'] } 
};

client.RedactFile(req, md, (err, resp) => {
  if (err) return console.error(err);
  fs.writeFileSync('redacted.pdf', resp.result.redactedFileBytes);
  console.log('Avidentifierad fil sparad som redacted.pdf');
});
import grpc
import redaction_v1_pb2
import redaction_v1_pb2_grpc

API_KEY = "Bearer live_xxx..."
ADDRESS = "api.avidentifiera.se:50051"

def run():
    creds = grpc.ssl_channel_credentials()
    with grpc.secure_channel(ADDRESS, creds) as channel:
        stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
        
        with open("dokument.pdf", "rb") as f:
            file_bytes = f.read()
            
        req = redaction_v1_pb2.RedactFileRequest(
            fileName="dokument.pdf",
            fileBytes=file_bytes,
            options=redaction_v1_pb2.Options(entities=[redaction_v1_pb2.EntityType.name, redaction_v1_pb2.EntityType.address])
        )
        
        md = [('authorization', API_KEY)]
        resp = stub.RedactFile(req, metadata=md)
        
        with open("redacted.pdf", "wb") as out:
            out.write(resp.result.redactedFileBytes)
        print("Avidentifierad fil sparad som redacted.pdf")

if __name__ == "__main__":
    run()

Avidentifiera text

RPCRedactText

Skicka en textsträng och få tillbaka en avidentifierad version. Lyckade anrop returnerar status OK.

Request: RedactTextRequest

FältBeskrivning
text
string
Texten som ska avidentifieras. Max 1 000 000 tecken.
options
Options
Valfri konfiguration som styr avidentifieringen. Om den utelämnas används standardprofilen (namn, adress, personnummer maskeras). Se Regelkonfiguration.

Response: RedactTextResponse

Svaret innehåller resultatet av avidentifieringen samt detaljerad metadata om vad som hittades.

FältBeskrivning
took
int64
Total bearbetningstid på servern i millisekunder.
result
TextResult
Innehåller den avidentifierade texten i fältet redactedText.

Fältet entities innehåller strukturerad data för varje entitetstyp (t.ex. name, address). Varje entitetsobjekt har följande fält:
  • method (Method): Metoden som användes (t.ex. replaceWithInitials eller pseudonymize).
  • matches (repeated string): Lista över originalvärden som identifierades i texten.
  • redactions (repeated RedactionDetail): Lista över faktiska ändringar. Varje RedactionDetail innehåller:
    • original: Texten som togs bort.
    • replacement: Texten som sattes in istället.

Notera: Listan redactions kan innehålla fler poster än matches. Om systemet t.ex. matchar "Erik Sandström", kan det även automatiskt rensa fristående förekomster av "Erik" för att säkerställa anonymitet.

using Grpc.Core;
            using Grpc.Net.Client;
            using Grpc.Net.Client.Web;
            using Grpc.Redaction.V1;

            const string apiKey = "Bearer live_xxx...";
            var metadata = new Metadata { { "Authorization", apiKey } };

            var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler());
            using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpHandler = grpcWebHandler });
            var client = new RedactionService.RedactionServiceClient(channel);

            var request = new RedactTextRequest
            {
                Text = "Mitt namn är Anna Andersson.",
                Options = new Options
                {
                    Entities = { EntityType.Name },
                    Rules = new Rules { Name = new NameRules { Method = Method.ReplaceWithInitials } }
                }
            };

            var response = await client.RedactTextAsync(request, metadata);
            Console.WriteLine($"Redacted: {response.Result.RedactedText}");

            // Iterera över utförda ändringar för namn
            foreach (var redaction in response.Result.Entities.Name.Redactions)
            {
                Console.WriteLine($"Changed '{redaction.Original}' to '{redaction.Replacement}'");
            }
using Grpc.Core;
            using Grpc.Net.Client;
            using Grpc.Redaction.V1;
            using System.Net;

            const string apiKey = "Bearer live_xxx...";
            var metadata = new Metadata { { "Authorization", apiKey } };

            var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
            var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
            using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
            var client = new RedactionService.RedactionServiceClient(channel);

            var request = new RedactTextRequest
            {
                Text = "Mitt namn är Anna Andersson.",
                Options = new Options
                {
                    Entities = { EntityType.Name },
                    Rules = new Rules { Name = new NameRules { Method = Method.ReplaceWithInitials } }
                }
            };

            var response = await client.RedactTextAsync(request, metadata);
            Console.WriteLine($"Redacted: {response.Result.RedactedText}");

            // Visa detaljer om vad som ändrades
            if (response.Result.Entities.Name != null)
            {
                foreach (var redaction in response.Result.Entities.Name.Redactions)
                {
                    Console.WriteLine($"Original: {redaction.Original} -> Nytt: {redaction.Replacement}");
                }
            }
import { RedactionServiceClient } from './generated/RedactionServiceClientPb.js';
            import { RedactTextRequest, Options, Rules, NameRules, Method, EntityType } from './generated/redaction_v1_pb.js';

            const client = new RedactionServiceClient('https://api.avidentifiera.se:50051');
            const req = new RedactTextRequest();
            req.setText('Mitt namn är Anna Andersson.');

            const options = new Options();
            options.addEntities(EntityType.NAME);
            const rules = new Rules();
            const nameRules = new NameRules();
            // Notera: Metodnamn i genererad JS-kod kan variera beroende på protoc-plugin
            nameRules.setMethod(Method.REPLACEWITHINITIALS); 
            rules.setName(nameRules);
            options.setRules(rules);
            req.setOptions(options);

            const metadata = { 'authorization': 'Bearer live_xxx...' };
            client.redactText(req, metadata, (err, resp) => {
              if (err) return console.error(err);
              
              console.log(resp.getResult().getRedactedText());
              
              // Hämta detaljer om namn-redigeringar
              const nameResult = resp.getResult().getEntities().getName();
              const redactions = nameResult.getRedactionsList();
              
              redactions.forEach(r => {
                  console.log(`Original: ${r.getOriginal()}, Replacement: ${r.getReplacement()}`);
              });
            });
import grpc from '@grpc/grpc-js';
            import protoLoader from '@grpc/proto-loader';

            const def = protoLoader.loadSync('./Protos/redaction.v1.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
            const proto = grpc.loadPackageDefinition(def).grpc.redaction.v1;
            const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());

            const md = new grpc.Metadata();
            md.set('authorization', 'Bearer live_xxx...');

            const request = {
                text: 'Mitt namn är Anna Andersson.',
                options: {
                    entities: ['name'],
                    rules: {
                        name: { method: 'replaceWithInitials' }
                    }
                }
            };

            client.RedactText(request, md, (err, resp) => {
              if (err) return console.error(err);
              console.log(resp.result.redactedText);
              
              // Accessa entities.name.redactions
              const nameRedactions = resp.result.entities.name.redactions;
              nameRedactions.forEach(r => {
                  console.log(`Original: ${r.original} -> ${r.replacement}`);
              });
            });
import grpc
            import redaction_v1_pb2
            import redaction_v1_pb2_grpc

            API_KEY = "Bearer live_xxx..."
            ADDRESS = "api.avidentifiera.se:50051"

            def run():
                creds = grpc.ssl_channel_credentials()
                with grpc.secure_channel(ADDRESS, creds) as channel:
                    stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
                    req = redaction_v1_pb2.RedactTextRequest(
                        text="Mitt namn är Anna Andersson.",
                        options=redaction_v1_pb2.Options(
                            entities=[redaction_v1_pb2.EntityType.name],
                            rules=redaction_v1_pb2.Rules(
                                name=redaction_v1_pb2.NameRules(method=redaction_v1_pb2.Method.replaceWithInitials)
                            )
                        )
                    )
                    md = [('authorization', API_KEY)]
                    resp = stub.RedactText(req, metadata=md)
                    print(f"Text: {resp.result.redactedText}")
                    
                    # Loopa igenom redactions för namn
                    for r in resp.result.entities.name.redactions:
                        print(f"Original: {r.original} -> Replacement: {r.replacement}")

            if __name__ == "__main__":
                run()

Regelkonfiguration (Options)

Meddelandet Options är valfritt. Om du utelämnar det används standardläget: namn, adress och personnummer maskeras.

Strukturen består av två delar:

  • entities – en lista med de EntityType som API:t ska leta efter och behandla.
  • rules – ett objekt där du kan specificera detaljerade regler för varje entitetstyp.

Övergripande struktur: Options

FältBeskrivning
entities
repeated EntityType
Vilka entiteter som ska avidentifieras. Värden: name, address, email, phoneNumber, personalNumber.
rules
Rules
Ett objekt som innehåller en default-regel och specifika regler för varje entitetstyp (t.ex. name, address). Specifika regler ärver från och skriver över default-regeln.

Gemensamma regelfält (gäller alla entiteter)

FältBeskrivning
method
Method
Hur värdet ska hanteras: mask,pseudonymize, replaceWithInitials (endast för name), replaceWith, remove.
replacementText
string
Krävs om method är replaceWith.
maskingCharacter
string
Tecken som används vid textmaskering när method är mask. Standard är "█".
fillColor / textColor
string
Hex-färger (#RRGGBB eller #RRGGBBAA) för visuell redigering i PDF/DOCX.
findValues / ignoreValues
repeated string
Listor med strängar för att tvinga fram eller ignorera specifika matchningar.

Gemensamma regelfält (gäller alla entiteter)

FältBeskrivning
method
Method
Hur värdet ska hanteras. Tillgängliga metoder:
  • mask – Ersätter tecken med maskeringstecken (t.ex. █).
  • remove – Tar bort värdet helt.
  • replaceWith – Ersätter med statisk text (kräver replacementText).
  • replaceWithInitials – (Endast namn) Ersätter med initialer (t.ex. "A.A").
  • pseudonymize – Ersätter varje unikt namn med en kort, slumpmässig och dokumentunik kod (pseudonym). Samma namn får samma kod inom dokumentet, vilket möjliggör spårbarhet utan att avslöja identitet.
replacementText
string
Krävs om method är replaceWith.
maskingCharacter
string
Tecken som används vid textmaskering när method är mask. Standard är "█".
fillColor / textColor
string
Hex-färger (#RRGGBB eller #RRGGBBAA) för visuell redigering i PDF/DOCX.
findValues / ignoreValues
repeated string
Listor med strängar för att tvinga fram eller ignorera specifika matchningar.

Idempotens

För att göra säkra retries utan att orsaka dubbeldebitering kan du skicka med metadata-headern idempotency-key: <unik-nyckel>. Detta är valfritt men rekommenderat för robusta integrationer.

Om ett anrop med en specifik nyckel lyckas, kommer efterföljande anrop med samma nyckel och identisk request-payload inom 15 minuter att returnera det cachade svaret istället för att utföra operationen på nytt.

Svaret innehåller två trailers (metadata som skickas efter responsen) för att bekräfta status:

  • idempotency-key: Ekar nyckeln som användes. Om du inte skickade en, innehåller den en server-genererad nyckel.
  • idempotency-status: Antingen created (första lyckade anropet) eller replayed (ett cachat svar returnerades).

Felhantering

gRPC-tjänsten använder standardiserade gRPC-statuskoder för att signalera resultat. Alla fel som uppstår i kommunikationen eller på servern signaleras som en RpcException (eller motsvarande i ditt språk).

Vid valideringsfel (INVALID_ARGUMENT) skickas en mer detaljerad felbeskrivning enligt Google RPC Error Model. Denna information finns i en binär trailer med nyckeln grpc-status-details-bin.

Standard gRPC-statuskoder

StatuskodBetydelse
OKAnropet lyckades.
INVALID_ARGUMENTOgiltig indata, t.ex. saknat fält eller felaktiga värden i Options. Detaljer finns i grpc-status-details-bin.
UNAUTHENTICATEDAPI-nyckel saknas eller är ogiltig.
FAILED_PRECONDITIONEn förutsättning är inte uppfylld, t.ex. filtypen som skickades stöds inte.
RESOURCE_EXHAUSTEDDin dagliga sidkvot har överskridits.
ALREADY_EXISTSIdempotenskonflikt. Samma idempotency-key har använts med en annan request-payload.
INTERNALEtt oväntat serverfel inträffade.
UNAVAILABLETjänsten är tillfälligt otillgänglig, t.ex. på grund av överbelastning eller underhåll. En retry kan fungera.
DEADLINE_EXCEEDEDAnropet tog för lång tid och avbröts.

Hantering av detaljerade valideringsfel (INVALID_ARGUMENT)

När ett anrop returnerar INVALID_ARGUMENT innehåller grpc-status-details-bin en serialiserad Google.Rpc.Status-meddelande. Detta meddelande kan i sin tur innehålla en lista av Any-objekt i fältet details, där varje Any kan packa upp till exempelvis ett Google.Rpc.BadRequest-meddelande med specifika FieldViolation-objekt.

Varje FieldViolation innehåller:

  • field: Sökvägen till det fält som orsakade felet (t.ex. options.rules.personalNumber.replacementText).
  • description: En läsbar beskrivning av valideringsfelet.

För att kunna parsa dessa detaljerade fel behöver du inkludera proto-definitionerna för google/rpc/status.proto och google/rpc/bad_request.proto i din klient-setup. I C# hanteras detta automatiskt via Google.Api.CommonProtos paketet.

using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Redaction.V1;
using Google.Rpc; // För Status och BadRequest
using Google.Protobuf; // För ByteString
using System.Net;

const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };

var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
var client = new RedactionService.RedactionServiceClient(channel);

// Skapa en request som medvetet orsakar ett valideringsfel
// replacementText kräver att method är ReplaceWith
var invalidRequest = new RedactTextRequest
{
    Text = "Test text.",
    Options = new Options
    {
        Entities = { EntityType.Name },
        Rules = new Rules
        {
            Name = new NameRules { 
                Method = Method.Mask, // Fel: Mask är satt, men replacementText är också satt
                ReplacementText = "NAMN" 
            }
        }
    }
};

try
{
    var response = await client.RedactTextAsync(invalidRequest, metadata);
    Console.WriteLine("Anrop lyckades: " + response.Result.RedactedText);
}
catch (RpcException rpcEx)
{
    Console.WriteLine($"gRPC-fel: {rpcEx.StatusCode} - {rpcEx.Status.Detail}");

    if (rpcEx.StatusCode == Grpc.Core.StatusCode.Unauthenticated)
    {
        Console.WriteLine("Fel: Ogiltig eller saknad API-nyckel.");
    }
    else if (rpcEx.StatusCode == Grpc.Core.StatusCode.InvalidArgument)
    {
        // Försök läsa detaljerade valideringsfel
        var trailerEntry = rpcEx.Trailers.Get("grpc-status-details-bin");
        if (trailerEntry?.ValueBytes is { Length: > 0 })
        {
            try
            {
                var status = Google.Rpc.Status.Parser.ParseFrom(trailerEntry.ValueBytes);
                Console.WriteLine($"Detaljerat felmeddelande: {status.Message}");

                foreach (var anyDetail in status.Details)
                {
                    if (anyDetail.Is())
                    {
                        var badRequest = anyDetail.Unpack();
                        Console.WriteLine("Valideringsfel:");
                        foreach (var violation in badRequest.FieldViolations)
                        {
                            Console.WriteLine($"- Fält: {violation.Field}, Beskrivning: {violation.Description}");
                        }
                    }
                    // Här kan du lägga till hantering för andra typer av detaljer om de finns
                }
            }
            catch (Exception parseEx)
            {
                Console.WriteLine($"Kunde inte parsa grpc-status-details-bin: {parseEx.Message}");
            }
        }
    }
    else
    {
        Console.WriteLine("Ett annat gRPC-fel inträffade.");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Ett oväntat fel inträffade: {ex.Message}");
}
import grpc
import redaction_v1_pb2
import redaction_v1_pb2_grpc
from google.rpc import status_pb2, bad_request_pb2 # Importera status och bad_request

API_KEY = "Bearer live_xxx..."
ADDRESS = "api.avidentifiera.se:50051"

def run():
    creds = grpc.ssl_channel_credentials()
    with grpc.secure_channel(ADDRESS, creds) as channel:
        stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
        
        # Skapa en request som medvetet orsakar ett valideringsfel
        # replacementText kräver att method är ReplaceWith
        invalid_request = redaction_v1_pb2.RedactTextRequest(
            text="Test text.",
            options=redaction_v1_pb2.Options(
                entities=[redaction_v1_pb2.EntityType.name],
                rules=redaction_v1_pb2.Rules(
                    name=redaction_v1_pb2.NameRules(
                        method=redaction_v1_pb2.Method.mask, # Fel: Mask är satt, men replacementText är också satt
                        replacementText="NAMN"
                    )
                )
            )
        )
        
        md = [('authorization', API_KEY)]
        
        try:
            response = stub.RedactText(invalid_request, metadata=md)
            print(f"Anrop lyckades: {response.result.redactedText}")
        except grpc.RpcError as rpc_error:
            print(f"gRPC-fel: {rpc_error.code()} - {rpc_error.details()}")

            if rpc_error.code() == grpc.StatusCode.UNAUTHENTICATED:
                print("Fel: Ogiltig eller saknad API-nyckel.")
            elif rpc_error.code() == grpc.StatusCode.INVALID_ARGUMENT:
                # Försök läsa detaljerade valideringsfel från trailers
                for key, value in rpc_error.trailing_metadata():
                    if key == 'grpc-status-details-bin':
                        try:
                            # Parsa Google.Rpc.Status meddelandet
                            status = status_pb2.Status()
                            status.ParseFromString(value)
                            print(f"Detaljerat felmeddelande: {status.message}")

                            for detail in status.details:
                                if detail.Is(bad_request_pb2.BadRequest.DESCRIPTOR):
                                    bad_request = bad_request_pb2.BadRequest()
                                    detail.Unpack(bad_request)
                                    print("Valideringsfel:")
                                    for violation in bad_request.field_violations:
                                        print(f"- Fält: {violation.field}, Beskrivning: {violation.description}")
                                # Här kan du lägga till hantering för andra typer av detaljer om de finns
                        except Exception as parse_ex:
                            print(f"Kunde inte parsa grpc-status-details-bin: {parse_ex}")
            else:
                print("Ett annat gRPC-fel inträffade.")
        except Exception as ex:
            print(f"Ett oväntat fel inträffade: {ex}")

if __name__ == "__main__":
    run()
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
import { Status } from 'google-protobuf/google/rpc/status_pb.js'; // Importera Status
import { BadRequest } from 'google-protobuf/google/rpc/bad_request_pb.js'; // Importera BadRequest

// Ladda in dina egna proto-filer
const packageDefinition = protoLoader.loadSync(
    ['./Protos/redaction.v1.proto', 'google/rpc/status.proto', 'google/rpc/bad_request.proto'], // Inkludera RPC error protos
    { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }
);
const proto = grpc.loadPackageDefinition(packageDefinition).grpc.redaction.v1;
const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());

const API_KEY = 'Bearer live_xxx...';
const metadata = new grpc.Metadata();
metadata.set('authorization', API_KEY);

// Skapa en request som medvetet orsakar ett valideringsfel
// replacementText kräver att method är ReplaceWith
const invalidRequest = {
    text: 'Test text.',
    options: {
        entities: ['name'],
        rules: {
            name: {
                method: 'mask', // Fel: Mask är satt, men replacementText är också satt
                replacementText: 'NAMN'
            }
        }
    }
};

client.RedactText(invalidRequest, metadata, (err, resp) => {
    if (err) {
        console.error(`gRPC-fel: ${err.code} - ${err.details}`);

        if (err.code === grpc.status.UNAUTHENTICATED) {
            console.error("Fel: Ogiltig eller saknad API-nyckel.");
        } else if (err.code === grpc.status.INVALID_ARGUMENT) {
            // Försök läsa detaljerade valideringsfel från trailers
            const statusDetailsBin = err.metadata.get('grpc-status-details-bin');
            if (statusDetailsBin && statusDetailsBin.length > 0) {
                try {
                    // statusDetailsBin är en array av Buffers, vi tar den första
                    const status = Status.deserializeBinary(statusDetailsBin[0]);
                    console.error(`Detaljerat felmeddelande: ${status.getMessage()}`);

                    for (const anyDetail of status.getDetailsList()) {
                        // Kontrollera om detaljen är av typen BadRequest
                        if (anyDetail.getTypeName() === 'type.googleapis.com/google.rpc.BadRequest') {
                            const badRequest = anyDetail.unpack(BadRequest.deserializeBinary, BadRequest.prototype.getTypeName());
                            if (badRequest) {
                                console.error("Valideringsfel:");
                                for (const violation of badRequest.getFieldViolationsList()) {
                                    console.error(`- Fält: ${violation.getField()}, Beskrivning: ${violation.getDescription()}`);
                                }
                            }
                        }
                        // Här kan du lägga till hantering för andra typer av detaljer om de finns
                    }
                } catch (parseEx) {
                    console.error(`Kunde inte parsa grpc-status-details-bin: ${parseEx.message}`);
                }
            }
        } else {
            console.error("Ett annat gRPC-fel inträffade.");
        }
    } else {
        console.log("Anrop lyckades: " + resp.result.redactedText);
    }
});
Avidentifiera Avidentifiera
Automatisera borttagning av känsliga uppgifter. © 2025 Avidentifiera |
Avidentifiera Avidentifiera Cookies
Vi använder cookies för att säkerställa webbplatsens funktionalitet. Du kan när som helst justera inställningar för analys och marknadsföring (avstängt som standard). Läs vår Cookiepolicy.