Skip to main content
US Army Corps of EngineersInstitute for Water Resources, Risk Management Center
Draft

This web application architecture guidance is still in draft and is subject to change.

Appendix: Flask to ASP.NET Mapping

This appendix provides a quick reference for developers transitioning from Flask/Python to ASP.NET Core/C#. It maps familiar Flask concepts to their ASP.NET equivalents.


Concept Mapping

Flask / PythonASP.NET Core / C#Notes
Flask appWebApplicationEntry point and configuration
BlueprintControllerGroups related endpoints
Route decorator[HttpGet], [HttpPost], etc.Maps HTTP methods to handlers
request objectControllerBase methodsAccess via method parameters
jsonify()Ok(), BadRequest(), etc.Returns JSON responses
SQLAlchemyEntity Framework CoreDatabase ORM
Flask-LoginJWT Bearer AuthAuthentication system
Flask configappsettings.jsonConfiguration files
requirements.txt.csproj PackageReferencesDependency management
pytestxUnit / MSTestTesting framework

Route Definition

from flask import Blueprint, jsonify bp = Blueprint('analysis', __name__, url_prefix='/api/analysis') @bp.route('/<int:id>', methods=['GET']) def get_analysis(id): analysis = Analysis.query.get_or_404(id) return jsonify(analysis.to_dict()) @bp.route('/<int:id>/calculate', methods=['POST']) def calculate(id): analysis = Analysis.query.get_or_404(id) result = run_calculation(analysis) return jsonify(result)

Request Handling

from flask import request @bp.route('/search', methods=['GET']) def search(): # Query parameters page = request.args.get('page', 1, type=int) limit = request.args.get('limit', 10, type=int) # Headers auth_token = request.headers.get('Authorization') return jsonify({'page': page, 'limit': limit}) @bp.route('/create', methods=['POST']) def create(): # JSON body data = request.get_json() name = data.get('name') return jsonify({'name': name}), 201

Database Operations

from app import db from app.models import Analysis # Get by ID analysis = Analysis.query.get(id) analysis = Analysis.query.get_or_404(id) # Filter analyses = Analysis.query.filter_by(user_id=user_id).all() analyses = Analysis.query.filter(Analysis.name.like('%test%')).all() # Create analysis = Analysis(name='New', user_id=user_id) db.session.add(analysis) db.session.commit() # Update analysis.name = 'Updated' db.session.commit() # Delete db.session.delete(analysis) db.session.commit()

Model Definition

from app import db from sqlalchemy.dialects.postgresql import JSONB class Analysis(db.Model): __tablename__ = 'analyses' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) user_id = db.Column(db.String, db.ForeignKey('users.id'), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow) parameters = db.Column(JSONB) results = db.Column(JSONB) user = db.relationship('User', back_populates='analyses') def to_dict(self): return { 'id': self.id, 'name': self.name, # ... }

Configuration

# config.py import os class Config: SECRET_KEY = os.environ.get('SECRET_KEY') SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') DEBUG = os.environ.get('DEBUG', 'false').lower() == 'true' # app/__init__.py app = Flask(__name__) app.config.from_object(Config) # Accessing config db_url = app.config['SQLALCHEMY_DATABASE_URI']

Error Handling

from flask import jsonify from werkzeug.exceptions import HTTPException @app.errorhandler(Exception) def handle_exception(e): if isinstance(e, HTTPException): return jsonify({'error': e.description}), e.code # Log the error app.logger.error(f'Unhandled exception: {e}') return jsonify({'error': 'Internal server error'}), 500 @app.errorhandler(404) def not_found(e): return jsonify({'error': 'Not found'}), 404 @app.errorhandler(400) def bad_request(e): return jsonify({'error': str(e)}), 400

Logging

import logging logger = logging.getLogger(__name__) # Configuration logging.basicConfig( level=logging.DEBUG, format='%(asctime)s [%(levelname)s] %(name)s: %(message)s' ) # Usage logger.debug(f"Processing analysis {analysis_id}") logger.info(f"[API] POST /calculate/{analysis_id}") logger.warning(f"Parameter {name} outside typical range") logger.error(f"Calculation failed: {error}", exc_info=True)

Testing

import pytest from app import create_app, db @pytest.fixture def client(): app = create_app('testing') with app.test_client() as client: with app.app_context(): db.create_all() yield client def test_get_analysis(client): response = client.get('/api/analysis/1') assert response.status_code == 200 data = response.get_json() assert 'id' in data def test_calculate_missing_params(client): response = client.post('/api/analysis/1/calculate') assert response.status_code == 400

Common Gotchas for Flask Developers

1. Async/Await is Required

In Flask, everything runs synchronously by default. In ASP.NET Core, database operations and I/O should be async:

// WRONG: Blocking call
var analysis = _db.Analyses.Find(id);

// CORRECT: Async call
var analysis = await _db.Analyses.FindAsync(id);

2. No Global Request Object

Flask has a global request object. In ASP.NET, request data comes through method parameters:

// Flask-style thinking (WRONG)
var body = Request.Body; // Don't do this

// ASP.NET style (CORRECT)
public IActionResult Create([FromBody] CreateDto dto)
{
// dto is already parsed
}

3. Explicit Null Checks

Python's get_or_404 doesn't exist. Check nulls explicitly:

var analysis = await _db.Analyses.FindAsync(id);
if (analysis == null)
return NotFound();

4. SaveChanges is Explicit

Unlike some Flask patterns, EF Core requires explicit saves:

analysis.Name = "Updated";
await _db.SaveChangesAsync(); // Must call this!

5. Dependency Injection is Required

Flask uses module imports. ASP.NET uses constructor injection:

public class MyController : ControllerBase
{
private readonly IMyService _service; // Injected

public MyController(IMyService service)
{
_service = service;
}
}

6. No Flask-style Decorators

Flask decorators become attributes in C#:

# Flask
@bp.route('/items/<int:id>', methods=['GET'])
@login_required
def get_item(id):
pass
// ASP.NET
[HttpGet("items/{id}")]
[Authorize]
public async Task<IActionResult> GetItem(int id)
{
// ...
}

Quick Translation Reference

Flask PatternASP.NET Equivalent
return jsonify(data), 200return Ok(data)
return jsonify(data), 201return Created(url, data)
return jsonify({"error": msg}), 400return BadRequest(new { error = msg })
return jsonify({"error": msg}), 404return NotFound()
abort(403)return Forbid()
request.get_json()[FromBody] MyDto dto
request.args.get("key")[FromQuery] string key
g.user (current user)User.FindFirstValue(ClaimTypes.NameIdentifier)
@login_required[Authorize]
db.session.commit()await _db.SaveChangesAsync()